| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 1 | /* | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 2 |  * Copyright © 1997-2003 by The XFree86 Project, Inc. | 
 | 3 |  * Copyright © 2007 Dave Airlie | 
 | 4 |  * Copyright © 2007-2008 Intel Corporation | 
 | 5 |  *   Jesse Barnes <jesse.barnes@intel.com> | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 6 |  * Copyright 2005-2006 Luc Verhaegen | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 7 |  * Copyright (c) 2001, Andy Ritger  aritger@nvidia.com | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 8 |  * | 
 | 9 |  * Permission is hereby granted, free of charge, to any person obtaining a | 
 | 10 |  * copy of this software and associated documentation files (the "Software"), | 
 | 11 |  * to deal in the Software without restriction, including without limitation | 
 | 12 |  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
 | 13 |  * and/or sell copies of the Software, and to permit persons to whom the | 
 | 14 |  * Software is furnished to do so, subject to the following conditions: | 
 | 15 |  * | 
 | 16 |  * The above copyright notice and this permission notice shall be included in | 
 | 17 |  * all copies or substantial portions of the Software. | 
 | 18 |  * | 
 | 19 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 | 20 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 | 21 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | 
 | 22 |  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | 
 | 23 |  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | 
 | 24 |  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | 
 | 25 |  * OTHER DEALINGS IN THE SOFTWARE. | 
 | 26 |  * | 
 | 27 |  * Except as contained in this notice, the name of the copyright holder(s) | 
 | 28 |  * and author(s) shall not be used in advertising or otherwise to promote | 
 | 29 |  * the sale, use or other dealings in this Software without prior written | 
 | 30 |  * authorization from the copyright holder(s) and author(s). | 
 | 31 |  */ | 
 | 32 |  | 
 | 33 | #include <linux/list.h> | 
| Dave Chinner | 2c76127 | 2010-01-12 17:39:16 +1100 | [diff] [blame] | 34 | #include <linux/list_sort.h> | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 35 | #include "drmP.h" | 
 | 36 | #include "drm.h" | 
 | 37 | #include "drm_crtc.h" | 
 | 38 |  | 
 | 39 | /** | 
 | 40 |  * drm_mode_debug_printmodeline - debug print a mode | 
 | 41 |  * @dev: DRM device | 
 | 42 |  * @mode: mode to print | 
 | 43 |  * | 
 | 44 |  * LOCKING: | 
 | 45 |  * None. | 
 | 46 |  * | 
 | 47 |  * Describe @mode using DRM_DEBUG. | 
 | 48 |  */ | 
 | 49 | void drm_mode_debug_printmodeline(struct drm_display_mode *mode) | 
 | 50 | { | 
| Zhao Yakui | f940f37 | 2009-07-20 13:48:05 +0800 | [diff] [blame] | 51 | 	DRM_DEBUG_KMS("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d " | 
| Zhao Yakui | 8a4c47f | 2009-07-20 13:48:04 +0800 | [diff] [blame] | 52 | 			"0x%x 0x%x\n", | 
| yakui_zhao | f053185 | 2009-06-02 14:12:47 +0800 | [diff] [blame] | 53 | 		mode->base.id, mode->name, mode->vrefresh, mode->clock, | 
 | 54 | 		mode->hdisplay, mode->hsync_start, | 
 | 55 | 		mode->hsync_end, mode->htotal, | 
 | 56 | 		mode->vdisplay, mode->vsync_start, | 
 | 57 | 		mode->vsync_end, mode->vtotal, mode->type, mode->flags); | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 58 | } | 
 | 59 | EXPORT_SYMBOL(drm_mode_debug_printmodeline); | 
 | 60 |  | 
 | 61 | /** | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 62 |  * drm_cvt_mode -create a modeline based on CVT algorithm | 
 | 63 |  * @dev: DRM device | 
 | 64 |  * @hdisplay: hdisplay size | 
 | 65 |  * @vdisplay: vdisplay size | 
 | 66 |  * @vrefresh  : vrefresh rate | 
 | 67 |  * @reduced : Whether the GTF calculation is simplified | 
 | 68 |  * @interlaced:Whether the interlace is supported | 
 | 69 |  * | 
 | 70 |  * LOCKING: | 
 | 71 |  * none. | 
 | 72 |  * | 
 | 73 |  * return the modeline based on CVT algorithm | 
 | 74 |  * | 
 | 75 |  * This function is called to generate the modeline based on CVT algorithm | 
 | 76 |  * according to the hdisplay, vdisplay, vrefresh. | 
 | 77 |  * It is based from the VESA(TM) Coordinated Video Timing Generator by | 
 | 78 |  * Graham Loveridge April 9, 2003 available at | 
| Justin P. Mattock | 631dd1a | 2010-10-18 11:03:14 +0200 | [diff] [blame] | 79 |  * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls  | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 80 |  * | 
 | 81 |  * And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c. | 
 | 82 |  * What I have done is to translate it by using integer calculation. | 
 | 83 |  */ | 
 | 84 | #define HV_FACTOR			1000 | 
 | 85 | struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, | 
 | 86 | 				      int vdisplay, int vrefresh, | 
