2801 lines · 80.3 KB · C
RawDownload
/* * Copyright © 1997-2003 by The XFree86 Project, Inc. * Copyright © 2007 Dave Airlie * Copyright © 2007-2008 Intel Corporation *   Jesse Barnes <jesse.barnes@intel.com> * Copyright 2005-2006 Luc Verhaegen * Copyright (c) 2001, Andy Ritger  aritger@nvidia.com * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of the copyright holder(s) * and author(s) shall not be used in advertising or otherwise to promote * the sale, use or other dealings in this Software without prior written * authorization from the copyright holder(s) and author(s). */ #include <linux/ctype.h>#include <linux/export.h>#include <linux/fb.h> /* for KHZ2PICOS() */#include <linux/list.h>#include <linux/list_sort.h>#include <linux/of.h> #include <video/of_display_timing.h>#include <video/of_videomode.h>#include <video/videomode.h> #include <drm/drm_crtc.h>#include <drm/drm_device.h>#include <drm/drm_edid.h>#include <drm/drm_modes.h>#include <drm/drm_print.h> #include "drm_crtc_internal.h" /** * drm_mode_debug_printmodeline - print a mode to dmesg * @mode: mode to print * * Describe @mode using DRM_DEBUG. */void drm_mode_debug_printmodeline(const struct drm_display_mode *mode){	DRM_DEBUG_KMS("Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));}EXPORT_SYMBOL(drm_mode_debug_printmodeline); /** * drm_mode_create - create a new display mode * @dev: DRM device * * Create a new, cleared drm_display_mode with kzalloc, allocate an ID for it * and return it. * * Returns: * Pointer to new mode on success, NULL on error. */struct drm_display_mode *drm_mode_create(struct drm_device *dev){	struct drm_display_mode *nmode; 	nmode = kzalloc_obj(struct drm_display_mode);	if (!nmode)		return NULL; 	return nmode;}EXPORT_SYMBOL(drm_mode_create); /** * drm_mode_destroy - remove a mode * @dev: DRM device * @mode: mode to remove * * Release @mode's unique ID, then free it @mode structure itself using kfree. */void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode){	if (!mode)		return; 	kfree(mode);}EXPORT_SYMBOL(drm_mode_destroy); /** * drm_mode_probed_add - add a mode to a connector's probed_mode list * @connector: connector the new mode * @mode: mode data * * Add @mode to @connector's probed_mode list for later use. This list should * then in a second step get filtered and all the modes actually supported by * the hardware moved to the @connector's modes list. */void drm_mode_probed_add(struct drm_connector *connector,			 struct drm_display_mode *mode){	WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); 	list_add_tail(&mode->head, &connector->probed_modes);}EXPORT_SYMBOL(drm_mode_probed_add); enum drm_mode_analog {	DRM_MODE_ANALOG_NTSC, /* 525 lines, 60Hz */	DRM_MODE_ANALOG_PAL, /* 625 lines, 50Hz */}; /* * The timings come from: * - https://web.archive.org/web/20220406232708/http://www.kolumbus.fi/pami1/video/pal_ntsc.html * - https://web.archive.org/web/20220406124914/http://martin.hinner.info/vga/pal.html * - https://web.archive.org/web/20220609202433/http://www.batsocks.co.uk/readme/video_timing.htm */#define NTSC_LINE_DURATION_NS		63556U#define NTSC_LINES_NUMBER		525 #define NTSC_HBLK_DURATION_TYP_NS	10900U#define NTSC_HBLK_DURATION_MIN_NS	(NTSC_HBLK_DURATION_TYP_NS - 200)#define NTSC_HBLK_DURATION_MAX_NS	(NTSC_HBLK_DURATION_TYP_NS + 200) #define NTSC_HACT_DURATION_TYP_NS	(NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_TYP_NS)#define NTSC_HACT_DURATION_MIN_NS	(NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MAX_NS)#define NTSC_HACT_DURATION_MAX_NS	(NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MIN_NS) #define NTSC_HFP_DURATION_TYP_NS	1500#define NTSC_HFP_DURATION_MIN_NS	1270#define NTSC_HFP_DURATION_MAX_NS	2220 #define NTSC_HSLEN_DURATION_TYP_NS	4700#define NTSC_HSLEN_DURATION_MIN_NS	(NTSC_HSLEN_DURATION_TYP_NS - 100)#define NTSC_HSLEN_DURATION_MAX_NS	(NTSC_HSLEN_DURATION_TYP_NS + 100) #define NTSC_HBP_DURATION_TYP_NS	4700 /* * I couldn't find the actual tolerance for the back porch, so let's * just reuse the sync length ones. */#define NTSC_HBP_DURATION_MIN_NS	(NTSC_HBP_DURATION_TYP_NS - 100)#define NTSC_HBP_DURATION_MAX_NS	(NTSC_HBP_DURATION_TYP_NS + 100) #define PAL_LINE_DURATION_NS		64000U#define PAL_LINES_NUMBER		625 #define PAL_HACT_DURATION_TYP_NS	51950U#define PAL_HACT_DURATION_MIN_NS	(PAL_HACT_DURATION_TYP_NS - 100)#define PAL_HACT_DURATION_MAX_NS	(PAL_HACT_DURATION_TYP_NS + 400) #define PAL_HBLK_DURATION_TYP_NS	(PAL_LINE_DURATION_NS - PAL_HACT_DURATION_TYP_NS)#define PAL_HBLK_DURATION_MIN_NS	(PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MAX_NS)#define PAL_HBLK_DURATION_MAX_NS	(PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MIN_NS) #define PAL_HFP_DURATION_TYP_NS		1650#define PAL_HFP_DURATION_MIN_NS		(PAL_HFP_DURATION_TYP_NS - 100)#define PAL_HFP_DURATION_MAX_NS		(PAL_HFP_DURATION_TYP_NS + 400) #define PAL_HSLEN_DURATION_TYP_NS	4700#define PAL_HSLEN_DURATION_MIN_NS	(PAL_HSLEN_DURATION_TYP_NS - 200)#define PAL_HSLEN_DURATION_MAX_NS	(PAL_HSLEN_DURATION_TYP_NS + 200) #define PAL_HBP_DURATION_TYP_NS		5700#define PAL_HBP_DURATION_MIN_NS		(PAL_HBP_DURATION_TYP_NS - 200)#define PAL_HBP_DURATION_MAX_NS		(PAL_HBP_DURATION_TYP_NS + 200) struct analog_param_field {	unsigned int even, odd;}; #define PARAM_FIELD(_odd, _even)		\	{ .even = _even, .odd = _odd } struct analog_param_range {	unsigned int	min, typ, max;}; #define PARAM_RANGE(_min, _typ, _max)		\	{ .min = _min, .typ = _typ, .max = _max } struct analog_parameters {	unsigned int			num_lines;	unsigned int			line_duration_ns; 	struct analog_param_range	hact_ns;	struct analog_param_range	hfp_ns;	struct analog_param_range	hslen_ns;	struct analog_param_range	hbp_ns;	struct analog_param_range	hblk_ns; 	unsigned int			bt601_hfp; 	struct analog_param_field	vfp_lines;	struct analog_param_field	vslen_lines;	struct analog_param_field	vbp_lines;}; #define TV_MODE_PARAMETER(_mode, _lines, _line_dur, _hact, _hfp,	\			  _hslen, _hbp, _hblk, _bt601_hfp, _vfp,	\			  _vslen, _vbp)					\	[_mode] = {							\		.num_lines = _lines,					\		.line_duration_ns = _line_dur,				\		.hact_ns = _hact,					\		.hfp_ns = _hfp,						\		.hslen_ns = _hslen,					\		.hbp_ns = _hbp,						\		.hblk_ns = _hblk,					\		.bt601_hfp = _bt601_hfp,				\		.vfp_lines = _vfp,					\		.vslen_lines = _vslen,					\		.vbp_lines = _vbp,					\	} static const struct analog_parameters tv_modes_parameters[] = {	TV_MODE_PARAMETER(DRM_MODE_ANALOG_NTSC,			  NTSC_LINES_NUMBER,			  NTSC_LINE_DURATION_NS,			  PARAM_RANGE(NTSC_HACT_DURATION_MIN_NS,				      NTSC_HACT_DURATION_TYP_NS,				      NTSC_HACT_DURATION_MAX_NS),			  PARAM_RANGE(NTSC_HFP_DURATION_MIN_NS,				      NTSC_HFP_DURATION_TYP_NS,				      NTSC_HFP_DURATION_MAX_NS),			  PARAM_RANGE(NTSC_HSLEN_DURATION_MIN_NS,				      NTSC_HSLEN_DURATION_TYP_NS,				      NTSC_HSLEN_DURATION_MAX_NS),			  PARAM_RANGE(NTSC_HBP_DURATION_MIN_NS,				      NTSC_HBP_DURATION_TYP_NS,				      NTSC_HBP_DURATION_MAX_NS),			  PARAM_RANGE(NTSC_HBLK_DURATION_MIN_NS,				      NTSC_HBLK_DURATION_TYP_NS,				      NTSC_HBLK_DURATION_MAX_NS),			  16,			  PARAM_FIELD(3, 3),			  PARAM_FIELD(3, 3),			  PARAM_FIELD(16, 17)),	TV_MODE_PARAMETER(DRM_MODE_ANALOG_PAL,			  PAL_LINES_NUMBER,			  PAL_LINE_DURATION_NS,			  PARAM_RANGE(PAL_HACT_DURATION_MIN_NS,				      PAL_HACT_DURATION_TYP_NS,				      PAL_HACT_DURATION_MAX_NS),			  PARAM_RANGE(PAL_HFP_DURATION_MIN_NS,				      PAL_HFP_DURATION_TYP_NS,				      PAL_HFP_DURATION_MAX_NS),			  PARAM_RANGE(PAL_HSLEN_DURATION_MIN_NS,				      PAL_HSLEN_DURATION_TYP_NS,				      PAL_HSLEN_DURATION_MAX_NS),			  PARAM_RANGE(PAL_HBP_DURATION_MIN_NS,				      PAL_HBP_DURATION_TYP_NS,				      PAL_HBP_DURATION_MAX_NS),			  PARAM_RANGE(PAL_HBLK_DURATION_MIN_NS,				      PAL_HBLK_DURATION_TYP_NS,				      PAL_HBLK_DURATION_MAX_NS),			  12, 			  /*			   * The front porch is actually 6 short sync			   * pulses for the even field, and 5 for the			   * odd field. Each sync takes half a life so			   * the odd field front porch is shorter by			   * half a line.			   *			   * In progressive, we're supposed to use 6			   * pulses, so we're fine there			   */			  PARAM_FIELD(3, 2), 			  /*			   * The vsync length is 5 long sync pulses,			   * each field taking half a line. We're			   * shorter for both fields by half a line.			   *			   * In progressive, we're supposed to use 5			   * pulses, so we're off by half			   * a line.			   *			   * In interlace, we're now off by half a line			   * for the even field and one line for the odd			   * field.			   */			  PARAM_FIELD(3, 3), 			  /*			   * The back porch starts with post-equalizing			   * pulses, consisting in 5 short sync pulses			   * for the even field, 4 for the odd field. In			   * progressive, it's 5 short syncs.			   *			   * In progressive, we thus have 2.5 lines,			   * plus the 0.5 line we were missing			   * previously, so we should use 3 lines.			   *			   * In interlace, the even field is in the			   * exact same case than progressive. For the			   * odd field, we should be using 2 lines but			   * we're one line short, so we'll make up for			   * it here by using 3.			   *			   * The entire blanking area is supposed to			   * take 25 lines, so we also need to account			   * for the rest of the blanking area that			   * can't be in either the front porch or sync			   * period.			   */			  PARAM_FIELD(19, 20)),}; static int fill_analog_mode(struct drm_device *dev,			    struct drm_display_mode *mode,			    const struct analog_parameters *params,			    unsigned long pixel_clock_hz,			    unsigned int hactive,			    unsigned int vactive,			    bool interlace){	unsigned long pixel_duration_ns = NSEC_PER_SEC / pixel_clock_hz;	unsigned int htotal, vtotal;	unsigned int max_hact, hact_duration_ns;	unsigned int hblk, hblk_duration_ns;	unsigned int hfp, hfp_duration_ns;	unsigned int hslen, hslen_duration_ns;	unsigned int hbp, hbp_duration_ns;	unsigned int porches, porches_duration_ns;	unsigned int vfp, vfp_min;	unsigned int vbp, vbp_min;	unsigned int vslen;	bool bt601 = false;	int porches_rem;	u64 result; 	drm_dbg_kms(dev,		    "Generating a %ux%u%c, %u-line mode with a %lu kHz clock\n",		    hactive, vactive,		    interlace ? 'i' : 'p',		    params->num_lines,		    pixel_clock_hz / 1000); 	max_hact = params->hact_ns.max / pixel_duration_ns;	if (pixel_clock_hz == 13500000 && hactive > max_hact && hactive <= 720) {		drm_dbg_kms(dev, "Trying to generate a BT.601 mode. Disabling checks.\n");		bt601 = true;	} 	/*	 * Our pixel duration is going to be round down by the division,	 * so rounding up is probably going to introduce even more	 * deviation.	 */	result = (u64)params->line_duration_ns * pixel_clock_hz;	do_div(result, NSEC_PER_SEC);	htotal = result; 	drm_dbg_kms(dev, "Total Horizontal Number of Pixels: %u\n", htotal); 	hact_duration_ns = hactive * pixel_duration_ns;	if (!bt601 &&	    (hact_duration_ns < params->hact_ns.min ||	     hact_duration_ns > params->hact_ns.max)) {		drm_err(dev, "Invalid horizontal active area duration: %uns (min: %u, max %u)\n",			hact_duration_ns, params->hact_ns.min, params->hact_ns.max);		return -EINVAL;	} 	hblk = htotal - hactive;	drm_dbg_kms(dev, "Horizontal Blanking Period: %u\n", hblk); 	hblk_duration_ns = hblk * pixel_duration_ns;	if (!bt601 &&	    (hblk_duration_ns < params->hblk_ns.min ||	     hblk_duration_ns > params->hblk_ns.max)) {		drm_err(dev, "Invalid horizontal blanking duration: %uns (min: %u, max %u)\n",			hblk_duration_ns, params->hblk_ns.min, params->hblk_ns.max);		return -EINVAL;	} 	hslen = DIV_ROUND_UP(params->hslen_ns.typ, pixel_duration_ns);	drm_dbg_kms(dev, "Horizontal Sync Period: %u\n", hslen); 	hslen_duration_ns = hslen * pixel_duration_ns;	if (!bt601 &&	    (hslen_duration_ns < params->hslen_ns.min ||	     hslen_duration_ns > params->hslen_ns.max)) {		drm_err(dev, "Invalid horizontal sync duration: %uns (min: %u, max %u)\n",			hslen_duration_ns, params->hslen_ns.min, params->hslen_ns.max);		return -EINVAL;	} 	porches = hblk - hslen;	drm_dbg_kms(dev, "Remaining horizontal pixels for both porches: %u\n", porches); 	porches_duration_ns = porches * pixel_duration_ns;	if (!bt601 &&	    (porches_duration_ns > (params->hfp_ns.max + params->hbp_ns.max) ||	     porches_duration_ns < (params->hfp_ns.min + params->hbp_ns.min))) {		drm_err(dev, "Invalid horizontal porches duration: %uns\n",			porches_duration_ns);		return -EINVAL;	} 	if (bt601) {		hfp = params->bt601_hfp;	} else {		unsigned int hfp_min = DIV_ROUND_UP(params->hfp_ns.min,						    pixel_duration_ns);		unsigned int hbp_min = DIV_ROUND_UP(params->hbp_ns.min,						    pixel_duration_ns);		int porches_rem = porches - hfp_min - hbp_min; 		hfp = hfp_min + DIV_ROUND_UP(porches_rem, 2);	} 	drm_dbg_kms(dev, "Horizontal Front Porch: %u\n", hfp); 	hfp_duration_ns = hfp * pixel_duration_ns;	if (!bt601 &&	    (hfp_duration_ns < params->hfp_ns.min ||	     hfp_duration_ns > params->hfp_ns.max)) {		drm_err(dev, "Invalid horizontal front porch duration: %uns (min: %u, max %u)\n",			hfp_duration_ns, params->hfp_ns.min, params->hfp_ns.max);		return -EINVAL;	} 	hbp = porches - hfp;	drm_dbg_kms(dev, "Horizontal Back Porch: %u\n", hbp); 	hbp_duration_ns = hbp * pixel_duration_ns;	if (!bt601 &&	    (hbp_duration_ns < params->hbp_ns.min ||	     hbp_duration_ns > params->hbp_ns.max)) {		drm_err(dev, "Invalid horizontal back porch duration: %uns (min: %u, max %u)\n",			hbp_duration_ns, params->hbp_ns.min, params->hbp_ns.max);		return -EINVAL;	} 	if (htotal != (hactive + hfp + hslen + hbp))		return -EINVAL; 	mode->clock = pixel_clock_hz / 1000;	mode->hdisplay = hactive;	mode->hsync_start = mode->hdisplay + hfp;	mode->hsync_end = mode->hsync_start + hslen;	mode->htotal = mode->hsync_end + hbp; 	if (interlace) {		vfp_min = params->vfp_lines.even + params->vfp_lines.odd;		vbp_min = params->vbp_lines.even + params->vbp_lines.odd;		vslen = params->vslen_lines.even + params->vslen_lines.odd;	} else {		/*		 * By convention, NTSC (aka 525/60) systems start with		 * the even field, but PAL (aka 625/50) systems start		 * with the odd one.		 *		 * PAL systems also have asymmetric timings between the		 * even and odd field, while NTSC is symmetric.		 *		 * Moreover, if we want to create a progressive mode for		 * PAL, we need to use the odd field timings.		 *		 * Since odd == even for NTSC, we can just use the odd		 * one all the time to simplify the code a bit.		 */		vfp_min = params->vfp_lines.odd;		vbp_min = params->vbp_lines.odd;		vslen = params->vslen_lines.odd;	} 	drm_dbg_kms(dev, "Vertical Sync Period: %u\n", vslen); 	porches = params->num_lines - vactive - vslen;	drm_dbg_kms(dev, "Remaining vertical pixels for both porches: %u\n", porches); 	porches_rem = porches - vfp_min - vbp_min;	vfp = vfp_min + (porches_rem / 2);	drm_dbg_kms(dev, "Vertical Front Porch: %u\n", vfp); 	vbp = porches - vfp;	drm_dbg_kms(dev, "Vertical Back Porch: %u\n", vbp); 	vtotal = vactive + vfp + vslen + vbp;	if (params->num_lines != vtotal) {		drm_err(dev, "Invalid vertical total: %upx (expected %upx)\n",			vtotal, params->num_lines);

Lines 1–500 of 2801.