| Dave Airlie | d50ba25 | 2009-09-23 14:44:08 +1000 | [diff] [blame] | 87 | 				      bool reduced, bool interlaced, bool margins) | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 88 | { | 
 | 89 | 	/* 1) top/bottom margin size (% of height) - default: 1.8, */ | 
 | 90 | #define	CVT_MARGIN_PERCENTAGE		18 | 
 | 91 | 	/* 2) character cell horizontal granularity (pixels) - default 8 */ | 
 | 92 | #define	CVT_H_GRANULARITY		8 | 
 | 93 | 	/* 3) Minimum vertical porch (lines) - default 3 */ | 
 | 94 | #define	CVT_MIN_V_PORCH			3 | 
 | 95 | 	/* 4) Minimum number of vertical back porch lines - default 6 */ | 
 | 96 | #define	CVT_MIN_V_BPORCH		6 | 
 | 97 | 	/* Pixel Clock step (kHz) */ | 
 | 98 | #define CVT_CLOCK_STEP			250 | 
 | 99 | 	struct drm_display_mode *drm_mode; | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 100 | 	unsigned int vfieldrate, hperiod; | 
 | 101 | 	int hdisplay_rnd, hmargin, vdisplay_rnd, vmargin, vsync; | 
 | 102 | 	int interlace; | 
 | 103 |  | 
 | 104 | 	/* allocate the drm_display_mode structure. If failure, we will | 
 | 105 | 	 * return directly | 
 | 106 | 	 */ | 
 | 107 | 	drm_mode = drm_mode_create(dev); | 
 | 108 | 	if (!drm_mode) | 
 | 109 | 		return NULL; | 
 | 110 |  | 
 | 111 | 	/* the CVT default refresh rate is 60Hz */ | 
 | 112 | 	if (!vrefresh) | 
 | 113 | 		vrefresh = 60; | 
 | 114 |  | 
 | 115 | 	/* the required field fresh rate */ | 
 | 116 | 	if (interlaced) | 
 | 117 | 		vfieldrate = vrefresh * 2; | 
 | 118 | 	else | 
 | 119 | 		vfieldrate = vrefresh; | 
 | 120 |  | 
 | 121 | 	/* horizontal pixels */ | 
 | 122 | 	hdisplay_rnd = hdisplay - (hdisplay % CVT_H_GRANULARITY); | 
 | 123 |  | 
 | 124 | 	/* determine the left&right borders */ | 
 | 125 | 	hmargin = 0; | 
 | 126 | 	if (margins) { | 
 | 127 | 		hmargin = hdisplay_rnd * CVT_MARGIN_PERCENTAGE / 1000; | 
 | 128 | 		hmargin -= hmargin % CVT_H_GRANULARITY; | 
 | 129 | 	} | 
 | 130 | 	/* find the total active pixels */ | 
 | 131 | 	drm_mode->hdisplay = hdisplay_rnd + 2 * hmargin; | 
 | 132 |  | 
 | 133 | 	/* find the number of lines per field */ | 
 | 134 | 	if (interlaced) | 
 | 135 | 		vdisplay_rnd = vdisplay / 2; | 
 | 136 | 	else | 
 | 137 | 		vdisplay_rnd = vdisplay; | 
 | 138 |  | 
 | 139 | 	/* find the top & bottom borders */ | 
 | 140 | 	vmargin = 0; | 
 | 141 | 	if (margins) | 
 | 142 | 		vmargin = vdisplay_rnd * CVT_MARGIN_PERCENTAGE / 1000; | 
 | 143 |  | 
| Francisco Jerez | 841b411 | 2009-08-12 02:30:09 +0200 | [diff] [blame] | 144 | 	drm_mode->vdisplay = vdisplay + 2 * vmargin; | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 145 |  | 
 | 146 | 	/* Interlaced */ | 
 | 147 | 	if (interlaced) | 
 | 148 | 		interlace = 1; | 
 | 149 | 	else | 
 | 150 | 		interlace = 0; | 
 | 151 |  | 
 | 152 | 	/* Determine VSync Width from aspect ratio */ | 
 | 153 | 	if (!(vdisplay % 3) && ((vdisplay * 4 / 3) == hdisplay)) | 
 | 154 | 		vsync = 4; | 
 | 155 | 	else if (!(vdisplay % 9) && ((vdisplay * 16 / 9) == hdisplay)) | 
 | 156 | 		vsync = 5; | 
 | 157 | 	else if (!(vdisplay % 10) && ((vdisplay * 16 / 10) == hdisplay)) | 
 | 158 | 		vsync = 6; | 
 | 159 | 	else if (!(vdisplay % 4) && ((vdisplay * 5 / 4) == hdisplay)) | 
 | 160 | 		vsync = 7; | 
 | 161 | 	else if (!(vdisplay % 9) && ((vdisplay * 15 / 9) == hdisplay)) | 
 | 162 | 		vsync = 7; | 
 | 163 | 	else /* custom */ | 
 | 164 | 		vsync = 10; | 
 | 165 |  | 
 | 166 | 	if (!reduced) { | 
 | 167 | 		/* simplify the GTF calculation */ | 
 | 168 | 		/* 4) Minimum time of vertical sync + back porch interval (µs) | 
 | 169 | 		 * default 550.0 | 
 | 170 | 		 */ | 
 | 171 | 		int tmp1, tmp2; | 
 | 172 | #define CVT_MIN_VSYNC_BP	550 | 
 | 173 | 		/* 3) Nominal HSync width (% of line period) - default 8 */ | 
 | 174 | #define CVT_HSYNC_PERCENTAGE	8 | 
 | 175 | 		unsigned int hblank_percentage; | 
 | 176 | 		int vsyncandback_porch, vback_porch, hblank; | 
 | 177 |  | 
 | 178 | 		/* estimated the horizontal period */ | 
 | 179 | 		tmp1 = HV_FACTOR * 1000000  - | 
 | 180 | 				CVT_MIN_VSYNC_BP * HV_FACTOR * vfieldrate; | 
 | 181 | 		tmp2 = (vdisplay_rnd + 2 * vmargin + CVT_MIN_V_PORCH) * 2 + | 
 | 182 | 				interlace; | 
 | 183 | 		hperiod = tmp1 * 2 / (tmp2 * vfieldrate); | 
 | 184 |  | 
 | 185 | 		tmp1 = CVT_MIN_VSYNC_BP * HV_FACTOR / hperiod + 1; | 
 | 186 | 		/* 9. Find number of lines in sync + backporch */ | 
 | 187 | 		if (tmp1 < (vsync + CVT_MIN_V_PORCH)) | 
 | 188 | 			vsyncandback_porch = vsync + CVT_MIN_V_PORCH; | 
 | 189 | 		else | 
 | 190 | 			vsyncandback_porch = tmp1; | 
 | 191 | 		/* 10. Find number of lines in back porch */ | 
 | 192 | 		vback_porch = vsyncandback_porch - vsync; | 
 | 193 | 		drm_mode->vtotal = vdisplay_rnd + 2 * vmargin + | 
 | 194 | 				vsyncandback_porch + CVT_MIN_V_PORCH; | 
 | 195 | 		/* 5) Definition of Horizontal blanking time limitation */ | 
 | 196 | 		/* Gradient (%/kHz) - default 600 */ | 
 | 197 | #define CVT_M_FACTOR	600 | 
 | 198 | 		/* Offset (%) - default 40 */ | 
 | 199 | #define CVT_C_FACTOR	40 | 
 | 200 | 		/* Blanking time scaling factor - default 128 */ | 
 | 201 | #define CVT_K_FACTOR	128 | 
 | 202 | 		/* Scaling factor weighting - default 20 */ | 
 | 203 | #define CVT_J_FACTOR	20 | 
 | 204 | #define CVT_M_PRIME	(CVT_M_FACTOR * CVT_K_FACTOR / 256) | 
 | 205 | #define CVT_C_PRIME	((CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \ | 
 | 206 | 			 CVT_J_FACTOR) | 
 | 207 | 		/* 12. Find ideal blanking duty cycle from formula */ | 
 | 208 | 		hblank_percentage = CVT_C_PRIME * HV_FACTOR - CVT_M_PRIME * | 
 | 209 | 					hperiod / 1000; | 
 | 210 | 		/* 13. Blanking time */ | 
 | 211 | 		if (hblank_percentage < 20 * HV_FACTOR) | 
 | 212 | 			hblank_percentage = 20 * HV_FACTOR; | 
 | 213 | 		hblank = drm_mode->hdisplay * hblank_percentage / | 
 | 214 | 			 (100 * HV_FACTOR - hblank_percentage); | 
 | 215 | 		hblank -= hblank % (2 * CVT_H_GRANULARITY); | 
 | 216 | 		/* 14. find the total pixes per line */ | 
 | 217 | 		drm_mode->htotal = drm_mode->hdisplay + hblank; | 
 | 218 | 		drm_mode->hsync_end = drm_mode->hdisplay + hblank / 2; | 
 | 219 | 		drm_mode->hsync_start = drm_mode->hsync_end - | 
 | 220 | 			(drm_mode->htotal * CVT_HSYNC_PERCENTAGE) / 100; | 
 | 221 | 		drm_mode->hsync_start += CVT_H_GRANULARITY - | 
 | 222 | 			drm_mode->hsync_start % CVT_H_GRANULARITY; | 
 | 223 | 		/* fill the Vsync values */ | 
 | 224 | 		drm_mode->vsync_start = drm_mode->vdisplay + CVT_MIN_V_PORCH; | 
 | 225 | 		drm_mode->vsync_end = drm_mode->vsync_start + vsync; | 
 | 226 | 	} else { | 
 | 227 | 		/* Reduced blanking */ | 
 | 228 | 		/* Minimum vertical blanking interval time (µs)- default 460 */ | 
 | 229 | #define CVT_RB_MIN_VBLANK	460 | 
 | 230 | 		/* Fixed number of clocks for horizontal sync */ | 
 | 231 | #define CVT_RB_H_SYNC		32 | 
 | 232 | 		/* Fixed number of clocks for horizontal blanking */ | 
 | 233 | #define CVT_RB_H_BLANK		160 | 
 | 234 | 		/* Fixed number of lines for vertical front porch - default 3*/ | 
 | 235 | #define CVT_RB_VFPORCH		3 | 
 | 236 | 		int vbilines; | 
 | 237 | 		int tmp1, tmp2; | 
 | 238 | 		/* 8. Estimate Horizontal period. */ | 
 | 239 | 		tmp1 = HV_FACTOR * 1000000 - | 
 | 240 | 			CVT_RB_MIN_VBLANK * HV_FACTOR * vfieldrate; | 
 | 241 | 		tmp2 = vdisplay_rnd + 2 * vmargin; | 
 | 242 | 		hperiod = tmp1 / (tmp2 * vfieldrate); | 
 | 243 | 		/* 9. Find number of lines in vertical blanking */ | 
 | 244 | 		vbilines = CVT_RB_MIN_VBLANK * HV_FACTOR / hperiod + 1; | 
 | 245 | 		/* 10. Check if vertical blanking is sufficient */ | 
 | 246 | 		if (vbilines < (CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH)) | 
 | 247 | 			vbilines = CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH; | 
 | 248 | 		/* 11. Find total number of lines in vertical field */ | 
 | 249 | 		drm_mode->vtotal = vdisplay_rnd + 2 * vmargin + vbilines; | 
 | 250 | 		/* 12. Find total number of pixels in a line */ | 
 | 251 | 		drm_mode->htotal = drm_mode->hdisplay + CVT_RB_H_BLANK; | 
 | 252 | 		/* Fill in HSync values */ | 
 | 253 | 		drm_mode->hsync_end = drm_mode->hdisplay + CVT_RB_H_BLANK / 2; | 
| Adam Jackson | adde0f2 | 2010-08-23 10:19:14 -0400 | [diff] [blame] | 254 | 		drm_mode->hsync_start = drm_mode->hsync_end - CVT_RB_H_SYNC; | 
 | 255 | 		/* Fill in VSync values */ | 
 | 256 | 		drm_mode->vsync_start = drm_mode->vdisplay + CVT_RB_VFPORCH; | 
 | 257 | 		drm_mode->vsync_end = drm_mode->vsync_start + vsync; | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 258 | 	} | 
 | 259 | 	/* 15/13. Find pixel clock frequency (kHz for xf86) */ | 
 | 260 | 	drm_mode->clock = drm_mode->htotal * HV_FACTOR * 1000 / hperiod; | 
 | 261 | 	drm_mode->clock -= drm_mode->clock % CVT_CLOCK_STEP; | 
 | 262 | 	/* 18/16. Find actual vertical frame frequency */ | 
 | 263 | 	/* ignore - just set the mode flag for interlaced */ | 
| Adam Jackson | 171fdd8 | 2010-03-29 21:43:31 +0000 | [diff] [blame] | 264 | 	if (interlaced) { | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 265 | 		drm_mode->vtotal *= 2; | 
| Adam Jackson | 171fdd8 | 2010-03-29 21:43:31 +0000 | [diff] [blame] | 266 | 		drm_mode->flags |= DRM_MODE_FLAG_INTERLACE; | 
 | 267 | 	} | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 268 | 	/* Fill the mode line name */ | 
 | 269 | 	drm_mode_set_name(drm_mode); | 
 | 270 | 	if (reduced) | 
 | 271 | 		drm_mode->flags |= (DRM_MODE_FLAG_PHSYNC | | 
 | 272 | 					DRM_MODE_FLAG_NVSYNC); | 
 | 273 | 	else | 
 | 274 | 		drm_mode->flags |= (DRM_MODE_FLAG_PVSYNC | | 
 | 275 | 					DRM_MODE_FLAG_NHSYNC); | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 276 |  | 
| Adam Jackson | 171fdd8 | 2010-03-29 21:43:31 +0000 | [diff] [blame] | 277 | 	return drm_mode; | 
| Zhao Yakui | d782c3f | 2009-06-22 13:17:08 +0800 | [diff] [blame] | 278 | } | 
 | 279 | EXPORT_SYMBOL(drm_cvt_mode); | 
 | 280 |  | 
 | 281 | /** | 
| Adam Jackson | 7a37435 | 2010-03-29 21:43:30 +0000 | [diff] [blame] | 282 |  * drm_gtf_mode_complex - create the modeline based on full GTF algorithm | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 283 |  * | 
 | 284 |  * @dev		:drm device | 
 | 285 |  * @hdisplay	:hdisplay size | 
 | 286 |  * @vdisplay	:vdisplay size | 
 | 287 |  * @vrefresh	:vrefresh rate. | 
 | 288 |  * @interlaced	:whether the interlace is supported | 
| Adam Jackson | 7a37435 | 2010-03-29 21:43:30 +0000 | [diff] [blame] | 289 |  * @margins	:desired margin size | 
 | 290 |  * @GTF_[MCKJ]  :extended GTF formula parameters | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 291 |  * | 
 | 292 |  * LOCKING. | 
 | 293 |  * none. | 
 | 294 |  * | 
| Adam Jackson | 7a37435 | 2010-03-29 21:43:30 +0000 | [diff] [blame] | 295 |  * return the modeline based on full GTF algorithm. | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 296 |  * | 
| Adam Jackson | 7a37435 | 2010-03-29 21:43:30 +0000 | [diff] [blame] | 297 |  * GTF feature blocks specify C and J in multiples of 0.5, so we pass them | 
 | 298 |  * in here multiplied by two.  For a C of 40, pass in 80. | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 299 |  */ | 
| Adam Jackson | 7a37435 | 2010-03-29 21:43:30 +0000 | [diff] [blame] | 300 | struct drm_display_mode * | 
 | 301 | drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay, | 
 | 302 | 		     int vrefresh, bool interlaced, int margins, | 
 | 303 | 		     int GTF_M, int GTF_2C, int GTF_K, int GTF_2J) | 
 | 304 | {	/* 1) top/bottom margin size (% of height) - default: 1.8, */ | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 305 | #define	GTF_MARGIN_PERCENTAGE		18 | 
 | 306 | 	/* 2) character cell horizontal granularity (pixels) - default 8 */ | 
 | 307 | #define	GTF_CELL_GRAN			8 | 
 | 308 | 	/* 3) Minimum vertical porch (lines) - default 3 */ | 
 | 309 | #define	GTF_MIN_V_PORCH			1 | 
 | 310 | 	/* width of vsync in lines */ | 
 | 311 | #define V_SYNC_RQD			3 | 
 | 312 | 	/* width of hsync as % of total line */ | 
 | 313 | #define H_SYNC_PERCENT			8 | 
 | 314 | 	/* min time of vsync + back porch (microsec) */ | 
 | 315 | #define MIN_VSYNC_PLUS_BP		550 | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 316 | 	/* C' and M' are part of the Blanking Duty Cycle computation */ | 
| Adam Jackson | 7a37435 | 2010-03-29 21:43:30 +0000 | [diff] [blame] | 317 | #define GTF_C_PRIME	((((GTF_2C - GTF_2J) * GTF_K / 256) + GTF_2J) / 2) | 
 | 318 | #define GTF_M_PRIME	(GTF_K * GTF_M / 256) | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 319 | 	struct drm_display_mode *drm_mode; | 
 | 320 | 	unsigned int hdisplay_rnd, vdisplay_rnd, vfieldrate_rqd; | 
 | 321 | 	int top_margin, bottom_margin; | 
 | 322 | 	int interlace; | 
 | 323 | 	unsigned int hfreq_est; | 
 | 324 | 	int vsync_plus_bp, vback_porch; | 
 | 325 | 	unsigned int vtotal_lines, vfieldrate_est, hperiod; | 
 | 326 | 	unsigned int vfield_rate, vframe_rate; | 
 | 327 | 	int left_margin, right_margin; | 
 | 328 | 	unsigned int total_active_pixels, ideal_duty_cycle; | 
 | 329 | 	unsigned int hblank, total_pixels, pixel_freq; | 
 | 330 | 	int hsync, hfront_porch, vodd_front_porch_lines; | 
 | 331 | 	unsigned int tmp1, tmp2; | 
 | 332 |  | 
 | 333 | 	drm_mode = drm_mode_create(dev); | 
 | 334 | 	if (!drm_mode) | 
 | 335 | 		return NULL; | 
 | 336 |  | 
 | 337 | 	/* 1. In order to give correct results, the number of horizontal | 
 | 338 | 	 * pixels requested is first processed to ensure that it is divisible | 
 | 339 | 	 * by the character size, by rounding it to the nearest character | 
 | 340 | 	 * cell boundary: | 
 | 341 | 	 */ | 
 | 342 | 	hdisplay_rnd = (hdisplay + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN; | 
 | 343 | 	hdisplay_rnd = hdisplay_rnd * GTF_CELL_GRAN; | 
 | 344 |  | 
 | 345 | 	/* 2. If interlace is requested, the number of vertical lines assumed | 
 | 346 | 	 * by the calculation must be halved, as the computation calculates | 
 | 347 | 	 * the number of vertical lines per field. | 
 | 348 | 	 */ | 
 | 349 | 	if (interlaced) | 
 | 350 | 		vdisplay_rnd = vdisplay / 2; | 
 | 351 | 	else | 
 | 352 | 		vdisplay_rnd = vdisplay; | 
 | 353 |  | 
 | 354 | 	/* 3. Find the frame rate required: */ | 
 | 355 | 	if (interlaced) | 
 | 356 | 		vfieldrate_rqd = vrefresh * 2; | 
 | 357 | 	else | 
 | 358 | 		vfieldrate_rqd = vrefresh; | 
 | 359 |  | 
 | 360 | 	/* 4. Find number of lines in Top margin: */ | 
 | 361 | 	top_margin = 0; | 
 | 362 | 	if (margins) | 
 | 363 | 		top_margin = (vdisplay_rnd * GTF_MARGIN_PERCENTAGE + 500) / | 
 | 364 | 				1000; | 
 | 365 | 	/* 5. Find number of lines in bottom margin: */ | 
 | 366 | 	bottom_margin = top_margin; | 
 | 367 |  | 
 | 368 | 	/* 6. If interlace is required, then set variable interlace: */ | 
 | 369 | 	if (interlaced) | 
 | 370 | 		interlace = 1; | 
 | 371 | 	else | 
 | 372 | 		interlace = 0; | 
 | 373 |  | 
 | 374 | 	/* 7. Estimate the Horizontal frequency */ | 
 | 375 | 	{ | 
 | 376 | 		tmp1 = (1000000  - MIN_VSYNC_PLUS_BP * vfieldrate_rqd) / 500; | 
 | 377 | 		tmp2 = (vdisplay_rnd + 2 * top_margin + GTF_MIN_V_PORCH) * | 
 | 378 | 				2 + interlace; | 
 | 379 | 		hfreq_est = (tmp2 * 1000 * vfieldrate_rqd) / tmp1; | 
 | 380 | 	} | 
 | 381 |  | 
 | 382 | 	/* 8. Find the number of lines in V sync + back porch */ | 
 | 383 | 	/* [V SYNC+BP] = RINT(([MIN VSYNC+BP] * hfreq_est / 1000000)) */ | 
 | 384 | 	vsync_plus_bp = MIN_VSYNC_PLUS_BP * hfreq_est / 1000; | 
 | 385 | 	vsync_plus_bp = (vsync_plus_bp + 500) / 1000; | 
 | 386 | 	/*  9. Find the number of lines in V back porch alone: */ | 
 | 387 | 	vback_porch = vsync_plus_bp - V_SYNC_RQD; | 
 | 388 | 	/*  10. Find the total number of lines in Vertical field period: */ | 
 | 389 | 	vtotal_lines = vdisplay_rnd + top_margin + bottom_margin + | 
 | 390 | 			vsync_plus_bp + GTF_MIN_V_PORCH; | 
 | 391 | 	/*  11. Estimate the Vertical field frequency: */ | 
 | 392 | 	vfieldrate_est = hfreq_est / vtotal_lines; | 
 | 393 | 	/*  12. Find the actual horizontal period: */ | 
 | 394 | 	hperiod = 1000000 / (vfieldrate_rqd * vtotal_lines); | 
 | 395 |  | 
 | 396 | 	/*  13. Find the actual Vertical field frequency: */ | 
 | 397 | 	vfield_rate = hfreq_est / vtotal_lines; | 
 | 398 | 	/*  14. Find the Vertical frame frequency: */ | 
 | 399 | 	if (interlaced) | 
 | 400 | 		vframe_rate = vfield_rate / 2; | 
 | 401 | 	else | 
 | 402 | 		vframe_rate = vfield_rate; | 
 | 403 | 	/*  15. Find number of pixels in left margin: */ | 
 | 404 | 	if (margins) | 
 | 405 | 		left_margin = (hdisplay_rnd * GTF_MARGIN_PERCENTAGE + 500) / | 
 | 406 | 				1000; | 
 | 407 | 	else | 
 | 408 | 		left_margin = 0; | 
 | 409 |  | 
 | 410 | 	/* 16.Find number of pixels in right margin: */ | 
 | 411 | 	right_margin = left_margin; | 
 | 412 | 	/* 17.Find total number of active pixels in image and left and right */ | 
 | 413 | 	total_active_pixels = hdisplay_rnd + left_margin + right_margin; | 
 | 414 | 	/* 18.Find the ideal blanking duty cycle from blanking duty cycle */ | 
 | 415 | 	ideal_duty_cycle = GTF_C_PRIME * 1000 - | 
 | 416 | 				(GTF_M_PRIME * 1000000 / hfreq_est); | 
 | 417 | 	/* 19.Find the number of pixels in the blanking time to the nearest | 
 | 418 | 	 * double character cell: */ | 
 | 419 | 	hblank = total_active_pixels * ideal_duty_cycle / | 
 | 420 | 			(100000 - ideal_duty_cycle); | 
 | 421 | 	hblank = (hblank + GTF_CELL_GRAN) / (2 * GTF_CELL_GRAN); | 
 | 422 | 	hblank = hblank * 2 * GTF_CELL_GRAN; | 
 | 423 | 	/* 20.Find total number of pixels: */ | 
 | 424 | 	total_pixels = total_active_pixels + hblank; | 
 | 425 | 	/* 21.Find pixel clock frequency: */ | 
 | 426 | 	pixel_freq = total_pixels * hfreq_est / 1000; | 
 | 427 | 	/* Stage 1 computations are now complete; I should really pass | 
 | 428 | 	 * the results to another function and do the Stage 2 computations, | 
 | 429 | 	 * but I only need a few more values so I'll just append the | 
 | 430 | 	 * computations here for now */ | 
 | 431 | 	/* 17. Find the number of pixels in the horizontal sync period: */ | 
 | 432 | 	hsync = H_SYNC_PERCENT * total_pixels / 100; | 
 | 433 | 	hsync = (hsync + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN; | 
 | 434 | 	hsync = hsync * GTF_CELL_GRAN; | 
 | 435 | 	/* 18. Find the number of pixels in horizontal front porch period */ | 
 | 436 | 	hfront_porch = hblank / 2 - hsync; | 
 | 437 | 	/*  36. Find the number of lines in the odd front porch period: */ | 
 | 438 | 	vodd_front_porch_lines = GTF_MIN_V_PORCH ; | 
 | 439 |  | 
 | 440 | 	/* finally, pack the results in the mode struct */ | 
 | 441 | 	drm_mode->hdisplay = hdisplay_rnd; | 
 | 442 | 	drm_mode->hsync_start = hdisplay_rnd + hfront_porch; | 
 | 443 | 	drm_mode->hsync_end = drm_mode->hsync_start + hsync; | 
 | 444 | 	drm_mode->htotal = total_pixels; | 
 | 445 | 	drm_mode->vdisplay = vdisplay_rnd; | 
 | 446 | 	drm_mode->vsync_start = vdisplay_rnd + vodd_front_porch_lines; | 
 | 447 | 	drm_mode->vsync_end = drm_mode->vsync_start + V_SYNC_RQD; | 
 | 448 | 	drm_mode->vtotal = vtotal_lines; | 
 | 449 |  | 
 | 450 | 	drm_mode->clock = pixel_freq; | 
 | 451 |  | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 452 | 	if (interlaced) { | 
 | 453 | 		drm_mode->vtotal *= 2; | 
 | 454 | 		drm_mode->flags |= DRM_MODE_FLAG_INTERLACE; | 
 | 455 | 	} | 
 | 456 |  | 
| Adam Jackson | 171fdd8 | 2010-03-29 21:43:31 +0000 | [diff] [blame] | 457 | 	drm_mode_set_name(drm_mode); | 
| Adam Jackson | c385e50c | 2010-04-08 19:00:30 +0000 | [diff] [blame] | 458 | 	if (GTF_M == 600 && GTF_2C == 80 && GTF_K == 128 && GTF_2J == 40) | 
 | 459 | 		drm_mode->flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC; | 
 | 460 | 	else | 
 | 461 | 		drm_mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC; | 
| Adam Jackson | 171fdd8 | 2010-03-29 21:43:31 +0000 | [diff] [blame] | 462 |  | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 463 | 	return drm_mode; | 
 | 464 | } | 
| Adam Jackson | 7a37435 | 2010-03-29 21:43:30 +0000 | [diff] [blame] | 465 | EXPORT_SYMBOL(drm_gtf_mode_complex); | 
 | 466 |  | 
 | 467 | /** | 
 | 468 |  * drm_gtf_mode - create the modeline based on GTF algorithm | 
 | 469 |  * | 
 | 470 |  * @dev		:drm device | 
 | 471 |  * @hdisplay	:hdisplay size | 
 | 472 |  * @vdisplay	:vdisplay size | 
 | 473 |  * @vrefresh	:vrefresh rate. | 
 | 474 |  * @interlaced	:whether the interlace is supported | 
 | 475 |  * @margins	:whether the margin is supported | 
 | 476 |  * | 
 | 477 |  * LOCKING. | 
 | 478 |  * none. | 
 | 479 |  * | 
 | 480 |  * return the modeline based on GTF algorithm | 
 | 481 |  * | 
 | 482 |  * This function is to create the modeline based on the GTF algorithm. | 
 | 483 |  * Generalized Timing Formula is derived from: | 
 | 484 |  *	GTF Spreadsheet by Andy Morrish (1/5/97) | 
 | 485 |  *	available at http://www.vesa.org | 
 | 486 |  * | 
 | 487 |  * And it is copied from the file of xserver/hw/xfree86/modes/xf86gtf.c. | 
 | 488 |  * What I have done is to translate it by using integer calculation. | 
 | 489 |  * I also refer to the function of fb_get_mode in the file of | 
 | 490 |  * drivers/video/fbmon.c | 
 | 491 |  * | 
 | 492 |  * Standard GTF parameters: | 
 | 493 |  * M = 600 | 
 | 494 |  * C = 40 | 
 | 495 |  * K = 128 | 
 | 496 |  * J = 20 | 
 | 497 |  */ | 
 | 498 | struct drm_display_mode * | 
 | 499 | drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, | 
 | 500 | 	     bool lace, int margins) | 
 | 501 | { | 
 | 502 | 	return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh, lace, | 
 | 503 | 				    margins, 600, 40 * 2, 128, 20 * 2); | 
 | 504 | } | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 505 | EXPORT_SYMBOL(drm_gtf_mode); | 
| Adam Jackson | 7a37435 | 2010-03-29 21:43:30 +0000 | [diff] [blame] | 506 |  | 
| Zhao Yakui | 26bbdad | 2009-06-22 13:17:09 +0800 | [diff] [blame] | 507 | /** | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 508 |  * drm_mode_set_name - set the name on a mode | 
 | 509 |  * @mode: name will be set in this mode | 
 | 510 |  * | 
 | 511 |  * LOCKING: | 
 | 512 |  * None. | 
 | 513 |  * | 
 | 514 |  * Set the name of @mode to a standard format. | 
 | 515 |  */ | 
 | 516 | void drm_mode_set_name(struct drm_display_mode *mode) | 
 | 517 | { | 
| Adam Jackson | 171fdd8 | 2010-03-29 21:43:31 +0000 | [diff] [blame] | 518 | 	bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); | 
 | 519 |  | 
 | 520 | 	snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d%s", | 
 | 521 | 		 mode->hdisplay, mode->vdisplay, | 
 | 522 | 		 interlaced ? "i" : ""); | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 523 | } | 
 | 524 | EXPORT_SYMBOL(drm_mode_set_name); | 
 | 525 |  | 
 | 526 | /** | 
 | 527 |  * drm_mode_list_concat - move modes from one list to another | 
 | 528 |  * @head: source list | 
 | 529 |  * @new: dst list | 
 | 530 |  * | 
 | 531 |  * LOCKING: | 
 | 532 |  * Caller must ensure both lists are locked. | 
 | 533 |  * | 
 | 534 |  * Move all the modes from @head to @new. | 
 | 535 |  */ | 
 | 536 | void drm_mode_list_concat(struct list_head *head, struct list_head *new) | 
 | 537 | { | 
 | 538 |  | 
 | 539 | 	struct list_head *entry, *tmp; | 
 | 540 |  | 
 | 541 | 	list_for_each_safe(entry, tmp, head) { | 
 | 542 | 		list_move_tail(entry, new); | 
 | 543 | 	} | 
 | 544 | } | 
 | 545 | EXPORT_SYMBOL(drm_mode_list_concat); | 
 | 546 |  | 
 | 547 | /** | 
 | 548 |  * drm_mode_width - get the width of a mode | 
 | 549 |  * @mode: mode | 
 | 550 |  * | 
 | 551 |  * LOCKING: | 
 | 552 |  * None. | 
 | 553 |  * | 
 | 554 |  * Return @mode's width (hdisplay) value. | 
 | 555 |  * | 
 | 556 |  * FIXME: is this needed? | 
 | 557 |  * | 
 | 558 |  * RETURNS: | 
 | 559 |  * @mode->hdisplay | 
 | 560 |  */ | 
 | 561 | int drm_mode_width(struct drm_display_mode *mode) | 
 | 562 | { | 
 | 563 | 	return mode->hdisplay; | 
 | 564 |  | 
 | 565 | } | 
 | 566 | EXPORT_SYMBOL(drm_mode_width); | 
 | 567 |  | 
 | 568 | /** | 
 | 569 |  * drm_mode_height - get the height of a mode | 
 | 570 |  * @mode: mode | 
 | 571 |  * | 
 | 572 |  * LOCKING: | 
 | 573 |  * None. | 
 | 574 |  * | 
 | 575 |  * Return @mode's height (vdisplay) value. | 
 | 576 |  * | 
 | 577 |  * FIXME: is this needed? | 
 | 578 |  * | 
 | 579 |  * RETURNS: | 
 | 580 |  * @mode->vdisplay | 
 | 581 |  */ | 
 | 582 | int drm_mode_height(struct drm_display_mode *mode) | 
 | 583 | { | 
 | 584 | 	return mode->vdisplay; | 
 | 585 | } | 
 | 586 | EXPORT_SYMBOL(drm_mode_height); | 
 | 587 |  | 
| Adam Jackson | 7ac96a9 | 2009-12-03 17:44:37 -0500 | [diff] [blame] | 588 | /** drm_mode_hsync - get the hsync of a mode | 
 | 589 |  * @mode: mode | 
 | 590 |  * | 
 | 591 |  * LOCKING: | 
 | 592 |  * None. | 
 | 593 |  * | 
 | 594 |  * Return @modes's hsync rate in kHz, rounded to the nearest int. | 
 | 595 |  */ | 
| Chris Wilson | b1f559e | 2011-01-26 09:49:47 +0000 | [diff] [blame] | 596 | int drm_mode_hsync(const struct drm_display_mode *mode) | 
| Adam Jackson | 7ac96a9 | 2009-12-03 17:44:37 -0500 | [diff] [blame] | 597 | { | 
 | 598 | 	unsigned int calc_val; | 
 | 599 |  | 
 | 600 | 	if (mode->hsync) | 
 | 601 | 		return mode->hsync; | 
 | 602 |  | 
 | 603 | 	if (mode->htotal < 0) | 
 | 604 | 		return 0; | 
 | 605 |  | 
 | 606 | 	calc_val = (mode->clock * 1000) / mode->htotal; /* hsync in Hz */ | 
 | 607 | 	calc_val += 500;				/* round to 1000Hz */ | 
 | 608 | 	calc_val /= 1000;				/* truncate to kHz */ | 
 | 609 |  | 
 | 610 | 	return calc_val; | 
 | 611 | } | 
 | 612 | EXPORT_SYMBOL(drm_mode_hsync); | 
 | 613 |  | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 614 | /** | 
 | 615 |  * drm_mode_vrefresh - get the vrefresh of a mode | 
 | 616 |  * @mode: mode | 
 | 617 |  * | 
 | 618 |  * LOCKING: | 
 | 619 |  * None. | 
 | 620 |  * | 
| Adam Jackson | 7ac96a9 | 2009-12-03 17:44:37 -0500 | [diff] [blame] | 621 |  * Return @mode's vrefresh rate in Hz or calculate it if necessary. | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 622 |  * | 
 | 623 |  * FIXME: why is this needed?  shouldn't vrefresh be set already? | 
 | 624 |  * | 
 | 625 |  * RETURNS: | 
| Zhao Yakui | 559ee21 | 2009-09-03 09:33:47 +0800 | [diff] [blame] | 626 |  * Vertical refresh rate. It will be the result of actual value plus 0.5. | 
 | 627 |  * If it is 70.288, it will return 70Hz. | 
 | 628 |  * If it is 59.6, it will return 60Hz. | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 629 |  */ | 
| Chris Wilson | b1f559e | 2011-01-26 09:49:47 +0000 | [diff] [blame] | 630 | int drm_mode_vrefresh(const struct drm_display_mode *mode) | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 631 | { | 
 | 632 | 	int refresh = 0; | 
 | 633 | 	unsigned int calc_val; | 
 | 634 |  | 
 | 635 | 	if (mode->vrefresh > 0) | 
 | 636 | 		refresh = mode->vrefresh; | 
 | 637 | 	else if (mode->htotal > 0 && mode->vtotal > 0) { | 
| Zhao Yakui | 559ee21 | 2009-09-03 09:33:47 +0800 | [diff] [blame] | 638 | 		int vtotal; | 
 | 639 | 		vtotal = mode->vtotal; | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 640 | 		/* work out vrefresh the value will be x1000 */ | 
 | 641 | 		calc_val = (mode->clock * 1000); | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 642 | 		calc_val /= mode->htotal; | 
| Zhao Yakui | 559ee21 | 2009-09-03 09:33:47 +0800 | [diff] [blame] | 643 | 		refresh = (calc_val + vtotal / 2) / vtotal; | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 644 |  | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 645 | 		if (mode->flags & DRM_MODE_FLAG_INTERLACE) | 
 | 646 | 			refresh *= 2; | 
 | 647 | 		if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | 
 | 648 | 			refresh /= 2; | 
 | 649 | 		if (mode->vscan > 1) | 
 | 650 | 			refresh /= mode->vscan; | 
 | 651 | 	} | 
 | 652 | 	return refresh; | 
 | 653 | } | 
 | 654 | EXPORT_SYMBOL(drm_mode_vrefresh); | 
 | 655 |  | 
 | 656 | /** | 
 | 657 |  * drm_mode_set_crtcinfo - set CRTC modesetting parameters | 
 | 658 |  * @p: mode | 
 | 659 |  * @adjust_flags: unused? (FIXME) | 
 | 660 |  * | 
 | 661 |  * LOCKING: | 
 | 662 |  * None. | 
 | 663 |  * | 
 | 664 |  * Setup the CRTC modesetting parameters for @p, adjusting if necessary. | 
 | 665 |  */ | 
 | 666 | void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) | 
 | 667 | { | 
 | 668 | 	if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN)) | 
 | 669 | 		return; | 
 | 670 |  | 
 | 671 | 	p->crtc_hdisplay = p->hdisplay; | 
 | 672 | 	p->crtc_hsync_start = p->hsync_start; | 
 | 673 | 	p->crtc_hsync_end = p->hsync_end; | 
 | 674 | 	p->crtc_htotal = p->htotal; | 
 | 675 | 	p->crtc_hskew = p->hskew; | 
 | 676 | 	p->crtc_vdisplay = p->vdisplay; | 
 | 677 | 	p->crtc_vsync_start = p->vsync_start; | 
 | 678 | 	p->crtc_vsync_end = p->vsync_end; | 
 | 679 | 	p->crtc_vtotal = p->vtotal; | 
 | 680 |  | 
 | 681 | 	if (p->flags & DRM_MODE_FLAG_INTERLACE) { | 
 | 682 | 		if (adjust_flags & CRTC_INTERLACE_HALVE_V) { | 
 | 683 | 			p->crtc_vdisplay /= 2; | 
 | 684 | 			p->crtc_vsync_start /= 2; | 
 | 685 | 			p->crtc_vsync_end /= 2; | 
 | 686 | 			p->crtc_vtotal /= 2; | 
 | 687 | 		} | 
 | 688 |  | 
 | 689 | 		p->crtc_vtotal |= 1; | 
 | 690 | 	} | 
 | 691 |  | 
 | 692 | 	if (p->flags & DRM_MODE_FLAG_DBLSCAN) { | 
 | 693 | 		p->crtc_vdisplay *= 2; | 
 | 694 | 		p->crtc_vsync_start *= 2; | 
 | 695 | 		p->crtc_vsync_end *= 2; | 
 | 696 | 		p->crtc_vtotal *= 2; | 
 | 697 | 	} | 
 | 698 |  | 
 | 699 | 	if (p->vscan > 1) { | 
 | 700 | 		p->crtc_vdisplay *= p->vscan; | 
 | 701 | 		p->crtc_vsync_start *= p->vscan; | 
 | 702 | 		p->crtc_vsync_end *= p->vscan; | 
 | 703 | 		p->crtc_vtotal *= p->vscan; | 
 | 704 | 	} | 
 | 705 |  | 
 | 706 | 	p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); | 
 | 707 | 	p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); | 
 | 708 | 	p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); | 
 | 709 | 	p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); | 
 | 710 |  | 
 | 711 | 	p->crtc_hadjusted = false; | 
 | 712 | 	p->crtc_vadjusted = false; | 
 | 713 | } | 
 | 714 | EXPORT_SYMBOL(drm_mode_set_crtcinfo); | 
 | 715 |  | 
 | 716 |  | 
 | 717 | /** | 
 | 718 |  * drm_mode_duplicate - allocate and duplicate an existing mode | 
 | 719 |  * @m: mode to duplicate | 
 | 720 |  * | 
 | 721 |  * LOCKING: | 
 | 722 |  * None. | 
 | 723 |  * | 
 | 724 |  * Just allocate a new mode, copy the existing mode into it, and return | 
 | 725 |  * a pointer to it.  Used to create new instances of established modes. | 
 | 726 |  */ | 
 | 727 | struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, | 
| Chris Wilson | b1f559e | 2011-01-26 09:49:47 +0000 | [diff] [blame] | 728 | 					    const struct drm_display_mode *mode) | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 729 | { | 
 | 730 | 	struct drm_display_mode *nmode; | 
 | 731 | 	int new_id; | 
 | 732 |  | 
 | 733 | 	nmode = drm_mode_create(dev); | 
 | 734 | 	if (!nmode) | 
 | 735 | 		return NULL; | 
 | 736 |  | 
 | 737 | 	new_id = nmode->base.id; | 
 | 738 | 	*nmode = *mode; | 
 | 739 | 	nmode->base.id = new_id; | 
 | 740 | 	INIT_LIST_HEAD(&nmode->head); | 
 | 741 | 	return nmode; | 
 | 742 | } | 
 | 743 | EXPORT_SYMBOL(drm_mode_duplicate); | 
 | 744 |  | 
 | 745 | /** | 
 | 746 |  * drm_mode_equal - test modes for equality | 
 | 747 |  * @mode1: first mode | 
 | 748 |  * @mode2: second mode | 
 | 749 |  * | 
 | 750 |  * LOCKING: | 
 | 751 |  * None. | 
 | 752 |  * | 
 | 753 |  * Check to see if @mode1 and @mode2 are equivalent. | 
 | 754 |  * | 
 | 755 |  * RETURNS: | 
 | 756 |  * True if the modes are equal, false otherwise. | 
 | 757 |  */ | 
 | 758 | bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2) | 
 | 759 | { | 
 | 760 | 	/* do clock check convert to PICOS so fb modes get matched | 
 | 761 | 	 * the same */ | 
 | 762 | 	if (mode1->clock && mode2->clock) { | 
 | 763 | 		if (KHZ2PICOS(mode1->clock) != KHZ2PICOS(mode2->clock)) | 
 | 764 | 			return false; | 
 | 765 | 	} else if (mode1->clock != mode2->clock) | 
 | 766 | 		return false; | 
 | 767 |  | 
 | 768 | 	if (mode1->hdisplay == mode2->hdisplay && | 
 | 769 | 	    mode1->hsync_start == mode2->hsync_start && | 
 | 770 | 	    mode1->hsync_end == mode2->hsync_end && | 
 | 771 | 	    mode1->htotal == mode2->htotal && | 
 | 772 | 	    mode1->hskew == mode2->hskew && | 
 | 773 | 	    mode1->vdisplay == mode2->vdisplay && | 
 | 774 | 	    mode1->vsync_start == mode2->vsync_start && | 
 | 775 | 	    mode1->vsync_end == mode2->vsync_end && | 
 | 776 | 	    mode1->vtotal == mode2->vtotal && | 
 | 777 | 	    mode1->vscan == mode2->vscan && | 
 | 778 | 	    mode1->flags == mode2->flags) | 
 | 779 | 		return true; | 
 | 780 |  | 
 | 781 | 	return false; | 
 | 782 | } | 
 | 783 | EXPORT_SYMBOL(drm_mode_equal); | 
 | 784 |  | 
 | 785 | /** | 
 | 786 |  * drm_mode_validate_size - make sure modes adhere to size constraints | 
 | 787 |  * @dev: DRM device | 
 | 788 |  * @mode_list: list of modes to check | 
 | 789 |  * @maxX: maximum width | 
 | 790 |  * @maxY: maximum height | 
 | 791 |  * @maxPitch: max pitch | 
 | 792 |  * | 
 | 793 |  * LOCKING: | 
 | 794 |  * Caller must hold a lock protecting @mode_list. | 
 | 795 |  * | 
 | 796 |  * The DRM device (@dev) has size and pitch limits.  Here we validate the | 
 | 797 |  * modes we probed for @dev against those limits and set their status as | 
 | 798 |  * necessary. | 
 | 799 |  */ | 
 | 800 | void drm_mode_validate_size(struct drm_device *dev, | 
 | 801 | 			    struct list_head *mode_list, | 
 | 802 | 			    int maxX, int maxY, int maxPitch) | 
 | 803 | { | 
 | 804 | 	struct drm_display_mode *mode; | 
 | 805 |  | 
 | 806 | 	list_for_each_entry(mode, mode_list, head) { | 
 | 807 | 		if (maxPitch > 0 && mode->hdisplay > maxPitch) | 
 | 808 | 			mode->status = MODE_BAD_WIDTH; | 
 | 809 |  | 
 | 810 | 		if (maxX > 0 && mode->hdisplay > maxX) | 
 | 811 | 			mode->status = MODE_VIRTUAL_X; | 
 | 812 |  | 
 | 813 | 		if (maxY > 0 && mode->vdisplay > maxY) | 
 | 814 | 			mode->status = MODE_VIRTUAL_Y; | 
 | 815 | 	} | 
 | 816 | } | 
 | 817 | EXPORT_SYMBOL(drm_mode_validate_size); | 
 | 818 |  | 
 | 819 | /** | 
 | 820 |  * drm_mode_validate_clocks - validate modes against clock limits | 
 | 821 |  * @dev: DRM device | 
 | 822 |  * @mode_list: list of modes to check | 
 | 823 |  * @min: minimum clock rate array | 
 | 824 |  * @max: maximum clock rate array | 
 | 825 |  * @n_ranges: number of clock ranges (size of arrays) | 
 | 826 |  * | 
 | 827 |  * LOCKING: | 
 | 828 |  * Caller must hold a lock protecting @mode_list. | 
 | 829 |  * | 
 | 830 |  * Some code may need to check a mode list against the clock limits of the | 
 | 831 |  * device in question.  This function walks the mode list, testing to make | 
 | 832 |  * sure each mode falls within a given range (defined by @min and @max | 
 | 833 |  * arrays) and sets @mode->status as needed. | 
 | 834 |  */ | 
 | 835 | void drm_mode_validate_clocks(struct drm_device *dev, | 
 | 836 | 			      struct list_head *mode_list, | 
 | 837 | 			      int *min, int *max, int n_ranges) | 
 | 838 | { | 
 | 839 | 	struct drm_display_mode *mode; | 
 | 840 | 	int i; | 
 | 841 |  | 
 | 842 | 	list_for_each_entry(mode, mode_list, head) { | 
 | 843 | 		bool good = false; | 
 | 844 | 		for (i = 0; i < n_ranges; i++) { | 
 | 845 | 			if (mode->clock >= min[i] && mode->clock <= max[i]) { | 
 | 846 | 				good = true; | 
 | 847 | 				break; | 
 | 848 | 			} | 
 | 849 | 		} | 
 | 850 | 		if (!good) | 
 | 851 | 			mode->status = MODE_CLOCK_RANGE; | 
 | 852 | 	} | 
 | 853 | } | 
 | 854 | EXPORT_SYMBOL(drm_mode_validate_clocks); | 
 | 855 |  | 
 | 856 | /** | 
 | 857 |  * drm_mode_prune_invalid - remove invalid modes from mode list | 
 | 858 |  * @dev: DRM device | 
 | 859 |  * @mode_list: list of modes to check | 
 | 860 |  * @verbose: be verbose about it | 
 | 861 |  * | 
 | 862 |  * LOCKING: | 
 | 863 |  * Caller must hold a lock protecting @mode_list. | 
 | 864 |  * | 
 | 865 |  * Once mode list generation is complete, a caller can use this routine to | 
 | 866 |  * remove invalid modes from a mode list.  If any of the modes have a | 
 | 867 |  * status other than %MODE_OK, they are removed from @mode_list and freed. | 
 | 868 |  */ | 
 | 869 | void drm_mode_prune_invalid(struct drm_device *dev, | 
 | 870 | 			    struct list_head *mode_list, bool verbose) | 
 | 871 | { | 
 | 872 | 	struct drm_display_mode *mode, *t; | 
 | 873 |  | 
 | 874 | 	list_for_each_entry_safe(mode, t, mode_list, head) { | 
 | 875 | 		if (mode->status != MODE_OK) { | 
 | 876 | 			list_del(&mode->head); | 
 | 877 | 			if (verbose) { | 
 | 878 | 				drm_mode_debug_printmodeline(mode); | 
| Zhao Yakui | f940f37 | 2009-07-20 13:48:05 +0800 | [diff] [blame] | 879 | 				DRM_DEBUG_KMS("Not using %s mode %d\n", | 
| yakui_zhao | f053185 | 2009-06-02 14:12:47 +0800 | [diff] [blame] | 880 | 					mode->name, mode->status); | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 881 | 			} | 
 | 882 | 			drm_mode_destroy(dev, mode); | 
 | 883 | 		} | 
 | 884 | 	} | 
 | 885 | } | 
 | 886 | EXPORT_SYMBOL(drm_mode_prune_invalid); | 
 | 887 |  | 
 | 888 | /** | 
 | 889 |  * drm_mode_compare - compare modes for favorability | 
| Dave Chinner | 2c76127 | 2010-01-12 17:39:16 +1100 | [diff] [blame] | 890 |  * @priv: unused | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 891 |  * @lh_a: list_head for first mode | 
 | 892 |  * @lh_b: list_head for second mode | 
 | 893 |  * | 
 | 894 |  * LOCKING: | 
 | 895 |  * None. | 
 | 896 |  * | 
 | 897 |  * Compare two modes, given by @lh_a and @lh_b, returning a value indicating | 
 | 898 |  * which is better. | 
 | 899 |  * | 
 | 900 |  * RETURNS: | 
 | 901 |  * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or | 
 | 902 |  * positive if @lh_b is better than @lh_a. | 
 | 903 |  */ | 
| Dave Chinner | 2c76127 | 2010-01-12 17:39:16 +1100 | [diff] [blame] | 904 | static int drm_mode_compare(void *priv, struct list_head *lh_a, struct list_head *lh_b) | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 905 | { | 
 | 906 | 	struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head); | 
 | 907 | 	struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head); | 
 | 908 | 	int diff; | 
 | 909 |  | 
 | 910 | 	diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) - | 
 | 911 | 		((a->type & DRM_MODE_TYPE_PREFERRED) != 0); | 
 | 912 | 	if (diff) | 
 | 913 | 		return diff; | 
 | 914 | 	diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay; | 
 | 915 | 	if (diff) | 
 | 916 | 		return diff; | 
 | 917 | 	diff = b->clock - a->clock; | 
 | 918 | 	return diff; | 
 | 919 | } | 
 | 920 |  | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 921 | /** | 
 | 922 |  * drm_mode_sort - sort mode list | 
 | 923 |  * @mode_list: list to sort | 
 | 924 |  * | 
 | 925 |  * LOCKING: | 
 | 926 |  * Caller must hold a lock protecting @mode_list. | 
 | 927 |  * | 
 | 928 |  * Sort @mode_list by favorability, putting good modes first. | 
 | 929 |  */ | 
 | 930 | void drm_mode_sort(struct list_head *mode_list) | 
 | 931 | { | 
| Dave Chinner | 2c76127 | 2010-01-12 17:39:16 +1100 | [diff] [blame] | 932 | 	list_sort(NULL, mode_list, drm_mode_compare); | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 933 | } | 
 | 934 | EXPORT_SYMBOL(drm_mode_sort); | 
 | 935 |  | 
 | 936 | /** | 
 | 937 |  * drm_mode_connector_list_update - update the mode list for the connector | 
 | 938 |  * @connector: the connector to update | 
 | 939 |  * | 
 | 940 |  * LOCKING: | 
 | 941 |  * Caller must hold a lock protecting @mode_list. | 
 | 942 |  * | 
 | 943 |  * This moves the modes from the @connector probed_modes list | 
 | 944 |  * to the actual mode list. It compares the probed mode against the current | 
 | 945 |  * list and only adds different modes. All modes unverified after this point | 
 | 946 |  * will be removed by the prune invalid modes. | 
 | 947 |  */ | 
 | 948 | void drm_mode_connector_list_update(struct drm_connector *connector) | 
 | 949 | { | 
 | 950 | 	struct drm_display_mode *mode; | 
 | 951 | 	struct drm_display_mode *pmode, *pt; | 
 | 952 | 	int found_it; | 
 | 953 |  | 
 | 954 | 	list_for_each_entry_safe(pmode, pt, &connector->probed_modes, | 
 | 955 | 				 head) { | 
 | 956 | 		found_it = 0; | 
 | 957 | 		/* go through current modes checking for the new probed mode */ | 
 | 958 | 		list_for_each_entry(mode, &connector->modes, head) { | 
 | 959 | 			if (drm_mode_equal(pmode, mode)) { | 
 | 960 | 				found_it = 1; | 
 | 961 | 				/* if equal delete the probed mode */ | 
 | 962 | 				mode->status = pmode->status; | 
| Keith Packard | 38d5487 | 2009-07-20 14:49:17 -0700 | [diff] [blame] | 963 | 				/* Merge type bits together */ | 
 | 964 | 				mode->type |= pmode->type; | 
| Dave Airlie | f453ba0 | 2008-11-07 14:05:41 -0800 | [diff] [blame] | 965 | 				list_del(&pmode->head); | 
 | 966 | 				drm_mode_destroy(connector->dev, pmode); | 
 | 967 | 				break; | 
 | 968 | 			} | 
 | 969 | 		} | 
 | 970 |  | 
 | 971 | 		if (!found_it) { | 
 | 972 | 			list_move_tail(&pmode->head, &connector->modes); | 
 | 973 | 		} | 
 | 974 | 	} | 
 | 975 | } | 
 | 976 | EXPORT_SYMBOL(drm_mode_connector_list_update); |