]>
Commit | Line | Data |
---|---|---|
f453ba04 | 1 | /* |
f453ba04 DA |
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> | |
d782c3f9 | 6 | * Copyright 2005-2006 Luc Verhaegen |
26bbdada | 7 | * Copyright (c) 2001, Andy Ritger aritger@nvidia.com |
f453ba04 DA |
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 | ||
e08ab74b | 33 | #include <linux/ctype.h> |
fb28b3f0 TZ |
34 | #include <linux/export.h> |
35 | #include <linux/fb.h> /* for KHZ2PICOS() */ | |
f453ba04 | 36 | #include <linux/list.h> |
2c761270 | 37 | #include <linux/list_sort.h> |
fb28b3f0 | 38 | #include <linux/of.h> |
0500c04e | 39 | |
95ae342d | 40 | #include <video/of_display_timing.h> |
edb37a95 | 41 | #include <video/of_videomode.h> |
ebc64e45 | 42 | #include <video/videomode.h> |
0500c04e SR |
43 | |
44 | #include <drm/drm_crtc.h> | |
45 | #include <drm/drm_device.h> | |
255490f9 | 46 | #include <drm/drm_edid.h> |
55310008 | 47 | #include <drm/drm_modes.h> |
0500c04e | 48 | #include <drm/drm_print.h> |
f453ba04 | 49 | |
8bd441b2 SV |
50 | #include "drm_crtc_internal.h" |
51 | ||
f453ba04 | 52 | /** |
3ec0db81 | 53 | * drm_mode_debug_printmodeline - print a mode to dmesg |
f453ba04 DA |
54 | * @mode: mode to print |
55 | * | |
f453ba04 DA |
56 | * Describe @mode using DRM_DEBUG. |
57 | */ | |
0b3904ab | 58 | void drm_mode_debug_printmodeline(const struct drm_display_mode *mode) |
f453ba04 | 59 | { |
65c7dc18 | 60 | DRM_DEBUG_KMS("Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); |
f453ba04 DA |
61 | } |
62 | EXPORT_SYMBOL(drm_mode_debug_printmodeline); | |
63 | ||
8bd441b2 SV |
64 | /** |
65 | * drm_mode_create - create a new display mode | |
66 | * @dev: DRM device | |
67 | * | |
f5aabb97 SV |
68 | * Create a new, cleared drm_display_mode with kzalloc, allocate an ID for it |
69 | * and return it. | |
8bd441b2 | 70 | * |
f5aabb97 | 71 | * Returns: |
8bd441b2 SV |
72 | * Pointer to new mode on success, NULL on error. |
73 | */ | |
74 | struct drm_display_mode *drm_mode_create(struct drm_device *dev) | |
75 | { | |
76 | struct drm_display_mode *nmode; | |
77 | ||
78 | nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); | |
79 | if (!nmode) | |
80 | return NULL; | |
81 | ||
8bd441b2 SV |
82 | return nmode; |
83 | } | |
84 | EXPORT_SYMBOL(drm_mode_create); | |
85 | ||
86 | /** | |
87 | * drm_mode_destroy - remove a mode | |
88 | * @dev: DRM device | |
89 | * @mode: mode to remove | |
90 | * | |
f5aabb97 | 91 | * Release @mode's unique ID, then free it @mode structure itself using kfree. |
8bd441b2 SV |
92 | */ |
93 | void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) | |
94 | { | |
95 | if (!mode) | |
96 | return; | |
97 | ||
8bd441b2 SV |
98 | kfree(mode); |
99 | } | |
100 | EXPORT_SYMBOL(drm_mode_destroy); | |
101 | ||
102 | /** | |
f5aabb97 | 103 | * drm_mode_probed_add - add a mode to a connector's probed_mode list |
8bd441b2 SV |
104 | * @connector: connector the new mode |
105 | * @mode: mode data | |
106 | * | |
f5aabb97 SV |
107 | * Add @mode to @connector's probed_mode list for later use. This list should |
108 | * then in a second step get filtered and all the modes actually supported by | |
109 | * the hardware moved to the @connector's modes list. | |
8bd441b2 SV |
110 | */ |
111 | void drm_mode_probed_add(struct drm_connector *connector, | |
112 | struct drm_display_mode *mode) | |
113 | { | |
114 | WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); | |
115 | ||
116 | list_add_tail(&mode->head, &connector->probed_modes); | |
117 | } | |
118 | EXPORT_SYMBOL(drm_mode_probed_add); | |
119 | ||
4fcd2385 MR |
120 | enum drm_mode_analog { |
121 | DRM_MODE_ANALOG_NTSC, /* 525 lines, 60Hz */ | |
122 | DRM_MODE_ANALOG_PAL, /* 625 lines, 50Hz */ | |
123 | }; | |
124 | ||
125 | /* | |
126 | * The timings come from: | |
127 | * - https://web.archive.org/web/20220406232708/http://www.kolumbus.fi/pami1/video/pal_ntsc.html | |
128 | * - https://web.archive.org/web/20220406124914/http://martin.hinner.info/vga/pal.html | |
129 | * - https://web.archive.org/web/20220609202433/http://www.batsocks.co.uk/readme/video_timing.htm | |
130 | */ | |
131 | #define NTSC_LINE_DURATION_NS 63556U | |
132 | #define NTSC_LINES_NUMBER 525 | |
133 | ||
134 | #define NTSC_HBLK_DURATION_TYP_NS 10900U | |
135 | #define NTSC_HBLK_DURATION_MIN_NS (NTSC_HBLK_DURATION_TYP_NS - 200) | |
136 | #define NTSC_HBLK_DURATION_MAX_NS (NTSC_HBLK_DURATION_TYP_NS + 200) | |
137 | ||
138 | #define NTSC_HACT_DURATION_TYP_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_TYP_NS) | |
139 | #define NTSC_HACT_DURATION_MIN_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MAX_NS) | |
140 | #define NTSC_HACT_DURATION_MAX_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MIN_NS) | |
141 | ||
142 | #define NTSC_HFP_DURATION_TYP_NS 1500 | |
143 | #define NTSC_HFP_DURATION_MIN_NS 1270 | |
144 | #define NTSC_HFP_DURATION_MAX_NS 2220 | |
145 | ||
146 | #define NTSC_HSLEN_DURATION_TYP_NS 4700 | |
147 | #define NTSC_HSLEN_DURATION_MIN_NS (NTSC_HSLEN_DURATION_TYP_NS - 100) | |
148 | #define NTSC_HSLEN_DURATION_MAX_NS (NTSC_HSLEN_DURATION_TYP_NS + 100) | |
149 | ||
150 | #define NTSC_HBP_DURATION_TYP_NS 4700 | |
151 | ||
152 | /* | |
153 | * I couldn't find the actual tolerance for the back porch, so let's | |
154 | * just reuse the sync length ones. | |
155 | */ | |
156 | #define NTSC_HBP_DURATION_MIN_NS (NTSC_HBP_DURATION_TYP_NS - 100) | |
157 | #define NTSC_HBP_DURATION_MAX_NS (NTSC_HBP_DURATION_TYP_NS + 100) | |
158 | ||
159 | #define PAL_LINE_DURATION_NS 64000U | |
160 | #define PAL_LINES_NUMBER 625 | |
161 | ||
162 | #define PAL_HACT_DURATION_TYP_NS 51950U | |
163 | #define PAL_HACT_DURATION_MIN_NS (PAL_HACT_DURATION_TYP_NS - 100) | |
164 | #define PAL_HACT_DURATION_MAX_NS (PAL_HACT_DURATION_TYP_NS + 400) | |
165 | ||
166 | #define PAL_HBLK_DURATION_TYP_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_TYP_NS) | |
167 | #define PAL_HBLK_DURATION_MIN_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MAX_NS) | |
168 | #define PAL_HBLK_DURATION_MAX_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MIN_NS) | |
169 | ||
170 | #define PAL_HFP_DURATION_TYP_NS 1650 | |
171 | #define PAL_HFP_DURATION_MIN_NS (PAL_HFP_DURATION_TYP_NS - 100) | |
172 | #define PAL_HFP_DURATION_MAX_NS (PAL_HFP_DURATION_TYP_NS + 400) | |
173 | ||
174 | #define PAL_HSLEN_DURATION_TYP_NS 4700 | |
175 | #define PAL_HSLEN_DURATION_MIN_NS (PAL_HSLEN_DURATION_TYP_NS - 200) | |
176 | #define PAL_HSLEN_DURATION_MAX_NS (PAL_HSLEN_DURATION_TYP_NS + 200) | |
177 | ||
178 | #define PAL_HBP_DURATION_TYP_NS 5700 | |
179 | #define PAL_HBP_DURATION_MIN_NS (PAL_HBP_DURATION_TYP_NS - 200) | |
180 | #define PAL_HBP_DURATION_MAX_NS (PAL_HBP_DURATION_TYP_NS + 200) | |
181 | ||
182 | struct analog_param_field { | |
183 | unsigned int even, odd; | |
184 | }; | |
185 | ||
186 | #define PARAM_FIELD(_odd, _even) \ | |
187 | { .even = _even, .odd = _odd } | |
188 | ||
189 | struct analog_param_range { | |
190 | unsigned int min, typ, max; | |
191 | }; | |
192 | ||
193 | #define PARAM_RANGE(_min, _typ, _max) \ | |
194 | { .min = _min, .typ = _typ, .max = _max } | |
195 | ||
196 | struct analog_parameters { | |
197 | unsigned int num_lines; | |
198 | unsigned int line_duration_ns; | |
199 | ||
200 | struct analog_param_range hact_ns; | |
201 | struct analog_param_range hfp_ns; | |
202 | struct analog_param_range hslen_ns; | |
203 | struct analog_param_range hbp_ns; | |
204 | struct analog_param_range hblk_ns; | |
205 | ||
206 | unsigned int bt601_hfp; | |
207 | ||
208 | struct analog_param_field vfp_lines; | |
209 | struct analog_param_field vslen_lines; | |
210 | struct analog_param_field vbp_lines; | |
211 | }; | |
212 | ||
213 | #define TV_MODE_PARAMETER(_mode, _lines, _line_dur, _hact, _hfp, \ | |
214 | _hslen, _hbp, _hblk, _bt601_hfp, _vfp, \ | |
215 | _vslen, _vbp) \ | |
216 | [_mode] = { \ | |
217 | .num_lines = _lines, \ | |
218 | .line_duration_ns = _line_dur, \ | |
219 | .hact_ns = _hact, \ | |
220 | .hfp_ns = _hfp, \ | |
221 | .hslen_ns = _hslen, \ | |
222 | .hbp_ns = _hbp, \ | |
223 | .hblk_ns = _hblk, \ | |
224 | .bt601_hfp = _bt601_hfp, \ | |
225 | .vfp_lines = _vfp, \ | |
226 | .vslen_lines = _vslen, \ | |
227 | .vbp_lines = _vbp, \ | |
228 | } | |
229 | ||
230 | static const struct analog_parameters tv_modes_parameters[] = { | |
231 | TV_MODE_PARAMETER(DRM_MODE_ANALOG_NTSC, | |
232 | NTSC_LINES_NUMBER, | |
233 | NTSC_LINE_DURATION_NS, | |
234 | PARAM_RANGE(NTSC_HACT_DURATION_MIN_NS, | |
235 | NTSC_HACT_DURATION_TYP_NS, | |
236 | NTSC_HACT_DURATION_MAX_NS), | |
237 | PARAM_RANGE(NTSC_HFP_DURATION_MIN_NS, | |
238 | NTSC_HFP_DURATION_TYP_NS, | |
239 | NTSC_HFP_DURATION_MAX_NS), | |
240 | PARAM_RANGE(NTSC_HSLEN_DURATION_MIN_NS, | |
241 | NTSC_HSLEN_DURATION_TYP_NS, | |
242 | NTSC_HSLEN_DURATION_MAX_NS), | |
243 | PARAM_RANGE(NTSC_HBP_DURATION_MIN_NS, | |
244 | NTSC_HBP_DURATION_TYP_NS, | |
245 | NTSC_HBP_DURATION_MAX_NS), | |
246 | PARAM_RANGE(NTSC_HBLK_DURATION_MIN_NS, | |
247 | NTSC_HBLK_DURATION_TYP_NS, | |
248 | NTSC_HBLK_DURATION_MAX_NS), | |
249 | 16, | |
250 | PARAM_FIELD(3, 3), | |
251 | PARAM_FIELD(3, 3), | |
252 | PARAM_FIELD(16, 17)), | |
253 | TV_MODE_PARAMETER(DRM_MODE_ANALOG_PAL, | |
254 | PAL_LINES_NUMBER, | |
255 | PAL_LINE_DURATION_NS, | |
256 | PARAM_RANGE(PAL_HACT_DURATION_MIN_NS, | |
257 | PAL_HACT_DURATION_TYP_NS, | |
258 | PAL_HACT_DURATION_MAX_NS), | |
259 | PARAM_RANGE(PAL_HFP_DURATION_MIN_NS, | |
260 | PAL_HFP_DURATION_TYP_NS, | |
261 | PAL_HFP_DURATION_MAX_NS), | |
262 | PARAM_RANGE(PAL_HSLEN_DURATION_MIN_NS, | |
263 | PAL_HSLEN_DURATION_TYP_NS, | |
264 | PAL_HSLEN_DURATION_MAX_NS), | |
265 | PARAM_RANGE(PAL_HBP_DURATION_MIN_NS, | |
266 | PAL_HBP_DURATION_TYP_NS, | |
267 | PAL_HBP_DURATION_MAX_NS), | |
268 | PARAM_RANGE(PAL_HBLK_DURATION_MIN_NS, | |
269 | PAL_HBLK_DURATION_TYP_NS, | |
270 | PAL_HBLK_DURATION_MAX_NS), | |
271 | 12, | |
272 | ||
273 | /* | |
274 | * The front porch is actually 6 short sync | |
275 | * pulses for the even field, and 5 for the | |
276 | * odd field. Each sync takes half a life so | |
277 | * the odd field front porch is shorter by | |
278 | * half a line. | |
279 | * | |
280 | * In progressive, we're supposed to use 6 | |
281 | * pulses, so we're fine there | |
282 | */ | |
283 | PARAM_FIELD(3, 2), | |
284 | ||
285 | /* | |
286 | * The vsync length is 5 long sync pulses, | |
287 | * each field taking half a line. We're | |
288 | * shorter for both fields by half a line. | |
289 | * | |
290 | * In progressive, we're supposed to use 5 | |
291 | * pulses, so we're off by half | |
292 | * a line. | |
293 | * | |
294 | * In interlace, we're now off by half a line | |
295 | * for the even field and one line for the odd | |
296 | * field. | |
297 | */ | |
298 | PARAM_FIELD(3, 3), | |
299 | ||
300 | /* | |
301 | * The back porch starts with post-equalizing | |
302 | * pulses, consisting in 5 short sync pulses | |
303 | * for the even field, 4 for the odd field. In | |
304 | * progressive, it's 5 short syncs. | |
305 | * | |
306 | * In progressive, we thus have 2.5 lines, | |
307 | * plus the 0.5 line we were missing | |
308 | * previously, so we should use 3 lines. | |
309 | * | |
310 | * In interlace, the even field is in the | |
311 | * exact same case than progressive. For the | |
312 | * odd field, we should be using 2 lines but | |
313 | * we're one line short, so we'll make up for | |
314 | * it here by using 3. | |
315 | * | |
316 | * The entire blanking area is supposed to | |
317 | * take 25 lines, so we also need to account | |
318 | * for the rest of the blanking area that | |
319 | * can't be in either the front porch or sync | |
320 | * period. | |
321 | */ | |
322 | PARAM_FIELD(19, 20)), | |
323 | }; | |
324 | ||
325 | static int fill_analog_mode(struct drm_device *dev, | |
326 | struct drm_display_mode *mode, | |
327 | const struct analog_parameters *params, | |
328 | unsigned long pixel_clock_hz, | |
329 | unsigned int hactive, | |
330 | unsigned int vactive, | |
331 | bool interlace) | |
332 | { | |
333 | unsigned long pixel_duration_ns = NSEC_PER_SEC / pixel_clock_hz; | |
334 | unsigned int htotal, vtotal; | |
335 | unsigned int max_hact, hact_duration_ns; | |
336 | unsigned int hblk, hblk_duration_ns; | |
337 | unsigned int hfp, hfp_duration_ns; | |
338 | unsigned int hslen, hslen_duration_ns; | |
339 | unsigned int hbp, hbp_duration_ns; | |
340 | unsigned int porches, porches_duration_ns; | |
341 | unsigned int vfp, vfp_min; | |
342 | unsigned int vbp, vbp_min; | |
343 | unsigned int vslen; | |
344 | bool bt601 = false; | |
345 | int porches_rem; | |
346 | u64 result; | |
347 | ||
348 | drm_dbg_kms(dev, | |
349 | "Generating a %ux%u%c, %u-line mode with a %lu kHz clock\n", | |
350 | hactive, vactive, | |
351 | interlace ? 'i' : 'p', | |
352 | params->num_lines, | |
353 | pixel_clock_hz / 1000); | |
354 | ||
355 | max_hact = params->hact_ns.max / pixel_duration_ns; | |
356 | if (pixel_clock_hz == 13500000 && hactive > max_hact && hactive <= 720) { | |
357 | drm_dbg_kms(dev, "Trying to generate a BT.601 mode. Disabling checks.\n"); | |
358 | bt601 = true; | |
359 | } | |
360 | ||
361 | /* | |
362 | * Our pixel duration is going to be round down by the division, | |
363 | * so rounding up is probably going to introduce even more | |
364 | * deviation. | |
365 | */ | |
366 | result = (u64)params->line_duration_ns * pixel_clock_hz; | |
367 | do_div(result, NSEC_PER_SEC); | |
368 | htotal = result; | |
369 | ||
370 | drm_dbg_kms(dev, "Total Horizontal Number of Pixels: %u\n", htotal); | |
371 | ||
372 | hact_duration_ns = hactive * pixel_duration_ns; | |
373 | if (!bt601 && | |
374 | (hact_duration_ns < params->hact_ns.min || | |
375 | hact_duration_ns > params->hact_ns.max)) { | |
376 | DRM_ERROR("Invalid horizontal active area duration: %uns (min: %u, max %u)\n", | |
377 | hact_duration_ns, params->hact_ns.min, params->hact_ns.max); | |
378 | return -EINVAL; | |
379 | } | |
380 | ||
381 | hblk = htotal - hactive; | |
382 | drm_dbg_kms(dev, "Horizontal Blanking Period: %u\n", hblk); | |
383 | ||
384 | hblk_duration_ns = hblk * pixel_duration_ns; | |
385 | if (!bt601 && | |
386 | (hblk_duration_ns < params->hblk_ns.min || | |
387 | hblk_duration_ns > params->hblk_ns.max)) { | |
388 | DRM_ERROR("Invalid horizontal blanking duration: %uns (min: %u, max %u)\n", | |
389 | hblk_duration_ns, params->hblk_ns.min, params->hblk_ns.max); | |
390 | return -EINVAL; | |
391 | } | |
392 | ||
393 | hslen = DIV_ROUND_UP(params->hslen_ns.typ, pixel_duration_ns); | |
394 | drm_dbg_kms(dev, "Horizontal Sync Period: %u\n", hslen); | |
395 | ||
396 | hslen_duration_ns = hslen * pixel_duration_ns; | |
397 | if (!bt601 && | |
398 | (hslen_duration_ns < params->hslen_ns.min || | |
399 | hslen_duration_ns > params->hslen_ns.max)) { | |
400 | DRM_ERROR("Invalid horizontal sync duration: %uns (min: %u, max %u)\n", | |
401 | hslen_duration_ns, params->hslen_ns.min, params->hslen_ns.max); | |
402 | return -EINVAL; | |
403 | } | |
404 | ||
405 | porches = hblk - hslen; | |
406 | drm_dbg_kms(dev, "Remaining horizontal pixels for both porches: %u\n", porches); | |
407 | ||
408 | porches_duration_ns = porches * pixel_duration_ns; | |
409 | if (!bt601 && | |
410 | (porches_duration_ns > (params->hfp_ns.max + params->hbp_ns.max) || | |
411 | porches_duration_ns < (params->hfp_ns.min + params->hbp_ns.min))) { | |
412 | DRM_ERROR("Invalid horizontal porches duration: %uns\n", porches_duration_ns); | |
413 | return -EINVAL; | |
414 | } | |
415 | ||
416 | if (bt601) { | |
417 | hfp = params->bt601_hfp; | |
418 | } else { | |
419 | unsigned int hfp_min = DIV_ROUND_UP(params->hfp_ns.min, | |
420 | pixel_duration_ns); | |
421 | unsigned int hbp_min = DIV_ROUND_UP(params->hbp_ns.min, | |
422 | pixel_duration_ns); | |
423 | int porches_rem = porches - hfp_min - hbp_min; | |
424 | ||
425 | hfp = hfp_min + DIV_ROUND_UP(porches_rem, 2); | |
426 | } | |
427 | ||
428 | drm_dbg_kms(dev, "Horizontal Front Porch: %u\n", hfp); | |
429 | ||
430 | hfp_duration_ns = hfp * pixel_duration_ns; | |
431 | if (!bt601 && | |
432 | (hfp_duration_ns < params->hfp_ns.min || | |
433 | hfp_duration_ns > params->hfp_ns.max)) { | |
434 | DRM_ERROR("Invalid horizontal front porch duration: %uns (min: %u, max %u)\n", | |
435 | hfp_duration_ns, params->hfp_ns.min, params->hfp_ns.max); | |
436 | return -EINVAL; | |
437 | } | |
438 | ||
439 | hbp = porches - hfp; | |
440 | drm_dbg_kms(dev, "Horizontal Back Porch: %u\n", hbp); | |
441 | ||
442 | hbp_duration_ns = hbp * pixel_duration_ns; | |
443 | if (!bt601 && | |
444 | (hbp_duration_ns < params->hbp_ns.min || | |
445 | hbp_duration_ns > params->hbp_ns.max)) { | |
446 | DRM_ERROR("Invalid horizontal back porch duration: %uns (min: %u, max %u)\n", | |
447 | hbp_duration_ns, params->hbp_ns.min, params->hbp_ns.max); | |
448 | return -EINVAL; | |
449 | } | |
450 | ||
451 | if (htotal != (hactive + hfp + hslen + hbp)) | |
452 | return -EINVAL; | |
453 | ||
454 | mode->clock = pixel_clock_hz / 1000; | |
455 | mode->hdisplay = hactive; | |
456 | mode->hsync_start = mode->hdisplay + hfp; | |
457 | mode->hsync_end = mode->hsync_start + hslen; | |
458 | mode->htotal = mode->hsync_end + hbp; | |
459 | ||
460 | if (interlace) { | |
461 | vfp_min = params->vfp_lines.even + params->vfp_lines.odd; | |
462 | vbp_min = params->vbp_lines.even + params->vbp_lines.odd; | |
463 | vslen = params->vslen_lines.even + params->vslen_lines.odd; | |
464 | } else { | |
465 | /* | |
466 | * By convention, NTSC (aka 525/60) systems start with | |
467 | * the even field, but PAL (aka 625/50) systems start | |
468 | * with the odd one. | |
469 | * | |
470 | * PAL systems also have asymmetric timings between the | |
471 | * even and odd field, while NTSC is symmetric. | |
472 | * | |
473 | * Moreover, if we want to create a progressive mode for | |
474 | * PAL, we need to use the odd field timings. | |
475 | * | |
476 | * Since odd == even for NTSC, we can just use the odd | |
477 | * one all the time to simplify the code a bit. | |
478 | */ | |
479 | vfp_min = params->vfp_lines.odd; | |
480 | vbp_min = params->vbp_lines.odd; | |
481 | vslen = params->vslen_lines.odd; | |
482 | } | |
483 | ||
484 | drm_dbg_kms(dev, "Vertical Sync Period: %u\n", vslen); | |
485 | ||
486 | porches = params->num_lines - vactive - vslen; | |
487 | drm_dbg_kms(dev, "Remaining vertical pixels for both porches: %u\n", porches); | |
488 | ||
489 | porches_rem = porches - vfp_min - vbp_min; | |
490 | vfp = vfp_min + (porches_rem / 2); | |
491 | drm_dbg_kms(dev, "Vertical Front Porch: %u\n", vfp); | |
492 | ||
493 | vbp = porches - vfp; | |
494 | drm_dbg_kms(dev, "Vertical Back Porch: %u\n", vbp); | |
495 | ||
496 | vtotal = vactive + vfp + vslen + vbp; | |
497 | if (params->num_lines != vtotal) { | |
498 | DRM_ERROR("Invalid vertical total: %upx (expected %upx)\n", | |
499 | vtotal, params->num_lines); | |
500 | return -EINVAL; | |
501 | } | |
502 | ||
503 | mode->vdisplay = vactive; | |
504 | mode->vsync_start = mode->vdisplay + vfp; | |
505 | mode->vsync_end = mode->vsync_start + vslen; | |
506 | mode->vtotal = mode->vsync_end + vbp; | |
507 | ||
508 | if (mode->vtotal != params->num_lines) | |
509 | return -EINVAL; | |
510 | ||
511 | mode->type = DRM_MODE_TYPE_DRIVER; | |
512 | mode->flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC; | |
513 | if (interlace) | |
514 | mode->flags |= DRM_MODE_FLAG_INTERLACE; | |
515 | ||
516 | drm_mode_set_name(mode); | |
517 | ||
518 | drm_dbg_kms(dev, "Generated mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); | |
519 | ||
520 | return 0; | |
521 | } | |
522 | ||
523 | /** | |
524 | * drm_analog_tv_mode - create a display mode for an analog TV | |
525 | * @dev: drm device | |
526 | * @tv_mode: TV Mode standard to create a mode for. See DRM_MODE_TV_MODE_*. | |
527 | * @pixel_clock_hz: Pixel Clock Frequency, in Hertz | |
528 | * @hdisplay: hdisplay size | |
529 | * @vdisplay: vdisplay size | |
530 | * @interlace: whether to compute an interlaced mode | |
531 | * | |
532 | * This function creates a struct drm_display_mode instance suited for | |
533 | * an analog TV output, for one of the usual analog TV mode. | |
534 | * | |
535 | * Note that @hdisplay is larger than the usual constraints for the PAL | |
536 | * and NTSC timings, and we'll choose to ignore most timings constraints | |
537 | * to reach those resolutions. | |
538 | * | |
539 | * Returns: | |
540 | * | |
541 | * A pointer to the mode, allocated with drm_mode_create(). Returns NULL | |
542 | * on error. | |
543 | */ | |
544 | struct drm_display_mode *drm_analog_tv_mode(struct drm_device *dev, | |
545 | enum drm_connector_tv_mode tv_mode, | |
546 | unsigned long pixel_clock_hz, | |
547 | unsigned int hdisplay, | |
548 | unsigned int vdisplay, | |
549 | bool interlace) | |
550 | { | |
551 | struct drm_display_mode *mode; | |
552 | enum drm_mode_analog analog; | |
553 | int ret; | |
554 | ||
555 | switch (tv_mode) { | |
556 | case DRM_MODE_TV_MODE_NTSC: | |
557 | fallthrough; | |
558 | case DRM_MODE_TV_MODE_NTSC_443: | |
559 | fallthrough; | |
560 | case DRM_MODE_TV_MODE_NTSC_J: | |
561 | fallthrough; | |
562 | case DRM_MODE_TV_MODE_PAL_M: | |
563 | analog = DRM_MODE_ANALOG_NTSC; | |
564 | break; | |
565 | ||
566 | case DRM_MODE_TV_MODE_PAL: | |
567 | fallthrough; | |
568 | case DRM_MODE_TV_MODE_PAL_N: | |
569 | fallthrough; | |
570 | case DRM_MODE_TV_MODE_SECAM: | |
571 | analog = DRM_MODE_ANALOG_PAL; | |
572 | break; | |
573 | ||
574 | default: | |
575 | return NULL; | |
576 | } | |
577 | ||
578 | mode = drm_mode_create(dev); | |
579 | if (!mode) | |
580 | return NULL; | |
581 | ||
582 | ret = fill_analog_mode(dev, mode, | |
583 | &tv_modes_parameters[analog], | |
584 | pixel_clock_hz, hdisplay, vdisplay, interlace); | |
585 | if (ret) | |
586 | goto err_free_mode; | |
587 | ||
588 | return mode; | |
589 | ||
590 | err_free_mode: | |
591 | drm_mode_destroy(dev, mode); | |
592 | return NULL; | |
593 | } | |
594 | EXPORT_SYMBOL(drm_analog_tv_mode); | |
595 | ||
d782c3f9 | 596 | /** |
f5aabb97 SV |
597 | * drm_cvt_mode -create a modeline based on the CVT algorithm |
598 | * @dev: drm device | |
d782c3f9 ZY |
599 | * @hdisplay: hdisplay size |
600 | * @vdisplay: vdisplay size | |
f5aabb97 SV |
601 | * @vrefresh: vrefresh rate |
602 | * @reduced: whether to use reduced blanking | |
603 | * @interlaced: whether to compute an interlaced mode | |
604 | * @margins: whether to add margins (borders) | |
d782c3f9 ZY |
605 | * |
606 | * This function is called to generate the modeline based on CVT algorithm | |
607 | * according to the hdisplay, vdisplay, vrefresh. | |
608 | * It is based from the VESA(TM) Coordinated Video Timing Generator by | |
609 | * Graham Loveridge April 9, 2003 available at | |
5558d6c2 | 610 | * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls |
d782c3f9 ZY |
611 | * |
612 | * And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c. | |
613 | * What I have done is to translate it by using integer calculation. | |
f5aabb97 SV |
614 | * |
615 | * Returns: | |
616 | * The modeline based on the CVT algorithm stored in a drm_display_mode object. | |
617 | * The display mode object is allocated with drm_mode_create(). Returns NULL | |
618 | * when no mode could be allocated. | |
d782c3f9 | 619 | */ |
d782c3f9 ZY |
620 | struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, |
621 | int vdisplay, int vrefresh, | |
d50ba256 | 622 | bool reduced, bool interlaced, bool margins) |
d782c3f9 | 623 | { |
3ec0db81 | 624 | #define HV_FACTOR 1000 |
d782c3f9 ZY |
625 | /* 1) top/bottom margin size (% of height) - default: 1.8, */ |
626 | #define CVT_MARGIN_PERCENTAGE 18 | |
627 | /* 2) character cell horizontal granularity (pixels) - default 8 */ | |
628 | #define CVT_H_GRANULARITY 8 | |
629 | /* 3) Minimum vertical porch (lines) - default 3 */ | |
630 | #define CVT_MIN_V_PORCH 3 | |
631 | /* 4) Minimum number of vertical back porch lines - default 6 */ | |
632 | #define CVT_MIN_V_BPORCH 6 | |
633 | /* Pixel Clock step (kHz) */ | |
634 | #define CVT_CLOCK_STEP 250 | |
635 | struct drm_display_mode *drm_mode; | |
d782c3f9 ZY |
636 | unsigned int vfieldrate, hperiod; |
637 | int hdisplay_rnd, hmargin, vdisplay_rnd, vmargin, vsync; | |
638 | int interlace; | |
8a5bbf32 | 639 | u64 tmp; |
d782c3f9 | 640 | |
7f3bbc0b DO |
641 | if (!hdisplay || !vdisplay) |
642 | return NULL; | |
643 | ||
d782c3f9 ZY |
644 | /* allocate the drm_display_mode structure. If failure, we will |
645 | * return directly | |
646 | */ | |
647 | drm_mode = drm_mode_create(dev); | |
648 | if (!drm_mode) | |
649 | return NULL; | |
650 | ||
651 | /* the CVT default refresh rate is 60Hz */ | |
652 | if (!vrefresh) | |
653 | vrefresh = 60; | |
654 | ||
655 | /* the required field fresh rate */ | |
656 | if (interlaced) | |
657 | vfieldrate = vrefresh * 2; | |
658 | else | |
659 | vfieldrate = vrefresh; | |
660 | ||
661 | /* horizontal pixels */ | |
662 | hdisplay_rnd = hdisplay - (hdisplay % CVT_H_GRANULARITY); | |
663 | ||
664 | /* determine the left&right borders */ | |
665 | hmargin = 0; | |
666 | if (margins) { | |
667 | hmargin = hdisplay_rnd * CVT_MARGIN_PERCENTAGE / 1000; | |
668 | hmargin -= hmargin % CVT_H_GRANULARITY; | |
669 | } | |
670 | /* find the total active pixels */ | |
671 | drm_mode->hdisplay = hdisplay_rnd + 2 * hmargin; | |
672 | ||
673 | /* find the number of lines per field */ | |
674 | if (interlaced) | |
675 | vdisplay_rnd = vdisplay / 2; | |
676 | else | |
677 | vdisplay_rnd = vdisplay; | |
678 | ||
679 | /* find the top & bottom borders */ | |
680 | vmargin = 0; | |
681 | if (margins) | |
682 | vmargin = vdisplay_rnd * CVT_MARGIN_PERCENTAGE / 1000; | |
683 | ||
841b4117 | 684 | drm_mode->vdisplay = vdisplay + 2 * vmargin; |
d782c3f9 ZY |
685 | |
686 | /* Interlaced */ | |
687 | if (interlaced) | |
688 | interlace = 1; | |
689 | else | |
690 | interlace = 0; | |
691 | ||
692 | /* Determine VSync Width from aspect ratio */ | |
693 | if (!(vdisplay % 3) && ((vdisplay * 4 / 3) == hdisplay)) | |
694 | vsync = 4; | |
695 | else if (!(vdisplay % 9) && ((vdisplay * 16 / 9) == hdisplay)) | |
696 | vsync = 5; | |
697 | else if (!(vdisplay % 10) && ((vdisplay * 16 / 10) == hdisplay)) | |
698 | vsync = 6; | |
699 | else if (!(vdisplay % 4) && ((vdisplay * 5 / 4) == hdisplay)) | |
700 | vsync = 7; | |
701 | else if (!(vdisplay % 9) && ((vdisplay * 15 / 9) == hdisplay)) | |
702 | vsync = 7; | |
703 | else /* custom */ | |
704 | vsync = 10; | |
705 | ||
706 | if (!reduced) { | |
707 | /* simplify the GTF calculation */ | |
708 | /* 4) Minimum time of vertical sync + back porch interval (µs) | |
709 | * default 550.0 | |
710 | */ | |
711 | int tmp1, tmp2; | |
712 | #define CVT_MIN_VSYNC_BP 550 | |
713 | /* 3) Nominal HSync width (% of line period) - default 8 */ | |
714 | #define CVT_HSYNC_PERCENTAGE 8 | |
715 | unsigned int hblank_percentage; | |
f199af00 | 716 | int vsyncandback_porch, __maybe_unused vback_porch, hblank; |
d782c3f9 ZY |
717 | |
718 | /* estimated the horizontal period */ | |
719 | tmp1 = HV_FACTOR * 1000000 - | |
720 | CVT_MIN_VSYNC_BP * HV_FACTOR * vfieldrate; | |
721 | tmp2 = (vdisplay_rnd + 2 * vmargin + CVT_MIN_V_PORCH) * 2 + | |
722 | interlace; | |
723 | hperiod = tmp1 * 2 / (tmp2 * vfieldrate); | |
724 | ||
725 | tmp1 = CVT_MIN_VSYNC_BP * HV_FACTOR / hperiod + 1; | |
726 | /* 9. Find number of lines in sync + backporch */ | |
727 | if (tmp1 < (vsync + CVT_MIN_V_PORCH)) | |
728 | vsyncandback_porch = vsync + CVT_MIN_V_PORCH; | |
729 | else | |
730 | vsyncandback_porch = tmp1; | |
731 | /* 10. Find number of lines in back porch */ | |
732 | vback_porch = vsyncandback_porch - vsync; | |
733 | drm_mode->vtotal = vdisplay_rnd + 2 * vmargin + | |
734 | vsyncandback_porch + CVT_MIN_V_PORCH; | |
735 | /* 5) Definition of Horizontal blanking time limitation */ | |
736 | /* Gradient (%/kHz) - default 600 */ | |
737 | #define CVT_M_FACTOR 600 | |
738 | /* Offset (%) - default 40 */ | |
739 | #define CVT_C_FACTOR 40 | |
740 | /* Blanking time scaling factor - default 128 */ | |
741 | #define CVT_K_FACTOR 128 | |
742 | /* Scaling factor weighting - default 20 */ | |
743 | #define CVT_J_FACTOR 20 | |
744 | #define CVT_M_PRIME (CVT_M_FACTOR * CVT_K_FACTOR / 256) | |
745 | #define CVT_C_PRIME ((CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \ | |
746 | CVT_J_FACTOR) | |
747 | /* 12. Find ideal blanking duty cycle from formula */ | |
748 | hblank_percentage = CVT_C_PRIME * HV_FACTOR - CVT_M_PRIME * | |
749 | hperiod / 1000; | |
750 | /* 13. Blanking time */ | |
751 | if (hblank_percentage < 20 * HV_FACTOR) | |
752 | hblank_percentage = 20 * HV_FACTOR; | |
753 | hblank = drm_mode->hdisplay * hblank_percentage / | |
754 | (100 * HV_FACTOR - hblank_percentage); | |
755 | hblank -= hblank % (2 * CVT_H_GRANULARITY); | |
2a97acd6 | 756 | /* 14. find the total pixels per line */ |
d782c3f9 ZY |
757 | drm_mode->htotal = drm_mode->hdisplay + hblank; |
758 | drm_mode->hsync_end = drm_mode->hdisplay + hblank / 2; | |
759 | drm_mode->hsync_start = drm_mode->hsync_end - | |
760 | (drm_mode->htotal * CVT_HSYNC_PERCENTAGE) / 100; | |
761 | drm_mode->hsync_start += CVT_H_GRANULARITY - | |
762 | drm_mode->hsync_start % CVT_H_GRANULARITY; | |
763 | /* fill the Vsync values */ | |
764 | drm_mode->vsync_start = drm_mode->vdisplay + CVT_MIN_V_PORCH; | |
765 | drm_mode->vsync_end = drm_mode->vsync_start + vsync; | |
766 | } else { | |
767 | /* Reduced blanking */ | |
768 | /* Minimum vertical blanking interval time (µs)- default 460 */ | |
769 | #define CVT_RB_MIN_VBLANK 460 | |
770 | /* Fixed number of clocks for horizontal sync */ | |
771 | #define CVT_RB_H_SYNC 32 | |
772 | /* Fixed number of clocks for horizontal blanking */ | |
773 | #define CVT_RB_H_BLANK 160 | |
774 | /* Fixed number of lines for vertical front porch - default 3*/ | |
775 | #define CVT_RB_VFPORCH 3 | |
776 | int vbilines; | |
777 | int tmp1, tmp2; | |
778 | /* 8. Estimate Horizontal period. */ | |
779 | tmp1 = HV_FACTOR * 1000000 - | |
780 | CVT_RB_MIN_VBLANK * HV_FACTOR * vfieldrate; | |
781 | tmp2 = vdisplay_rnd + 2 * vmargin; | |
782 | hperiod = tmp1 / (tmp2 * vfieldrate); | |
783 | /* 9. Find number of lines in vertical blanking */ | |
784 | vbilines = CVT_RB_MIN_VBLANK * HV_FACTOR / hperiod + 1; | |
785 | /* 10. Check if vertical blanking is sufficient */ | |
786 | if (vbilines < (CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH)) | |
787 | vbilines = CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH; | |
788 | /* 11. Find total number of lines in vertical field */ | |
789 | drm_mode->vtotal = vdisplay_rnd + 2 * vmargin + vbilines; | |
790 | /* 12. Find total number of pixels in a line */ | |
791 | drm_mode->htotal = drm_mode->hdisplay + CVT_RB_H_BLANK; | |
792 | /* Fill in HSync values */ | |
793 | drm_mode->hsync_end = drm_mode->hdisplay + CVT_RB_H_BLANK / 2; | |
adde0f23 AJ |
794 | drm_mode->hsync_start = drm_mode->hsync_end - CVT_RB_H_SYNC; |
795 | /* Fill in VSync values */ | |
796 | drm_mode->vsync_start = drm_mode->vdisplay + CVT_RB_VFPORCH; | |
797 | drm_mode->vsync_end = drm_mode->vsync_start + vsync; | |
d782c3f9 ZY |
798 | } |
799 | /* 15/13. Find pixel clock frequency (kHz for xf86) */ | |
8a5bbf32 CW |
800 | tmp = drm_mode->htotal; /* perform intermediate calcs in u64 */ |
801 | tmp *= HV_FACTOR * 1000; | |
802 | do_div(tmp, hperiod); | |
803 | tmp -= drm_mode->clock % CVT_CLOCK_STEP; | |
804 | drm_mode->clock = tmp; | |
d782c3f9 ZY |
805 | /* 18/16. Find actual vertical frame frequency */ |
806 | /* ignore - just set the mode flag for interlaced */ | |
171fdd89 | 807 | if (interlaced) { |
d782c3f9 | 808 | drm_mode->vtotal *= 2; |
171fdd89 AJ |
809 | drm_mode->flags |= DRM_MODE_FLAG_INTERLACE; |
810 | } | |
d782c3f9 ZY |
811 | /* Fill the mode line name */ |
812 | drm_mode_set_name(drm_mode); | |
813 | if (reduced) | |
814 | drm_mode->flags |= (DRM_MODE_FLAG_PHSYNC | | |
815 | DRM_MODE_FLAG_NVSYNC); | |
816 | else | |
817 | drm_mode->flags |= (DRM_MODE_FLAG_PVSYNC | | |
818 | DRM_MODE_FLAG_NHSYNC); | |
d782c3f9 | 819 | |
171fdd89 | 820 | return drm_mode; |
d782c3f9 ZY |
821 | } |
822 | EXPORT_SYMBOL(drm_cvt_mode); | |
823 | ||
26bbdada | 824 | /** |
f5aabb97 SV |
825 | * drm_gtf_mode_complex - create the modeline based on the full GTF algorithm |
826 | * @dev: drm device | |
827 | * @hdisplay: hdisplay size | |
828 | * @vdisplay: vdisplay size | |
829 | * @vrefresh: vrefresh rate. | |
830 | * @interlaced: whether to compute an interlaced mode | |
831 | * @margins: desired margin (borders) size | |
3ec0db81 SV |
832 | * @GTF_M: extended GTF formula parameters |
833 | * @GTF_2C: extended GTF formula parameters | |
834 | * @GTF_K: extended GTF formula parameters | |
835 | * @GTF_2J: extended GTF formula parameters | |
26bbdada | 836 | * |
7a374350 AJ |
837 | * GTF feature blocks specify C and J in multiples of 0.5, so we pass them |
838 | * in here multiplied by two. For a C of 40, pass in 80. | |
f5aabb97 SV |
839 | * |
840 | * Returns: | |
841 | * The modeline based on the full GTF algorithm stored in a drm_display_mode object. | |
842 | * The display mode object is allocated with drm_mode_create(). Returns NULL | |
843 | * when no mode could be allocated. | |
26bbdada | 844 | */ |
7a374350 AJ |
845 | struct drm_display_mode * |
846 | drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay, | |
847 | int vrefresh, bool interlaced, int margins, | |
848 | int GTF_M, int GTF_2C, int GTF_K, int GTF_2J) | |
849 | { /* 1) top/bottom margin size (% of height) - default: 1.8, */ | |
26bbdada ZY |
850 | #define GTF_MARGIN_PERCENTAGE 18 |
851 | /* 2) character cell horizontal granularity (pixels) - default 8 */ | |
852 | #define GTF_CELL_GRAN 8 | |
853 | /* 3) Minimum vertical porch (lines) - default 3 */ | |
854 | #define GTF_MIN_V_PORCH 1 | |
855 | /* width of vsync in lines */ | |
856 | #define V_SYNC_RQD 3 | |
857 | /* width of hsync as % of total line */ | |
858 | #define H_SYNC_PERCENT 8 | |
859 | /* min time of vsync + back porch (microsec) */ | |
860 | #define MIN_VSYNC_PLUS_BP 550 | |
26bbdada | 861 | /* C' and M' are part of the Blanking Duty Cycle computation */ |
7a374350 AJ |
862 | #define GTF_C_PRIME ((((GTF_2C - GTF_2J) * GTF_K / 256) + GTF_2J) / 2) |
863 | #define GTF_M_PRIME (GTF_K * GTF_M / 256) | |
26bbdada ZY |
864 | struct drm_display_mode *drm_mode; |
865 | unsigned int hdisplay_rnd, vdisplay_rnd, vfieldrate_rqd; | |
866 | int top_margin, bottom_margin; | |
867 | int interlace; | |
868 | unsigned int hfreq_est; | |
f199af00 BG |
869 | int vsync_plus_bp, __maybe_unused vback_porch; |
870 | unsigned int vtotal_lines, __maybe_unused vfieldrate_est; | |
871 | unsigned int __maybe_unused hperiod; | |
872 | unsigned int vfield_rate, __maybe_unused vframe_rate; | |
26bbdada ZY |
873 | int left_margin, right_margin; |
874 | unsigned int total_active_pixels, ideal_duty_cycle; | |
875 | unsigned int hblank, total_pixels, pixel_freq; | |
876 | int hsync, hfront_porch, vodd_front_porch_lines; | |
877 | unsigned int tmp1, tmp2; | |
878 | ||
7f3bbc0b DO |
879 | if (!hdisplay || !vdisplay) |
880 | return NULL; | |
881 | ||
26bbdada ZY |
882 | drm_mode = drm_mode_create(dev); |
883 | if (!drm_mode) | |
884 | return NULL; | |
885 | ||
886 | /* 1. In order to give correct results, the number of horizontal | |
887 | * pixels requested is first processed to ensure that it is divisible | |
888 | * by the character size, by rounding it to the nearest character | |
889 | * cell boundary: | |
890 | */ | |
891 | hdisplay_rnd = (hdisplay + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN; | |
892 | hdisplay_rnd = hdisplay_rnd * GTF_CELL_GRAN; | |
893 | ||
894 | /* 2. If interlace is requested, the number of vertical lines assumed | |
895 | * by the calculation must be halved, as the computation calculates | |
896 | * the number of vertical lines per field. | |
897 | */ | |
898 | if (interlaced) | |
899 | vdisplay_rnd = vdisplay / 2; | |
900 | else | |
901 | vdisplay_rnd = vdisplay; | |
902 | ||
903 | /* 3. Find the frame rate required: */ | |
904 | if (interlaced) | |
905 | vfieldrate_rqd = vrefresh * 2; | |
906 | else | |
907 | vfieldrate_rqd = vrefresh; | |
908 | ||
909 | /* 4. Find number of lines in Top margin: */ | |
910 | top_margin = 0; | |
911 | if (margins) | |
912 | top_margin = (vdisplay_rnd * GTF_MARGIN_PERCENTAGE + 500) / | |
913 | 1000; | |
914 | /* 5. Find number of lines in bottom margin: */ | |
915 | bottom_margin = top_margin; | |
916 | ||
917 | /* 6. If interlace is required, then set variable interlace: */ | |
918 | if (interlaced) | |
919 | interlace = 1; | |
920 | else | |
921 | interlace = 0; | |
922 | ||
923 | /* 7. Estimate the Horizontal frequency */ | |
924 | { | |
925 | tmp1 = (1000000 - MIN_VSYNC_PLUS_BP * vfieldrate_rqd) / 500; | |
926 | tmp2 = (vdisplay_rnd + 2 * top_margin + GTF_MIN_V_PORCH) * | |
927 | 2 + interlace; | |
928 | hfreq_est = (tmp2 * 1000 * vfieldrate_rqd) / tmp1; | |
929 | } | |
930 | ||
931 | /* 8. Find the number of lines in V sync + back porch */ | |
932 | /* [V SYNC+BP] = RINT(([MIN VSYNC+BP] * hfreq_est / 1000000)) */ | |
933 | vsync_plus_bp = MIN_VSYNC_PLUS_BP * hfreq_est / 1000; | |
934 | vsync_plus_bp = (vsync_plus_bp + 500) / 1000; | |
935 | /* 9. Find the number of lines in V back porch alone: */ | |
936 | vback_porch = vsync_plus_bp - V_SYNC_RQD; | |
937 | /* 10. Find the total number of lines in Vertical field period: */ | |
938 | vtotal_lines = vdisplay_rnd + top_margin + bottom_margin + | |
939 | vsync_plus_bp + GTF_MIN_V_PORCH; | |
940 | /* 11. Estimate the Vertical field frequency: */ | |
941 | vfieldrate_est = hfreq_est / vtotal_lines; | |
942 | /* 12. Find the actual horizontal period: */ | |
943 | hperiod = 1000000 / (vfieldrate_rqd * vtotal_lines); | |
944 | ||
945 | /* 13. Find the actual Vertical field frequency: */ | |
946 | vfield_rate = hfreq_est / vtotal_lines; | |
947 | /* 14. Find the Vertical frame frequency: */ | |
948 | if (interlaced) | |
949 | vframe_rate = vfield_rate / 2; | |
950 | else | |
951 | vframe_rate = vfield_rate; | |
952 | /* 15. Find number of pixels in left margin: */ | |
953 | if (margins) | |
954 | left_margin = (hdisplay_rnd * GTF_MARGIN_PERCENTAGE + 500) / | |
955 | 1000; | |
956 | else | |
957 | left_margin = 0; | |
958 | ||
959 | /* 16.Find number of pixels in right margin: */ | |
960 | right_margin = left_margin; | |
961 | /* 17.Find total number of active pixels in image and left and right */ | |
962 | total_active_pixels = hdisplay_rnd + left_margin + right_margin; | |
963 | /* 18.Find the ideal blanking duty cycle from blanking duty cycle */ | |
964 | ideal_duty_cycle = GTF_C_PRIME * 1000 - | |
965 | (GTF_M_PRIME * 1000000 / hfreq_est); | |
966 | /* 19.Find the number of pixels in the blanking time to the nearest | |
967 | * double character cell: */ | |
968 | hblank = total_active_pixels * ideal_duty_cycle / | |
969 | (100000 - ideal_duty_cycle); | |
970 | hblank = (hblank + GTF_CELL_GRAN) / (2 * GTF_CELL_GRAN); | |
971 | hblank = hblank * 2 * GTF_CELL_GRAN; | |
972 | /* 20.Find total number of pixels: */ | |
973 | total_pixels = total_active_pixels + hblank; | |
974 | /* 21.Find pixel clock frequency: */ | |
975 | pixel_freq = total_pixels * hfreq_est / 1000; | |
976 | /* Stage 1 computations are now complete; I should really pass | |
977 | * the results to another function and do the Stage 2 computations, | |
978 | * but I only need a few more values so I'll just append the | |
979 | * computations here for now */ | |
980 | /* 17. Find the number of pixels in the horizontal sync period: */ | |
981 | hsync = H_SYNC_PERCENT * total_pixels / 100; | |
982 | hsync = (hsync + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN; | |
983 | hsync = hsync * GTF_CELL_GRAN; | |
984 | /* 18. Find the number of pixels in horizontal front porch period */ | |
985 | hfront_porch = hblank / 2 - hsync; | |
986 | /* 36. Find the number of lines in the odd front porch period: */ | |
987 | vodd_front_porch_lines = GTF_MIN_V_PORCH ; | |
988 | ||
989 | /* finally, pack the results in the mode struct */ | |
990 | drm_mode->hdisplay = hdisplay_rnd; | |
991 | drm_mode->hsync_start = hdisplay_rnd + hfront_porch; | |
992 | drm_mode->hsync_end = drm_mode->hsync_start + hsync; | |
993 | drm_mode->htotal = total_pixels; | |
994 | drm_mode->vdisplay = vdisplay_rnd; | |
995 | drm_mode->vsync_start = vdisplay_rnd + vodd_front_porch_lines; | |
996 | drm_mode->vsync_end = drm_mode->vsync_start + V_SYNC_RQD; | |
997 | drm_mode->vtotal = vtotal_lines; | |
998 | ||
999 | drm_mode->clock = pixel_freq; | |
1000 | ||
26bbdada ZY |
1001 | if (interlaced) { |
1002 | drm_mode->vtotal *= 2; | |
1003 | drm_mode->flags |= DRM_MODE_FLAG_INTERLACE; | |
1004 | } | |
1005 | ||
171fdd89 | 1006 | drm_mode_set_name(drm_mode); |
c385e50c AJ |
1007 | if (GTF_M == 600 && GTF_2C == 80 && GTF_K == 128 && GTF_2J == 40) |
1008 | drm_mode->flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC; | |
1009 | else | |
1010 | drm_mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC; | |
171fdd89 | 1011 | |
26bbdada ZY |
1012 | return drm_mode; |
1013 | } | |
7a374350 AJ |
1014 | EXPORT_SYMBOL(drm_gtf_mode_complex); |
1015 | ||
1016 | /** | |
f5aabb97 SV |
1017 | * drm_gtf_mode - create the modeline based on the GTF algorithm |
1018 | * @dev: drm device | |
1019 | * @hdisplay: hdisplay size | |
1020 | * @vdisplay: vdisplay size | |
1021 | * @vrefresh: vrefresh rate. | |
1022 | * @interlaced: whether to compute an interlaced mode | |
1023 | * @margins: desired margin (borders) size | |
7a374350 | 1024 | * |
7a374350 AJ |
1025 | * return the modeline based on GTF algorithm |
1026 | * | |
1027 | * This function is to create the modeline based on the GTF algorithm. | |
1028 | * Generalized Timing Formula is derived from: | |
2e7a5701 | 1029 | * |
7a374350 | 1030 | * GTF Spreadsheet by Andy Morrish (1/5/97) |
b0487e0d | 1031 | * available at https://www.vesa.org |
7a374350 AJ |
1032 | * |
1033 | * And it is copied from the file of xserver/hw/xfree86/modes/xf86gtf.c. | |
1034 | * What I have done is to translate it by using integer calculation. | |
1035 | * I also refer to the function of fb_get_mode in the file of | |
1036 | * drivers/video/fbmon.c | |
1037 | * | |
da5335b8 SV |
1038 | * Standard GTF parameters:: |
1039 | * | |
f03d8ede DCLP |
1040 | * M = 600 |
1041 | * C = 40 | |
1042 | * K = 128 | |
1043 | * J = 20 | |
f5aabb97 SV |
1044 | * |
1045 | * Returns: | |
1046 | * The modeline based on the GTF algorithm stored in a drm_display_mode object. | |
1047 | * The display mode object is allocated with drm_mode_create(). Returns NULL | |
1048 | * when no mode could be allocated. | |
7a374350 AJ |
1049 | */ |
1050 | struct drm_display_mode * | |
1051 | drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, | |
3ec0db81 | 1052 | bool interlaced, int margins) |
7a374350 | 1053 | { |
3ec0db81 SV |
1054 | return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh, |
1055 | interlaced, margins, | |
1056 | 600, 40 * 2, 128, 20 * 2); | |
7a374350 | 1057 | } |
26bbdada | 1058 | EXPORT_SYMBOL(drm_gtf_mode); |
7a374350 | 1059 | |
a38884f6 | 1060 | #ifdef CONFIG_VIDEOMODE_HELPERS |
f5aabb97 SV |
1061 | /** |
1062 | * drm_display_mode_from_videomode - fill in @dmode using @vm, | |
1063 | * @vm: videomode structure to use as source | |
1064 | * @dmode: drm_display_mode structure to use as destination | |
1065 | * | |
1066 | * Fills out @dmode using the display mode specified in @vm. | |
1067 | */ | |
ba0c2422 SV |
1068 | void drm_display_mode_from_videomode(const struct videomode *vm, |
1069 | struct drm_display_mode *dmode) | |
ebc64e45 ST |
1070 | { |
1071 | dmode->hdisplay = vm->hactive; | |
1072 | dmode->hsync_start = dmode->hdisplay + vm->hfront_porch; | |
1073 | dmode->hsync_end = dmode->hsync_start + vm->hsync_len; | |
1074 | dmode->htotal = dmode->hsync_end + vm->hback_porch; | |
1075 | ||
1076 | dmode->vdisplay = vm->vactive; | |
1077 | dmode->vsync_start = dmode->vdisplay + vm->vfront_porch; | |
1078 | dmode->vsync_end = dmode->vsync_start + vm->vsync_len; | |
1079 | dmode->vtotal = dmode->vsync_end + vm->vback_porch; | |
1080 | ||
1081 | dmode->clock = vm->pixelclock / 1000; | |
1082 | ||
1083 | dmode->flags = 0; | |
06a33079 | 1084 | if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) |
ebc64e45 | 1085 | dmode->flags |= DRM_MODE_FLAG_PHSYNC; |
06a33079 | 1086 | else if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW) |
ebc64e45 | 1087 | dmode->flags |= DRM_MODE_FLAG_NHSYNC; |
06a33079 | 1088 | if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) |
ebc64e45 | 1089 | dmode->flags |= DRM_MODE_FLAG_PVSYNC; |
06a33079 | 1090 | else if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW) |
ebc64e45 | 1091 | dmode->flags |= DRM_MODE_FLAG_NVSYNC; |
06a33079 | 1092 | if (vm->flags & DISPLAY_FLAGS_INTERLACED) |
ebc64e45 | 1093 | dmode->flags |= DRM_MODE_FLAG_INTERLACE; |
06a33079 | 1094 | if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN) |
ebc64e45 | 1095 | dmode->flags |= DRM_MODE_FLAG_DBLSCAN; |
328a4719 ST |
1096 | if (vm->flags & DISPLAY_FLAGS_DOUBLECLK) |
1097 | dmode->flags |= DRM_MODE_FLAG_DBLCLK; | |
ebc64e45 | 1098 | drm_mode_set_name(dmode); |
ebc64e45 ST |
1099 | } |
1100 | EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); | |
ebc64e45 | 1101 | |
d490f455 SL |
1102 | /** |
1103 | * drm_display_mode_to_videomode - fill in @vm using @dmode, | |
1104 | * @dmode: drm_display_mode structure to use as source | |
1105 | * @vm: videomode structure to use as destination | |
1106 | * | |
1107 | * Fills out @vm using the display mode specified in @dmode. | |
1108 | */ | |
1109 | void drm_display_mode_to_videomode(const struct drm_display_mode *dmode, | |
1110 | struct videomode *vm) | |
1111 | { | |
1112 | vm->hactive = dmode->hdisplay; | |
1113 | vm->hfront_porch = dmode->hsync_start - dmode->hdisplay; | |
1114 | vm->hsync_len = dmode->hsync_end - dmode->hsync_start; | |
1115 | vm->hback_porch = dmode->htotal - dmode->hsync_end; | |
1116 | ||
1117 | vm->vactive = dmode->vdisplay; | |
1118 | vm->vfront_porch = dmode->vsync_start - dmode->vdisplay; | |
1119 | vm->vsync_len = dmode->vsync_end - dmode->vsync_start; | |
1120 | vm->vback_porch = dmode->vtotal - dmode->vsync_end; | |
1121 | ||
1122 | vm->pixelclock = dmode->clock * 1000; | |
1123 | ||
1124 | vm->flags = 0; | |
1125 | if (dmode->flags & DRM_MODE_FLAG_PHSYNC) | |
1126 | vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH; | |
1127 | else if (dmode->flags & DRM_MODE_FLAG_NHSYNC) | |
1128 | vm->flags |= DISPLAY_FLAGS_HSYNC_LOW; | |
1129 | if (dmode->flags & DRM_MODE_FLAG_PVSYNC) | |
1130 | vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH; | |
1131 | else if (dmode->flags & DRM_MODE_FLAG_NVSYNC) | |
1132 | vm->flags |= DISPLAY_FLAGS_VSYNC_LOW; | |
1133 | if (dmode->flags & DRM_MODE_FLAG_INTERLACE) | |
1134 | vm->flags |= DISPLAY_FLAGS_INTERLACED; | |
1135 | if (dmode->flags & DRM_MODE_FLAG_DBLSCAN) | |
1136 | vm->flags |= DISPLAY_FLAGS_DOUBLESCAN; | |
1137 | if (dmode->flags & DRM_MODE_FLAG_DBLCLK) | |
1138 | vm->flags |= DISPLAY_FLAGS_DOUBLECLK; | |
1139 | } | |
1140 | EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode); | |
1141 | ||
cb34d7f2 LW |
1142 | /** |
1143 | * drm_bus_flags_from_videomode - extract information about pixelclk and | |
1144 | * DE polarity from videomode and store it in a separate variable | |
1145 | * @vm: videomode structure to use | |
955f60db PU |
1146 | * @bus_flags: information about pixelclk, sync and DE polarity will be stored |
1147 | * here | |
cb34d7f2 | 1148 | * |
88bc4178 LP |
1149 | * Sets DRM_BUS_FLAG_DE_(LOW|HIGH), DRM_BUS_FLAG_PIXDATA_DRIVE_(POS|NEG)EDGE |
1150 | * and DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS | |
955f60db | 1151 | * found in @vm |
cb34d7f2 | 1152 | */ |
d72daa0d LW |
1153 | void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags) |
1154 | { | |
1155 | *bus_flags = 0; | |
1156 | if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) | |
88bc4178 | 1157 | *bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; |
d72daa0d | 1158 | if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) |
88bc4178 | 1159 | *bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; |
d72daa0d | 1160 | |
955f60db | 1161 | if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE) |
88bc4178 | 1162 | *bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE; |
955f60db | 1163 | if (vm->flags & DISPLAY_FLAGS_SYNC_NEGEDGE) |
88bc4178 | 1164 | *bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE; |
955f60db | 1165 | |
d72daa0d LW |
1166 | if (vm->flags & DISPLAY_FLAGS_DE_LOW) |
1167 | *bus_flags |= DRM_BUS_FLAG_DE_LOW; | |
1168 | if (vm->flags & DISPLAY_FLAGS_DE_HIGH) | |
1169 | *bus_flags |= DRM_BUS_FLAG_DE_HIGH; | |
1170 | } | |
1171 | EXPORT_SYMBOL_GPL(drm_bus_flags_from_videomode); | |
1172 | ||
a38884f6 | 1173 | #ifdef CONFIG_OF |
edb37a95 ST |
1174 | /** |
1175 | * of_get_drm_display_mode - get a drm_display_mode from devicetree | |
1176 | * @np: device_node with the timing specification | |
1177 | * @dmode: will be set to the return value | |
955f60db | 1178 | * @bus_flags: information about pixelclk, sync and DE polarity |
edb37a95 ST |
1179 | * @index: index into the list of display timings in devicetree |
1180 | * | |
1181 | * This function is expensive and should only be used, if only one mode is to be | |
1182 | * read from DT. To get multiple modes start with of_get_display_timings and | |
1183 | * work with that instead. | |
f5aabb97 SV |
1184 | * |
1185 | * Returns: | |
1186 | * 0 on success, a negative errno code when no of videomode node was found. | |
edb37a95 ST |
1187 | */ |
1188 | int of_get_drm_display_mode(struct device_node *np, | |
fafc79ef LW |
1189 | struct drm_display_mode *dmode, u32 *bus_flags, |
1190 | int index) | |
edb37a95 ST |
1191 | { |
1192 | struct videomode vm; | |
1193 | int ret; | |
1194 | ||
1195 | ret = of_get_videomode(np, &vm, index); | |
1196 | if (ret) | |
1197 | return ret; | |
1198 | ||
1199 | drm_display_mode_from_videomode(&vm, dmode); | |
fafc79ef LW |
1200 | if (bus_flags) |
1201 | drm_bus_flags_from_videomode(&vm, bus_flags); | |
edb37a95 | 1202 | |
f384d7d5 RH |
1203 | pr_debug("%pOF: got %dx%d display mode\n", |
1204 | np, vm.hactive, vm.vactive); | |
edb37a95 ST |
1205 | drm_mode_debug_printmodeline(dmode); |
1206 | ||
1207 | return 0; | |
1208 | } | |
1209 | EXPORT_SYMBOL_GPL(of_get_drm_display_mode); | |
95ae342d NT |
1210 | |
1211 | /** | |
1212 | * of_get_drm_panel_display_mode - get a panel-timing drm_display_mode from devicetree | |
1213 | * @np: device_node with the panel-timing specification | |
1214 | * @dmode: will be set to the return value | |
1215 | * @bus_flags: information about pixelclk, sync and DE polarity | |
1216 | * | |
f470cfe4 MV |
1217 | * The mandatory Device Tree properties width-mm and height-mm |
1218 | * are read and set on the display mode. | |
95ae342d NT |
1219 | * |
1220 | * Returns: | |
1221 | * Zero on success, negative error code on failure. | |
1222 | */ | |
1223 | int of_get_drm_panel_display_mode(struct device_node *np, | |
1224 | struct drm_display_mode *dmode, u32 *bus_flags) | |
1225 | { | |
1226 | u32 width_mm = 0, height_mm = 0; | |
1227 | struct display_timing timing; | |
1228 | struct videomode vm; | |
1229 | int ret; | |
1230 | ||
1231 | ret = of_get_display_timing(np, "panel-timing", &timing); | |
1232 | if (ret) | |
1233 | return ret; | |
1234 | ||
1235 | videomode_from_timing(&timing, &vm); | |
1236 | ||
1237 | memset(dmode, 0, sizeof(*dmode)); | |
1238 | drm_display_mode_from_videomode(&vm, dmode); | |
1239 | if (bus_flags) | |
1240 | drm_bus_flags_from_videomode(&vm, bus_flags); | |
1241 | ||
1242 | ret = of_property_read_u32(np, "width-mm", &width_mm); | |
f470cfe4 | 1243 | if (ret) |
95ae342d NT |
1244 | return ret; |
1245 | ||
1246 | ret = of_property_read_u32(np, "height-mm", &height_mm); | |
f470cfe4 | 1247 | if (ret) |
95ae342d NT |
1248 | return ret; |
1249 | ||
1250 | dmode->width_mm = width_mm; | |
1251 | dmode->height_mm = height_mm; | |
1252 | ||
1253 | drm_mode_debug_printmodeline(dmode); | |
1254 | ||
1255 | return 0; | |
1256 | } | |
1257 | EXPORT_SYMBOL_GPL(of_get_drm_panel_display_mode); | |
a38884f6 TV |
1258 | #endif /* CONFIG_OF */ |
1259 | #endif /* CONFIG_VIDEOMODE_HELPERS */ | |
edb37a95 | 1260 | |
f453ba04 DA |
1261 | /** |
1262 | * drm_mode_set_name - set the name on a mode | |
1263 | * @mode: name will be set in this mode | |
1264 | * | |
f5aabb97 SV |
1265 | * Set the name of @mode to a standard format which is <hdisplay>x<vdisplay> |
1266 | * with an optional 'i' suffix for interlaced modes. | |
f453ba04 DA |
1267 | */ |
1268 | void drm_mode_set_name(struct drm_display_mode *mode) | |
1269 | { | |
171fdd89 AJ |
1270 | bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); |
1271 | ||
1272 | snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d%s", | |
1273 | mode->hdisplay, mode->vdisplay, | |
1274 | interlaced ? "i" : ""); | |
f453ba04 DA |
1275 | } |
1276 | EXPORT_SYMBOL(drm_mode_set_name); | |
1277 | ||
f453ba04 DA |
1278 | /** |
1279 | * drm_mode_vrefresh - get the vrefresh of a mode | |
1280 | * @mode: mode | |
1281 | * | |
f5aabb97 SV |
1282 | * Returns: |
1283 | * @modes's vrefresh rate in Hz, rounded to the nearest integer. Calculates the | |
1284 | * value first if it is not yet set. | |
f453ba04 | 1285 | */ |
b1f559ec | 1286 | int drm_mode_vrefresh(const struct drm_display_mode *mode) |
f453ba04 | 1287 | { |
4ed2101c | 1288 | unsigned int num, den; |
f453ba04 | 1289 | |
4ed2101c VS |
1290 | if (mode->htotal == 0 || mode->vtotal == 0) |
1291 | return 0; | |
2f0e9d80 | 1292 | |
5b34ab52 | 1293 | num = mode->clock; |
4ed2101c | 1294 | den = mode->htotal * mode->vtotal; |
f453ba04 | 1295 | |
4ed2101c VS |
1296 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) |
1297 | num *= 2; | |
1298 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | |
1299 | den *= 2; | |
1300 | if (mode->vscan > 1) | |
1301 | den *= mode->vscan; | |
2f0e9d80 | 1302 | |
5b34ab52 | 1303 | return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(num, 1000), den); |
f453ba04 DA |
1304 | } |
1305 | EXPORT_SYMBOL(drm_mode_vrefresh); | |
1306 | ||
196cd5d3 SV |
1307 | /** |
1308 | * drm_mode_get_hv_timing - Fetches hdisplay/vdisplay for given mode | |
1309 | * @mode: mode to query | |
1310 | * @hdisplay: hdisplay value to fill in | |
1311 | * @vdisplay: vdisplay value to fill in | |
1312 | * | |
1313 | * The vdisplay value will be doubled if the specified mode is a stereo mode of | |
1314 | * the appropriate layout. | |
1315 | */ | |
1316 | void drm_mode_get_hv_timing(const struct drm_display_mode *mode, | |
1317 | int *hdisplay, int *vdisplay) | |
1318 | { | |
563c4a75 VS |
1319 | struct drm_display_mode adjusted; |
1320 | ||
1321 | drm_mode_init(&adjusted, mode); | |
196cd5d3 | 1322 | |
196cd5d3 SV |
1323 | drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY); |
1324 | *hdisplay = adjusted.crtc_hdisplay; | |
1325 | *vdisplay = adjusted.crtc_vdisplay; | |
1326 | } | |
1327 | EXPORT_SYMBOL(drm_mode_get_hv_timing); | |
1328 | ||
f453ba04 | 1329 | /** |
f5aabb97 | 1330 | * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters |
f453ba04 | 1331 | * @p: mode |
448cce25 | 1332 | * @adjust_flags: a combination of adjustment flags |
f453ba04 | 1333 | * |
f5aabb97 | 1334 | * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary. |
448cce25 DL |
1335 | * |
1336 | * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of | |
1337 | * interlaced modes. | |
1338 | * - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for | |
1339 | * buffers containing two eyes (only adjust the timings when needed, eg. for | |
1340 | * "frame packing" or "side by side full"). | |
ecb7e16b GP |
1341 | * - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not* |
1342 | * be performed for doublescan and vscan > 1 modes respectively. | |
f453ba04 DA |
1343 | */ |
1344 | void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) | |
1345 | { | |
4f09c77b | 1346 | if (!p) |
f453ba04 DA |
1347 | return; |
1348 | ||
bde2dcf7 | 1349 | p->crtc_clock = p->clock; |
f453ba04 DA |
1350 | p->crtc_hdisplay = p->hdisplay; |
1351 | p->crtc_hsync_start = p->hsync_start; | |
1352 | p->crtc_hsync_end = p->hsync_end; | |
1353 | p->crtc_htotal = p->htotal; | |
1354 | p->crtc_hskew = p->hskew; | |
1355 | p->crtc_vdisplay = p->vdisplay; | |
1356 | p->crtc_vsync_start = p->vsync_start; | |
1357 | p->crtc_vsync_end = p->vsync_end; | |
1358 | p->crtc_vtotal = p->vtotal; | |
1359 | ||
1360 | if (p->flags & DRM_MODE_FLAG_INTERLACE) { | |
1361 | if (adjust_flags & CRTC_INTERLACE_HALVE_V) { | |
1362 | p->crtc_vdisplay /= 2; | |
1363 | p->crtc_vsync_start /= 2; | |
1364 | p->crtc_vsync_end /= 2; | |
1365 | p->crtc_vtotal /= 2; | |
1366 | } | |
f453ba04 DA |
1367 | } |
1368 | ||
ecb7e16b GP |
1369 | if (!(adjust_flags & CRTC_NO_DBLSCAN)) { |
1370 | if (p->flags & DRM_MODE_FLAG_DBLSCAN) { | |
1371 | p->crtc_vdisplay *= 2; | |
1372 | p->crtc_vsync_start *= 2; | |
1373 | p->crtc_vsync_end *= 2; | |
1374 | p->crtc_vtotal *= 2; | |
1375 | } | |
f453ba04 DA |
1376 | } |
1377 | ||
ecb7e16b GP |
1378 | if (!(adjust_flags & CRTC_NO_VSCAN)) { |
1379 | if (p->vscan > 1) { | |
1380 | p->crtc_vdisplay *= p->vscan; | |
1381 | p->crtc_vsync_start *= p->vscan; | |
1382 | p->crtc_vsync_end *= p->vscan; | |
1383 | p->crtc_vtotal *= p->vscan; | |
1384 | } | |
f453ba04 DA |
1385 | } |
1386 | ||
448cce25 DL |
1387 | if (adjust_flags & CRTC_STEREO_DOUBLE) { |
1388 | unsigned int layout = p->flags & DRM_MODE_FLAG_3D_MASK; | |
1389 | ||
1390 | switch (layout) { | |
1391 | case DRM_MODE_FLAG_3D_FRAME_PACKING: | |
1392 | p->crtc_clock *= 2; | |
1393 | p->crtc_vdisplay += p->crtc_vtotal; | |
1394 | p->crtc_vsync_start += p->crtc_vtotal; | |
1395 | p->crtc_vsync_end += p->crtc_vtotal; | |
1396 | p->crtc_vtotal += p->crtc_vtotal; | |
1397 | break; | |
1398 | } | |
1399 | } | |
1400 | ||
f453ba04 DA |
1401 | p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); |
1402 | p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); | |
1403 | p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); | |
1404 | p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); | |
f453ba04 DA |
1405 | } |
1406 | EXPORT_SYMBOL(drm_mode_set_crtcinfo); | |
1407 | ||
c3c50e8b VS |
1408 | /** |
1409 | * drm_mode_copy - copy the mode | |
1410 | * @dst: mode to overwrite | |
1411 | * @src: mode to copy | |
1412 | * | |
b3d9f59f | 1413 | * Copy an existing mode into another mode, preserving the |
72e45e92 | 1414 | * list head of the destination mode. |
c3c50e8b VS |
1415 | */ |
1416 | void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src) | |
1417 | { | |
72e45e92 | 1418 | struct list_head head = dst->head; |
c3c50e8b VS |
1419 | |
1420 | *dst = *src; | |
72e45e92 | 1421 | dst->head = head; |
c3c50e8b VS |
1422 | } |
1423 | EXPORT_SYMBOL(drm_mode_copy); | |
1424 | ||
2d3eec89 VS |
1425 | /** |
1426 | * drm_mode_init - initialize the mode from another mode | |
1427 | * @dst: mode to overwrite | |
1428 | * @src: mode to copy | |
1429 | * | |
1430 | * Copy an existing mode into another mode, zeroing the | |
1431 | * list head of the destination mode. Typically used | |
1432 | * to guarantee the list head is not left with stack | |
1433 | * garbage in on-stack modes. | |
1434 | */ | |
1435 | void drm_mode_init(struct drm_display_mode *dst, const struct drm_display_mode *src) | |
1436 | { | |
1437 | memset(dst, 0, sizeof(*dst)); | |
1438 | drm_mode_copy(dst, src); | |
1439 | } | |
1440 | EXPORT_SYMBOL(drm_mode_init); | |
1441 | ||
f453ba04 DA |
1442 | /** |
1443 | * drm_mode_duplicate - allocate and duplicate an existing mode | |
3ec0db81 SV |
1444 | * @dev: drm_device to allocate the duplicated mode for |
1445 | * @mode: mode to duplicate | |
f453ba04 | 1446 | * |
f453ba04 DA |
1447 | * Just allocate a new mode, copy the existing mode into it, and return |
1448 | * a pointer to it. Used to create new instances of established modes. | |
f5aabb97 SV |
1449 | * |
1450 | * Returns: | |
1451 | * Pointer to duplicated mode on success, NULL on error. | |
f453ba04 DA |
1452 | */ |
1453 | struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, | |
b1f559ec | 1454 | const struct drm_display_mode *mode) |
f453ba04 DA |
1455 | { |
1456 | struct drm_display_mode *nmode; | |
f453ba04 DA |
1457 | |
1458 | nmode = drm_mode_create(dev); | |
1459 | if (!nmode) | |
1460 | return NULL; | |
1461 | ||
c3c50e8b VS |
1462 | drm_mode_copy(nmode, mode); |
1463 | ||
f453ba04 DA |
1464 | return nmode; |
1465 | } | |
1466 | EXPORT_SYMBOL(drm_mode_duplicate); | |
1467 | ||
dd7c2626 VS |
1468 | static bool drm_mode_match_timings(const struct drm_display_mode *mode1, |
1469 | const struct drm_display_mode *mode2) | |
1470 | { | |
1471 | return mode1->hdisplay == mode2->hdisplay && | |
1472 | mode1->hsync_start == mode2->hsync_start && | |
1473 | mode1->hsync_end == mode2->hsync_end && | |
1474 | mode1->htotal == mode2->htotal && | |
1475 | mode1->hskew == mode2->hskew && | |
1476 | mode1->vdisplay == mode2->vdisplay && | |
1477 | mode1->vsync_start == mode2->vsync_start && | |
1478 | mode1->vsync_end == mode2->vsync_end && | |
1479 | mode1->vtotal == mode2->vtotal && | |
1480 | mode1->vscan == mode2->vscan; | |
1481 | } | |
1482 | ||
1483 | static bool drm_mode_match_clock(const struct drm_display_mode *mode1, | |
1484 | const struct drm_display_mode *mode2) | |
1485 | { | |
1486 | /* | |
1487 | * do clock check convert to PICOS | |
1488 | * so fb modes get matched the same | |
1489 | */ | |
1490 | if (mode1->clock && mode2->clock) | |
1491 | return KHZ2PICOS(mode1->clock) == KHZ2PICOS(mode2->clock); | |
1492 | else | |
1493 | return mode1->clock == mode2->clock; | |
1494 | } | |
1495 | ||
1496 | static bool drm_mode_match_flags(const struct drm_display_mode *mode1, | |
1497 | const struct drm_display_mode *mode2) | |
1498 | { | |
1499 | return (mode1->flags & ~DRM_MODE_FLAG_3D_MASK) == | |
1500 | (mode2->flags & ~DRM_MODE_FLAG_3D_MASK); | |
1501 | } | |
1502 | ||
1503 | static bool drm_mode_match_3d_flags(const struct drm_display_mode *mode1, | |
1504 | const struct drm_display_mode *mode2) | |
1505 | { | |
1506 | return (mode1->flags & DRM_MODE_FLAG_3D_MASK) == | |
1507 | (mode2->flags & DRM_MODE_FLAG_3D_MASK); | |
1508 | } | |
1509 | ||
1510 | static bool drm_mode_match_aspect_ratio(const struct drm_display_mode *mode1, | |
1511 | const struct drm_display_mode *mode2) | |
1512 | { | |
1513 | return mode1->picture_aspect_ratio == mode2->picture_aspect_ratio; | |
1514 | } | |
1515 | ||
f453ba04 | 1516 | /** |
dd7c2626 | 1517 | * drm_mode_match - test modes for (partial) equality |
f453ba04 DA |
1518 | * @mode1: first mode |
1519 | * @mode2: second mode | |
dd7c2626 | 1520 | * @match_flags: which parts need to match (DRM_MODE_MATCH_*) |
f453ba04 | 1521 | * |
f453ba04 DA |
1522 | * Check to see if @mode1 and @mode2 are equivalent. |
1523 | * | |
f5aabb97 | 1524 | * Returns: |
dd7c2626 | 1525 | * True if the modes are (partially) equal, false otherwise. |
f453ba04 | 1526 | */ |
dd7c2626 VS |
1527 | bool drm_mode_match(const struct drm_display_mode *mode1, |
1528 | const struct drm_display_mode *mode2, | |
1529 | unsigned int match_flags) | |
f453ba04 | 1530 | { |
54270952 DS |
1531 | if (!mode1 && !mode2) |
1532 | return true; | |
1533 | ||
1534 | if (!mode1 || !mode2) | |
1535 | return false; | |
1536 | ||
dd7c2626 VS |
1537 | if (match_flags & DRM_MODE_MATCH_TIMINGS && |
1538 | !drm_mode_match_timings(mode1, mode2)) | |
f453ba04 DA |
1539 | return false; |
1540 | ||
dd7c2626 VS |
1541 | if (match_flags & DRM_MODE_MATCH_CLOCK && |
1542 | !drm_mode_match_clock(mode1, mode2)) | |
1543 | return false; | |
1544 | ||
1545 | if (match_flags & DRM_MODE_MATCH_FLAGS && | |
1546 | !drm_mode_match_flags(mode1, mode2)) | |
1547 | return false; | |
1548 | ||
1549 | if (match_flags & DRM_MODE_MATCH_3D_FLAGS && | |
1550 | !drm_mode_match_3d_flags(mode1, mode2)) | |
1551 | return false; | |
1552 | ||
1553 | if (match_flags & DRM_MODE_MATCH_ASPECT_RATIO && | |
1554 | !drm_mode_match_aspect_ratio(mode1, mode2)) | |
1555 | return false; | |
1556 | ||
1557 | return true; | |
1558 | } | |
1559 | EXPORT_SYMBOL(drm_mode_match); | |
1560 | ||
1561 | /** | |
1562 | * drm_mode_equal - test modes for equality | |
1563 | * @mode1: first mode | |
1564 | * @mode2: second mode | |
1565 | * | |
1566 | * Check to see if @mode1 and @mode2 are equivalent. | |
1567 | * | |
1568 | * Returns: | |
1569 | * True if the modes are equal, false otherwise. | |
1570 | */ | |
1571 | bool drm_mode_equal(const struct drm_display_mode *mode1, | |
1572 | const struct drm_display_mode *mode2) | |
1573 | { | |
1574 | return drm_mode_match(mode1, mode2, | |
1575 | DRM_MODE_MATCH_TIMINGS | | |
1576 | DRM_MODE_MATCH_CLOCK | | |
1577 | DRM_MODE_MATCH_FLAGS | | |
222ec161 SS |
1578 | DRM_MODE_MATCH_3D_FLAGS| |
1579 | DRM_MODE_MATCH_ASPECT_RATIO); | |
4c6bcf44 VS |
1580 | } |
1581 | EXPORT_SYMBOL(drm_mode_equal); | |
1582 | ||
1583 | /** | |
1584 | * drm_mode_equal_no_clocks - test modes for equality | |
1585 | * @mode1: first mode | |
1586 | * @mode2: second mode | |
1587 | * | |
1588 | * Check to see if @mode1 and @mode2 are equivalent, but | |
1589 | * don't check the pixel clocks. | |
1590 | * | |
1591 | * Returns: | |
1592 | * True if the modes are equal, false otherwise. | |
1593 | */ | |
dd7c2626 VS |
1594 | bool drm_mode_equal_no_clocks(const struct drm_display_mode *mode1, |
1595 | const struct drm_display_mode *mode2) | |
4c6bcf44 | 1596 | { |
dd7c2626 VS |
1597 | return drm_mode_match(mode1, mode2, |
1598 | DRM_MODE_MATCH_TIMINGS | | |
1599 | DRM_MODE_MATCH_FLAGS | | |
1600 | DRM_MODE_MATCH_3D_FLAGS); | |
8cc3f23c | 1601 | } |
4c6bcf44 | 1602 | EXPORT_SYMBOL(drm_mode_equal_no_clocks); |
8cc3f23c VS |
1603 | |
1604 | /** | |
f2ecf2e3 | 1605 | * drm_mode_equal_no_clocks_no_stereo - test modes for equality |
8cc3f23c VS |
1606 | * @mode1: first mode |
1607 | * @mode2: second mode | |
1608 | * | |
8cc3f23c | 1609 | * Check to see if @mode1 and @mode2 are equivalent, but |
f2ecf2e3 | 1610 | * don't check the pixel clocks nor the stereo layout. |
8cc3f23c | 1611 | * |
f5aabb97 | 1612 | * Returns: |
8cc3f23c VS |
1613 | * True if the modes are equal, false otherwise. |
1614 | */ | |
f2ecf2e3 DL |
1615 | bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, |
1616 | const struct drm_display_mode *mode2) | |
8cc3f23c | 1617 | { |
dd7c2626 VS |
1618 | return drm_mode_match(mode1, mode2, |
1619 | DRM_MODE_MATCH_TIMINGS | | |
1620 | DRM_MODE_MATCH_FLAGS); | |
f453ba04 | 1621 | } |
f2ecf2e3 | 1622 | EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo); |
f453ba04 | 1623 | |
75a655e0 | 1624 | static enum drm_mode_status |
abc0b144 VS |
1625 | drm_mode_validate_basic(const struct drm_display_mode *mode) |
1626 | { | |
c6ed6dad VS |
1627 | if (mode->type & ~DRM_MODE_TYPE_ALL) |
1628 | return MODE_BAD; | |
1629 | ||
1630 | if (mode->flags & ~DRM_MODE_FLAG_ALL) | |
1631 | return MODE_BAD; | |
1632 | ||
064a3e6e VS |
1633 | if ((mode->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX) |
1634 | return MODE_BAD; | |
1635 | ||
abc0b144 VS |
1636 | if (mode->clock == 0) |
1637 | return MODE_CLOCK_LOW; | |
1638 | ||
1639 | if (mode->hdisplay == 0 || | |
1640 | mode->hsync_start < mode->hdisplay || | |
1641 | mode->hsync_end < mode->hsync_start || | |
1642 | mode->htotal < mode->hsync_end) | |
1643 | return MODE_H_ILLEGAL; | |
1644 | ||
1645 | if (mode->vdisplay == 0 || | |
1646 | mode->vsync_start < mode->vdisplay || | |
1647 | mode->vsync_end < mode->vsync_start || | |
1648 | mode->vtotal < mode->vsync_end) | |
1649 | return MODE_V_ILLEGAL; | |
1650 | ||
1651 | return MODE_OK; | |
1652 | } | |
75a655e0 VS |
1653 | |
1654 | /** | |
1655 | * drm_mode_validate_driver - make sure the mode is somewhat sane | |
1656 | * @dev: drm device | |
1657 | * @mode: mode to check | |
1658 | * | |
1659 | * First do basic validation on the mode, and then allow the driver | |
1660 | * to check for device/driver specific limitations via the optional | |
1661 | * &drm_mode_config_helper_funcs.mode_valid hook. | |
1662 | * | |
1663 | * Returns: | |
1664 | * The mode status | |
1665 | */ | |
1666 | enum drm_mode_status | |
1667 | drm_mode_validate_driver(struct drm_device *dev, | |
1668 | const struct drm_display_mode *mode) | |
1669 | { | |
1670 | enum drm_mode_status status; | |
1671 | ||
1672 | status = drm_mode_validate_basic(mode); | |
1673 | if (status != MODE_OK) | |
1674 | return status; | |
1675 | ||
1676 | if (dev->mode_config.funcs->mode_valid) | |
1677 | return dev->mode_config.funcs->mode_valid(dev, mode); | |
1678 | else | |
1679 | return MODE_OK; | |
1680 | } | |
1681 | EXPORT_SYMBOL(drm_mode_validate_driver); | |
abc0b144 | 1682 | |
f453ba04 DA |
1683 | /** |
1684 | * drm_mode_validate_size - make sure modes adhere to size constraints | |
05acaec3 | 1685 | * @mode: mode to check |
f453ba04 DA |
1686 | * @maxX: maximum width |
1687 | * @maxY: maximum height | |
f453ba04 | 1688 | * |
f5aabb97 SV |
1689 | * This function is a helper which can be used to validate modes against size |
1690 | * limitations of the DRM device/connector. If a mode is too big its status | |
32197aab | 1691 | * member is updated with the appropriate validation failure code. The list |
f5aabb97 | 1692 | * itself is not changed. |
05acaec3 VS |
1693 | * |
1694 | * Returns: | |
1695 | * The mode status | |
f453ba04 | 1696 | */ |
05acaec3 VS |
1697 | enum drm_mode_status |
1698 | drm_mode_validate_size(const struct drm_display_mode *mode, | |
1699 | int maxX, int maxY) | |
f453ba04 | 1700 | { |
05acaec3 VS |
1701 | if (maxX > 0 && mode->hdisplay > maxX) |
1702 | return MODE_VIRTUAL_X; | |
f453ba04 | 1703 | |
05acaec3 VS |
1704 | if (maxY > 0 && mode->vdisplay > maxY) |
1705 | return MODE_VIRTUAL_Y; | |
f453ba04 | 1706 | |
05acaec3 | 1707 | return MODE_OK; |
f453ba04 DA |
1708 | } |
1709 | EXPORT_SYMBOL(drm_mode_validate_size); | |
1710 | ||
d8523153 SS |
1711 | /** |
1712 | * drm_mode_validate_ycbcr420 - add 'ycbcr420-only' modes only when allowed | |
1713 | * @mode: mode to check | |
1714 | * @connector: drm connector under action | |
1715 | * | |
1716 | * This function is a helper which can be used to filter out any YCBCR420 | |
1717 | * only mode, when the source doesn't support it. | |
1718 | * | |
1719 | * Returns: | |
1720 | * The mode status | |
1721 | */ | |
1722 | enum drm_mode_status | |
1723 | drm_mode_validate_ycbcr420(const struct drm_display_mode *mode, | |
1724 | struct drm_connector *connector) | |
1725 | { | |
1c4f39b0 VS |
1726 | if (!connector->ycbcr_420_allowed && |
1727 | drm_mode_is_420_only(&connector->display_info, mode)) | |
1728 | return MODE_NO_420; | |
d8523153 | 1729 | |
1c4f39b0 | 1730 | return MODE_OK; |
d8523153 SS |
1731 | } |
1732 | EXPORT_SYMBOL(drm_mode_validate_ycbcr420); | |
1733 | ||
e4bf44b3 VS |
1734 | #define MODE_STATUS(status) [MODE_ ## status + 3] = #status |
1735 | ||
1736 | static const char * const drm_mode_status_names[] = { | |
1737 | MODE_STATUS(OK), | |
1738 | MODE_STATUS(HSYNC), | |
1739 | MODE_STATUS(VSYNC), | |
1740 | MODE_STATUS(H_ILLEGAL), | |
1741 | MODE_STATUS(V_ILLEGAL), | |
1742 | MODE_STATUS(BAD_WIDTH), | |
1743 | MODE_STATUS(NOMODE), | |
1744 | MODE_STATUS(NO_INTERLACE), | |
1745 | MODE_STATUS(NO_DBLESCAN), | |
1746 | MODE_STATUS(NO_VSCAN), | |
1747 | MODE_STATUS(MEM), | |
1748 | MODE_STATUS(VIRTUAL_X), | |
1749 | MODE_STATUS(VIRTUAL_Y), | |
1750 | MODE_STATUS(MEM_VIRT), | |
1751 | MODE_STATUS(NOCLOCK), | |
1752 | MODE_STATUS(CLOCK_HIGH), | |
1753 | MODE_STATUS(CLOCK_LOW), | |
1754 | MODE_STATUS(CLOCK_RANGE), | |
1755 | MODE_STATUS(BAD_HVALUE), | |
1756 | MODE_STATUS(BAD_VVALUE), | |
1757 | MODE_STATUS(BAD_VSCAN), | |
1758 | MODE_STATUS(HSYNC_NARROW), | |
1759 | MODE_STATUS(HSYNC_WIDE), | |
1760 | MODE_STATUS(HBLANK_NARROW), | |
1761 | MODE_STATUS(HBLANK_WIDE), | |
1762 | MODE_STATUS(VSYNC_NARROW), | |
1763 | MODE_STATUS(VSYNC_WIDE), | |
1764 | MODE_STATUS(VBLANK_NARROW), | |
1765 | MODE_STATUS(VBLANK_WIDE), | |
1766 | MODE_STATUS(PANEL), | |
1767 | MODE_STATUS(INTERLACE_WIDTH), | |
1768 | MODE_STATUS(ONE_WIDTH), | |
1769 | MODE_STATUS(ONE_HEIGHT), | |
1770 | MODE_STATUS(ONE_SIZE), | |
1771 | MODE_STATUS(NO_REDUCED), | |
1772 | MODE_STATUS(NO_STEREO), | |
d8523153 | 1773 | MODE_STATUS(NO_420), |
5ba89406 | 1774 | MODE_STATUS(STALE), |
e4bf44b3 VS |
1775 | MODE_STATUS(BAD), |
1776 | MODE_STATUS(ERROR), | |
1777 | }; | |
1778 | ||
1779 | #undef MODE_STATUS | |
1780 | ||
6ab0edf4 | 1781 | const char *drm_get_mode_status_name(enum drm_mode_status status) |
e4bf44b3 VS |
1782 | { |
1783 | int index = status + 3; | |
1784 | ||
1785 | if (WARN_ON(index < 0 || index >= ARRAY_SIZE(drm_mode_status_names))) | |
1786 | return ""; | |
1787 | ||
1788 | return drm_mode_status_names[index]; | |
1789 | } | |
1790 | ||
f453ba04 DA |
1791 | /** |
1792 | * drm_mode_prune_invalid - remove invalid modes from mode list | |
1793 | * @dev: DRM device | |
1794 | * @mode_list: list of modes to check | |
1795 | * @verbose: be verbose about it | |
1796 | * | |
f5aabb97 | 1797 | * This helper function can be used to prune a display mode list after |
1e55a53a | 1798 | * validation has been completed. All modes whose status is not MODE_OK will be |
f5aabb97 SV |
1799 | * removed from the list, and if @verbose the status code and mode name is also |
1800 | * printed to dmesg. | |
f453ba04 DA |
1801 | */ |
1802 | void drm_mode_prune_invalid(struct drm_device *dev, | |
1803 | struct list_head *mode_list, bool verbose) | |
1804 | { | |
1805 | struct drm_display_mode *mode, *t; | |
1806 | ||
1807 | list_for_each_entry_safe(mode, t, mode_list, head) { | |
1808 | if (mode->status != MODE_OK) { | |
1809 | list_del(&mode->head); | |
b6415bd7 TZ |
1810 | if (mode->type & DRM_MODE_TYPE_USERDEF) { |
1811 | drm_warn(dev, "User-defined mode not supported: " | |
1812 | DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); | |
1813 | } | |
f453ba04 DA |
1814 | if (verbose) { |
1815 | drm_mode_debug_printmodeline(mode); | |
e4bf44b3 VS |
1816 | DRM_DEBUG_KMS("Not using %s mode: %s\n", |
1817 | mode->name, | |
1818 | drm_get_mode_status_name(mode->status)); | |
f453ba04 DA |
1819 | } |
1820 | drm_mode_destroy(dev, mode); | |
1821 | } | |
1822 | } | |
1823 | } | |
1824 | EXPORT_SYMBOL(drm_mode_prune_invalid); | |
1825 | ||
1826 | /** | |
1827 | * drm_mode_compare - compare modes for favorability | |
2c761270 | 1828 | * @priv: unused |
f453ba04 DA |
1829 | * @lh_a: list_head for first mode |
1830 | * @lh_b: list_head for second mode | |
1831 | * | |
f453ba04 DA |
1832 | * Compare two modes, given by @lh_a and @lh_b, returning a value indicating |
1833 | * which is better. | |
1834 | * | |
f5aabb97 | 1835 | * Returns: |
f453ba04 DA |
1836 | * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or |
1837 | * positive if @lh_b is better than @lh_a. | |
1838 | */ | |
4f0f586b ST |
1839 | static int drm_mode_compare(void *priv, const struct list_head *lh_a, |
1840 | const struct list_head *lh_b) | |
f453ba04 DA |
1841 | { |
1842 | struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head); | |
1843 | struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head); | |
1844 | int diff; | |
1845 | ||
1846 | diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) - | |
1847 | ((a->type & DRM_MODE_TYPE_PREFERRED) != 0); | |
1848 | if (diff) | |
1849 | return diff; | |
1850 | diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay; | |
1851 | if (diff) | |
1852 | return diff; | |
9bc3cd56 | 1853 | |
0425662f | 1854 | diff = drm_mode_vrefresh(b) - drm_mode_vrefresh(a); |
9bc3cd56 VS |
1855 | if (diff) |
1856 | return diff; | |
1857 | ||
f453ba04 DA |
1858 | diff = b->clock - a->clock; |
1859 | return diff; | |
1860 | } | |
1861 | ||
f453ba04 DA |
1862 | /** |
1863 | * drm_mode_sort - sort mode list | |
f5aabb97 | 1864 | * @mode_list: list of drm_display_mode structures to sort |
f453ba04 | 1865 | * |
f5aabb97 | 1866 | * Sort @mode_list by favorability, moving good modes to the head of the list. |
f453ba04 DA |
1867 | */ |
1868 | void drm_mode_sort(struct list_head *mode_list) | |
1869 | { | |
2c761270 | 1870 | list_sort(NULL, mode_list, drm_mode_compare); |
f453ba04 DA |
1871 | } |
1872 | EXPORT_SYMBOL(drm_mode_sort); | |
1873 | ||
1874 | /** | |
97e14fbe | 1875 | * drm_connector_list_update - update the mode list for the connector |
f453ba04 DA |
1876 | * @connector: the connector to update |
1877 | * | |
f453ba04 DA |
1878 | * This moves the modes from the @connector probed_modes list |
1879 | * to the actual mode list. It compares the probed mode against the current | |
f5aabb97 SV |
1880 | * list and only adds different/new modes. |
1881 | * | |
1882 | * This is just a helper functions doesn't validate any modes itself and also | |
1883 | * doesn't prune any invalid modes. Callers need to do that themselves. | |
f453ba04 | 1884 | */ |
97e14fbe | 1885 | void drm_connector_list_update(struct drm_connector *connector) |
f453ba04 | 1886 | { |
f453ba04 | 1887 | struct drm_display_mode *pmode, *pt; |
f453ba04 | 1888 | |
63951385 SV |
1889 | WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); |
1890 | ||
2f8c19e7 VS |
1891 | list_for_each_entry_safe(pmode, pt, &connector->probed_modes, head) { |
1892 | struct drm_display_mode *mode; | |
1893 | bool found_it = false; | |
1894 | ||
f453ba04 DA |
1895 | /* go through current modes checking for the new probed mode */ |
1896 | list_for_each_entry(mode, &connector->modes, head) { | |
2f8c19e7 VS |
1897 | if (!drm_mode_equal(pmode, mode)) |
1898 | continue; | |
1899 | ||
1900 | found_it = true; | |
fc245f88 VS |
1901 | |
1902 | /* | |
1903 | * If the old matching mode is stale (ie. left over | |
1904 | * from a previous probe) just replace it outright. | |
1905 | * Otherwise just merge the type bits between all | |
1906 | * equal probed modes. | |
1907 | * | |
1908 | * If two probed modes are considered equal, pick the | |
1909 | * actual timings from the one that's marked as | |
1910 | * preferred (in case the match isn't 100%). If | |
1911 | * multiple or zero preferred modes are present, favor | |
1912 | * the mode added to the probed_modes list first. | |
1913 | */ | |
1914 | if (mode->status == MODE_STALE) { | |
1915 | drm_mode_copy(mode, pmode); | |
1916 | } else if ((mode->type & DRM_MODE_TYPE_PREFERRED) == 0 && | |
1917 | (pmode->type & DRM_MODE_TYPE_PREFERRED) != 0) { | |
6af3e656 | 1918 | pmode->type |= mode->type; |
fc245f88 VS |
1919 | drm_mode_copy(mode, pmode); |
1920 | } else { | |
6af3e656 | 1921 | mode->type |= pmode->type; |
fc245f88 VS |
1922 | } |
1923 | ||
2f8c19e7 VS |
1924 | list_del(&pmode->head); |
1925 | drm_mode_destroy(connector->dev, pmode); | |
1926 | break; | |
f453ba04 DA |
1927 | } |
1928 | ||
1929 | if (!found_it) { | |
1930 | list_move_tail(&pmode->head, &connector->modes); | |
1931 | } | |
1932 | } | |
1933 | } | |
97e14fbe | 1934 | EXPORT_SYMBOL(drm_connector_list_update); |
1794d257 | 1935 | |
e08ab74b MR |
1936 | static int drm_mode_parse_cmdline_bpp(const char *str, char **end_ptr, |
1937 | struct drm_cmdline_mode *mode) | |
1938 | { | |
1939 | unsigned int bpp; | |
1940 | ||
1941 | if (str[0] != '-') | |
1942 | return -EINVAL; | |
1943 | ||
1944 | str++; | |
1945 | bpp = simple_strtol(str, end_ptr, 10); | |
1946 | if (*end_ptr == str) | |
1947 | return -EINVAL; | |
1948 | ||
1949 | mode->bpp = bpp; | |
1950 | mode->bpp_specified = true; | |
1951 | ||
1952 | return 0; | |
1953 | } | |
1954 | ||
1955 | static int drm_mode_parse_cmdline_refresh(const char *str, char **end_ptr, | |
1956 | struct drm_cmdline_mode *mode) | |
1957 | { | |
1958 | unsigned int refresh; | |
1959 | ||
1960 | if (str[0] != '@') | |
1961 | return -EINVAL; | |
1962 | ||
1963 | str++; | |
1964 | refresh = simple_strtol(str, end_ptr, 10); | |
1965 | if (*end_ptr == str) | |
1966 | return -EINVAL; | |
1967 | ||
1968 | mode->refresh = refresh; | |
1969 | mode->refresh_specified = true; | |
1970 | ||
1971 | return 0; | |
1972 | } | |
1973 | ||
1974 | static int drm_mode_parse_cmdline_extra(const char *str, int length, | |
325d0ab3 | 1975 | bool freestanding, |
c0898fca | 1976 | const struct drm_connector *connector, |
e08ab74b MR |
1977 | struct drm_cmdline_mode *mode) |
1978 | { | |
1979 | int i; | |
1980 | ||
1981 | for (i = 0; i < length; i++) { | |
1982 | switch (str[i]) { | |
1983 | case 'i': | |
325d0ab3 MR |
1984 | if (freestanding) |
1985 | return -EINVAL; | |
1986 | ||
e08ab74b MR |
1987 | mode->interlace = true; |
1988 | break; | |
1989 | case 'm': | |
325d0ab3 MR |
1990 | if (freestanding) |
1991 | return -EINVAL; | |
1992 | ||
e08ab74b MR |
1993 | mode->margins = true; |
1994 | break; | |
1995 | case 'D': | |
1996 | if (mode->force != DRM_FORCE_UNSPECIFIED) | |
1997 | return -EINVAL; | |
1998 | ||
1999 | if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) && | |
2000 | (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB)) | |
2001 | mode->force = DRM_FORCE_ON; | |
2002 | else | |
2003 | mode->force = DRM_FORCE_ON_DIGITAL; | |
2004 | break; | |
2005 | case 'd': | |
2006 | if (mode->force != DRM_FORCE_UNSPECIFIED) | |
2007 | return -EINVAL; | |
2008 | ||
2009 | mode->force = DRM_FORCE_OFF; | |
2010 | break; | |
2011 | case 'e': | |
2012 | if (mode->force != DRM_FORCE_UNSPECIFIED) | |
2013 | return -EINVAL; | |
2014 | ||
2015 | mode->force = DRM_FORCE_ON; | |
2016 | break; | |
2017 | default: | |
2018 | return -EINVAL; | |
2019 | } | |
2020 | } | |
2021 | ||
2022 | return 0; | |
2023 | } | |
2024 | ||
2025 | static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, | |
2026 | bool extras, | |
c0898fca | 2027 | const struct drm_connector *connector, |
e08ab74b MR |
2028 | struct drm_cmdline_mode *mode) |
2029 | { | |
2030 | const char *str_start = str; | |
2031 | bool rb = false, cvt = false; | |
2032 | int xres = 0, yres = 0; | |
2033 | int remaining, i; | |
2034 | char *end_ptr; | |
2035 | ||
2036 | xres = simple_strtol(str, &end_ptr, 10); | |
2037 | if (end_ptr == str) | |
2038 | return -EINVAL; | |
2039 | ||
2040 | if (end_ptr[0] != 'x') | |
2041 | return -EINVAL; | |
2042 | end_ptr++; | |
2043 | ||
2044 | str = end_ptr; | |
2045 | yres = simple_strtol(str, &end_ptr, 10); | |
2046 | if (end_ptr == str) | |
2047 | return -EINVAL; | |
2048 | ||
2049 | remaining = length - (end_ptr - str_start); | |
2050 | if (remaining < 0) | |
2051 | return -EINVAL; | |
2052 | ||
2053 | for (i = 0; i < remaining; i++) { | |
2054 | switch (end_ptr[i]) { | |
2055 | case 'M': | |
2056 | cvt = true; | |
2057 | break; | |
2058 | case 'R': | |
2059 | rb = true; | |
2060 | break; | |
2061 | default: | |
2062 | /* | |
2063 | * Try to pass that to our extras parsing | |
2064 | * function to handle the case where the | |
2065 | * extras are directly after the resolution | |
2066 | */ | |
2067 | if (extras) { | |
2068 | int ret = drm_mode_parse_cmdline_extra(end_ptr + i, | |
2069 | 1, | |
325d0ab3 | 2070 | false, |
e08ab74b MR |
2071 | connector, |
2072 | mode); | |
2073 | if (ret) | |
2074 | return ret; | |
2075 | } else { | |
2076 | return -EINVAL; | |
2077 | } | |
2078 | } | |
2079 | } | |
2080 | ||
2081 | mode->xres = xres; | |
2082 | mode->yres = yres; | |
2083 | mode->cvt = cvt; | |
2084 | mode->rb = rb; | |
2085 | ||
2086 | return 0; | |
2087 | } | |
2088 | ||
8582e244 HG |
2089 | static int drm_mode_parse_cmdline_int(const char *delim, unsigned int *int_ret) |
2090 | { | |
2091 | const char *value; | |
2092 | char *endp; | |
2093 | ||
2094 | /* | |
2095 | * delim must point to the '=', otherwise it is a syntax error and | |
0ae865ef | 2096 | * if delim points to the terminating zero, then delim + 1 will point |
8582e244 HG |
2097 | * past the end of the string. |
2098 | */ | |
2099 | if (*delim != '=') | |
2100 | return -EINVAL; | |
2101 | ||
2102 | value = delim + 1; | |
2103 | *int_ret = simple_strtol(value, &endp, 10); | |
2104 | ||
2105 | /* Make sure we have parsed something */ | |
2106 | if (endp == value) | |
2107 | return -EINVAL; | |
2108 | ||
2109 | return 0; | |
2110 | } | |
2111 | ||
4e7a4a6f HG |
2112 | static int drm_mode_parse_panel_orientation(const char *delim, |
2113 | struct drm_cmdline_mode *mode) | |
2114 | { | |
2115 | const char *value; | |
2116 | ||
2117 | if (*delim != '=') | |
2118 | return -EINVAL; | |
2119 | ||
2120 | value = delim + 1; | |
2121 | delim = strchr(value, ','); | |
2122 | if (!delim) | |
2123 | delim = value + strlen(value); | |
2124 | ||
2125 | if (!strncmp(value, "normal", delim - value)) | |
2126 | mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL; | |
2127 | else if (!strncmp(value, "upside_down", delim - value)) | |
2128 | mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; | |
2129 | else if (!strncmp(value, "left_side_up", delim - value)) | |
2130 | mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP; | |
2131 | else if (!strncmp(value, "right_side_up", delim - value)) | |
2132 | mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; | |
2133 | else | |
2134 | return -EINVAL; | |
2135 | ||
2136 | return 0; | |
2137 | } | |
2138 | ||
e691c999 MR |
2139 | static int drm_mode_parse_tv_mode(const char *delim, |
2140 | struct drm_cmdline_mode *mode) | |
2141 | { | |
2142 | const char *value; | |
2143 | int ret; | |
2144 | ||
2145 | if (*delim != '=') | |
2146 | return -EINVAL; | |
2147 | ||
2148 | value = delim + 1; | |
2149 | delim = strchr(value, ','); | |
2150 | if (!delim) | |
2151 | delim = value + strlen(value); | |
2152 | ||
2153 | ret = drm_get_tv_mode_from_name(value, delim - value); | |
2154 | if (ret < 0) | |
2155 | return ret; | |
2156 | ||
2157 | mode->tv_mode_specified = true; | |
2158 | mode->tv_mode = ret; | |
2159 | ||
2160 | return 0; | |
2161 | } | |
2162 | ||
739b200c | 2163 | static int drm_mode_parse_cmdline_options(const char *str, |
99e2716e | 2164 | bool freestanding, |
c0898fca | 2165 | const struct drm_connector *connector, |
1bf4e092 MR |
2166 | struct drm_cmdline_mode *mode) |
2167 | { | |
8582e244 | 2168 | unsigned int deg, margin, rotation = 0; |
739b200c | 2169 | const char *delim, *option, *sep; |
1bf4e092 | 2170 | |
739b200c HG |
2171 | option = str; |
2172 | do { | |
1bf4e092 MR |
2173 | delim = strchr(option, '='); |
2174 | if (!delim) { | |
2175 | delim = strchr(option, ','); | |
2176 | ||
2177 | if (!delim) | |
739b200c | 2178 | delim = option + strlen(option); |
1bf4e092 MR |
2179 | } |
2180 | ||
2181 | if (!strncmp(option, "rotate", delim - option)) { | |
8582e244 | 2182 | if (drm_mode_parse_cmdline_int(delim, °)) |
1bf4e092 MR |
2183 | return -EINVAL; |
2184 | ||
2185 | switch (deg) { | |
2186 | case 0: | |
2187 | rotation |= DRM_MODE_ROTATE_0; | |
2188 | break; | |
2189 | ||
2190 | case 90: | |
2191 | rotation |= DRM_MODE_ROTATE_90; | |
2192 | break; | |
2193 | ||
2194 | case 180: | |
2195 | rotation |= DRM_MODE_ROTATE_180; | |
2196 | break; | |
2197 | ||
2198 | case 270: | |
2199 | rotation |= DRM_MODE_ROTATE_270; | |
2200 | break; | |
2201 | ||
2202 | default: | |
2203 | return -EINVAL; | |
2204 | } | |
2205 | } else if (!strncmp(option, "reflect_x", delim - option)) { | |
2206 | rotation |= DRM_MODE_REFLECT_X; | |
1bf4e092 MR |
2207 | } else if (!strncmp(option, "reflect_y", delim - option)) { |
2208 | rotation |= DRM_MODE_REFLECT_Y; | |
3d46a300 | 2209 | } else if (!strncmp(option, "margin_right", delim - option)) { |
8582e244 | 2210 | if (drm_mode_parse_cmdline_int(delim, &margin)) |
3d46a300 MR |
2211 | return -EINVAL; |
2212 | ||
2213 | mode->tv_margins.right = margin; | |
2214 | } else if (!strncmp(option, "margin_left", delim - option)) { | |
8582e244 | 2215 | if (drm_mode_parse_cmdline_int(delim, &margin)) |
3d46a300 MR |
2216 | return -EINVAL; |
2217 | ||
2218 | mode->tv_margins.left = margin; | |
2219 | } else if (!strncmp(option, "margin_top", delim - option)) { | |
8582e244 | 2220 | if (drm_mode_parse_cmdline_int(delim, &margin)) |
3d46a300 MR |
2221 | return -EINVAL; |
2222 | ||
2223 | mode->tv_margins.top = margin; | |
2224 | } else if (!strncmp(option, "margin_bottom", delim - option)) { | |
8582e244 | 2225 | if (drm_mode_parse_cmdline_int(delim, &margin)) |
3d46a300 MR |
2226 | return -EINVAL; |
2227 | ||
2228 | mode->tv_margins.bottom = margin; | |
4e7a4a6f HG |
2229 | } else if (!strncmp(option, "panel_orientation", delim - option)) { |
2230 | if (drm_mode_parse_panel_orientation(delim, mode)) | |
2231 | return -EINVAL; | |
e691c999 MR |
2232 | } else if (!strncmp(option, "tv_mode", delim - option)) { |
2233 | if (drm_mode_parse_tv_mode(delim, mode)) | |
2234 | return -EINVAL; | |
1bf4e092 MR |
2235 | } else { |
2236 | return -EINVAL; | |
2237 | } | |
739b200c HG |
2238 | sep = strchr(delim, ','); |
2239 | option = sep + 1; | |
2240 | } while (sep); | |
1bf4e092 | 2241 | |
99e2716e HG |
2242 | if (rotation && freestanding) |
2243 | return -EINVAL; | |
2244 | ||
e6980a72 SG |
2245 | if (!(rotation & DRM_MODE_ROTATE_MASK)) |
2246 | rotation |= DRM_MODE_ROTATE_0; | |
2247 | ||
2248 | /* Make sure there is exactly one rotation defined */ | |
2249 | if (!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK)) | |
2250 | return -EINVAL; | |
2251 | ||
1bf4e092 MR |
2252 | mode->rotation_reflection = rotation; |
2253 | ||
2254 | return 0; | |
2255 | } | |
2256 | ||
a7ab1553 MR |
2257 | struct drm_named_mode { |
2258 | const char *name; | |
0e308efe | 2259 | unsigned int pixel_clock_khz; |
00dcc4a2 MR |
2260 | unsigned int xres; |
2261 | unsigned int yres; | |
2262 | unsigned int flags; | |
e691c999 | 2263 | unsigned int tv_mode; |
a7ab1553 MR |
2264 | }; |
2265 | ||
e691c999 | 2266 | #define NAMED_MODE(_name, _pclk, _x, _y, _flags, _mode) \ |
00dcc4a2 MR |
2267 | { \ |
2268 | .name = _name, \ | |
0e308efe | 2269 | .pixel_clock_khz = _pclk, \ |
00dcc4a2 MR |
2270 | .xres = _x, \ |
2271 | .yres = _y, \ | |
2272 | .flags = _flags, \ | |
e691c999 | 2273 | .tv_mode = _mode, \ |
00dcc4a2 MR |
2274 | } |
2275 | ||
a7ab1553 | 2276 | static const struct drm_named_mode drm_named_modes[] = { |
e691c999 | 2277 | NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC), |
0740ac38 | 2278 | NAMED_MODE("NTSC-J", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC_J), |
e691c999 | 2279 | NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL), |
0740ac38 | 2280 | NAMED_MODE("PAL-M", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL_M), |
37641379 MR |
2281 | }; |
2282 | ||
a631bf30 MR |
2283 | static int drm_mode_parse_cmdline_named_mode(const char *name, |
2284 | unsigned int name_end, | |
2285 | struct drm_cmdline_mode *cmdline_mode) | |
2286 | { | |
2287 | unsigned int i; | |
2288 | ||
2289 | if (!name_end) | |
2290 | return 0; | |
2291 | ||
2292 | /* If the name starts with a digit, it's not a named mode */ | |
2293 | if (isdigit(name[0])) | |
2294 | return 0; | |
2295 | ||
2296 | /* | |
2297 | * If there's an equal sign in the name, the command-line | |
2298 | * contains only an option and no mode. | |
2299 | */ | |
2300 | if (strnchr(name, name_end, '=')) | |
2301 | return 0; | |
2302 | ||
2303 | /* The connection status extras can be set without a mode. */ | |
2304 | if (name_end == 1 && | |
2305 | (name[0] == 'd' || name[0] == 'D' || name[0] == 'e')) | |
2306 | return 0; | |
2307 | ||
2308 | /* | |
2309 | * We're sure we're a named mode at this point, iterate over the | |
2310 | * list of modes we're aware of. | |
2311 | */ | |
a7ab1553 MR |
2312 | for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) { |
2313 | const struct drm_named_mode *mode = &drm_named_modes[i]; | |
a631bf30 MR |
2314 | int ret; |
2315 | ||
a7ab1553 | 2316 | ret = str_has_prefix(name, mode->name); |
a631bf30 MR |
2317 | if (ret != name_end) |
2318 | continue; | |
2319 | ||
0f9aa074 | 2320 | strscpy(cmdline_mode->name, mode->name, sizeof(cmdline_mode->name)); |
0e308efe | 2321 | cmdline_mode->pixel_clock = mode->pixel_clock_khz; |
00dcc4a2 MR |
2322 | cmdline_mode->xres = mode->xres; |
2323 | cmdline_mode->yres = mode->yres; | |
2324 | cmdline_mode->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); | |
e691c999 MR |
2325 | cmdline_mode->tv_mode = mode->tv_mode; |
2326 | cmdline_mode->tv_mode_specified = true; | |
a631bf30 MR |
2327 | cmdline_mode->specified = true; |
2328 | ||
2329 | return 1; | |
2330 | } | |
2331 | ||
2332 | return -EINVAL; | |
2333 | } | |
2334 | ||
1794d257 | 2335 | /** |
f5aabb97 SV |
2336 | * drm_mode_parse_command_line_for_connector - parse command line modeline for connector |
2337 | * @mode_option: optional per connector mode option | |
2338 | * @connector: connector to parse modeline for | |
2339 | * @mode: preallocated drm_cmdline_mode structure to fill out | |
2340 | * | |
2341 | * This parses @mode_option command line modeline for modes and options to | |
1e84dadb | 2342 | * configure the connector. |
1794d257 | 2343 | * |
f5aabb97 | 2344 | * This uses the same parameters as the fb modedb.c, except for an extra |
dbd124f0 | 2345 | * force-enable, force-enable-digital and force-disable bit at the end:: |
1794d257 | 2346 | * |
dbd124f0 | 2347 | * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] |
1794d257 | 2348 | * |
1bf4e092 MR |
2349 | * Additionals options can be provided following the mode, using a comma to |
2350 | * separate each option. Valid options can be found in | |
bff9e34c | 2351 | * Documentation/fb/modedb.rst. |
1bf4e092 | 2352 | * |
f5aabb97 | 2353 | * The intermediate drm_cmdline_mode structure is required to store additional |
2a97acd6 | 2354 | * options from the command line modline like the force-enable/disable flag. |
f5aabb97 SV |
2355 | * |
2356 | * Returns: | |
2357 | * True if a valid modeline has been parsed, false otherwise. | |
1794d257 CW |
2358 | */ |
2359 | bool drm_mode_parse_command_line_for_connector(const char *mode_option, | |
c0898fca | 2360 | const struct drm_connector *connector, |
1794d257 CW |
2361 | struct drm_cmdline_mode *mode) |
2362 | { | |
2363 | const char *name; | |
7b1cce76 | 2364 | bool freestanding = false, parse_extras = false; |
1bf4e092 | 2365 | unsigned int bpp_off = 0, refresh_off = 0, options_off = 0; |
e08ab74b | 2366 | unsigned int mode_end = 0; |
83e14ea3 HG |
2367 | const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL; |
2368 | const char *options_ptr = NULL; | |
e08ab74b | 2369 | char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL; |
a631bf30 | 2370 | int len, ret; |
1794d257 | 2371 | |
d1fe276b | 2372 | memset(mode, 0, sizeof(*mode)); |
4e7a4a6f HG |
2373 | mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; |
2374 | ||
d1fe276b | 2375 | if (!mode_option) |
1794d257 | 2376 | return false; |
1794d257 CW |
2377 | |
2378 | name = mode_option; | |
04fee895 | 2379 | |
90c258ba MR |
2380 | /* Locate the start of named options */ |
2381 | options_ptr = strchr(name, ','); | |
2382 | if (options_ptr) | |
2383 | options_off = options_ptr - name; | |
2384 | else | |
2385 | options_off = strlen(name); | |
2386 | ||
e08ab74b | 2387 | /* Try to locate the bpp and refresh specifiers, if any */ |
90c258ba | 2388 | bpp_ptr = strnchr(name, options_off, '-'); |
8b6e28ea GU |
2389 | while (bpp_ptr && !isdigit(bpp_ptr[1])) |
2390 | bpp_ptr = strnchr(bpp_ptr + 1, options_off, '-'); | |
6a2d1637 | 2391 | if (bpp_ptr) |
e08ab74b | 2392 | bpp_off = bpp_ptr - name; |
04fee895 | 2393 | |
90c258ba | 2394 | refresh_ptr = strnchr(name, options_off, '@'); |
7b1cce76 | 2395 | if (refresh_ptr) |
e08ab74b | 2396 | refresh_off = refresh_ptr - name; |
04fee895 | 2397 | |
e08ab74b | 2398 | /* Locate the end of the name / resolution, and parse it */ |
1bf4e092 | 2399 | if (bpp_ptr) { |
e08ab74b MR |
2400 | mode_end = bpp_off; |
2401 | } else if (refresh_ptr) { | |
2402 | mode_end = refresh_off; | |
1bf4e092 MR |
2403 | } else if (options_ptr) { |
2404 | mode_end = options_off; | |
cfb0881b | 2405 | parse_extras = true; |
e08ab74b MR |
2406 | } else { |
2407 | mode_end = strlen(name); | |
2408 | parse_extras = true; | |
1794d257 | 2409 | } |
e08ab74b | 2410 | |
a631bf30 MR |
2411 | if (!mode_end) |
2412 | return false; | |
37641379 | 2413 | |
a631bf30 MR |
2414 | ret = drm_mode_parse_cmdline_named_mode(name, mode_end, mode); |
2415 | if (ret < 0) | |
2416 | return false; | |
2417 | ||
2418 | /* | |
2419 | * Having a mode that starts by a letter (and thus is named) and | |
2420 | * an at-sign (used to specify a refresh rate) is disallowed. | |
2421 | */ | |
2422 | if (ret && refresh_ptr) | |
2423 | return false; | |
37641379 | 2424 | |
7b1cce76 HG |
2425 | /* No named mode? Check for a normal mode argument, e.g. 1024x768 */ |
2426 | if (!mode->specified && isdigit(name[0])) { | |
3aeeb13d MR |
2427 | ret = drm_mode_parse_cmdline_res_mode(name, mode_end, |
2428 | parse_extras, | |
2429 | connector, | |
2430 | mode); | |
2431 | if (ret) | |
2432 | return false; | |
7b1cce76 HG |
2433 | |
2434 | mode->specified = true; | |
2435 | } | |
2436 | ||
2437 | /* No mode? Check for freestanding extras and/or options */ | |
2438 | if (!mode->specified) { | |
2439 | unsigned int len = strlen(mode_option); | |
2440 | ||
2441 | if (bpp_ptr || refresh_ptr) | |
2442 | return false; /* syntax error */ | |
2443 | ||
2444 | if (len == 1 || (len >= 2 && mode_option[1] == ',')) | |
2445 | extra_ptr = mode_option; | |
2446 | else | |
2447 | options_ptr = mode_option - 1; | |
2448 | ||
2449 | freestanding = true; | |
3aeeb13d | 2450 | } |
04fee895 | 2451 | |
e08ab74b MR |
2452 | if (bpp_ptr) { |
2453 | ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode); | |
2454 | if (ret) | |
2455 | return false; | |
6a2d1637 HG |
2456 | |
2457 | mode->bpp_specified = true; | |
1794d257 CW |
2458 | } |
2459 | ||
e08ab74b MR |
2460 | if (refresh_ptr) { |
2461 | ret = drm_mode_parse_cmdline_refresh(refresh_ptr, | |
2462 | &refresh_end_ptr, mode); | |
2463 | if (ret) | |
2464 | return false; | |
6a2d1637 HG |
2465 | |
2466 | mode->refresh_specified = true; | |
1794d257 CW |
2467 | } |
2468 | ||
e08ab74b MR |
2469 | /* |
2470 | * Locate the end of the bpp / refresh, and parse the extras | |
2471 | * if relevant | |
2472 | */ | |
2473 | if (bpp_ptr && refresh_ptr) | |
2474 | extra_ptr = max(bpp_end_ptr, refresh_end_ptr); | |
2475 | else if (bpp_ptr) | |
2476 | extra_ptr = bpp_end_ptr; | |
2477 | else if (refresh_ptr) | |
2478 | extra_ptr = refresh_end_ptr; | |
2479 | ||
c2ed3e94 HG |
2480 | if (extra_ptr) { |
2481 | if (options_ptr) | |
2482 | len = options_ptr - extra_ptr; | |
2483 | else | |
2484 | len = strlen(extra_ptr); | |
e08ab74b | 2485 | |
7b1cce76 | 2486 | ret = drm_mode_parse_cmdline_extra(extra_ptr, len, freestanding, |
1bf4e092 MR |
2487 | connector, mode); |
2488 | if (ret) | |
2489 | return false; | |
2490 | } | |
3aeeb13d | 2491 | |
1bf4e092 | 2492 | if (options_ptr) { |
739b200c | 2493 | ret = drm_mode_parse_cmdline_options(options_ptr + 1, |
7b1cce76 | 2494 | freestanding, |
1bf4e092 MR |
2495 | connector, mode); |
2496 | if (ret) | |
2497 | return false; | |
1794d257 | 2498 | } |
1794d257 CW |
2499 | |
2500 | return true; | |
2501 | } | |
2502 | EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector); | |
2503 | ||
fedcaf72 MR |
2504 | static struct drm_display_mode *drm_named_mode(struct drm_device *dev, |
2505 | struct drm_cmdline_mode *cmd) | |
2506 | { | |
2507 | unsigned int i; | |
2508 | ||
2509 | for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) { | |
2510 | const struct drm_named_mode *named_mode = &drm_named_modes[i]; | |
2511 | ||
2512 | if (strcmp(cmd->name, named_mode->name)) | |
2513 | continue; | |
2514 | ||
2515 | if (!cmd->tv_mode_specified) | |
2516 | continue; | |
2517 | ||
2518 | return drm_analog_tv_mode(dev, | |
2519 | named_mode->tv_mode, | |
2520 | named_mode->pixel_clock_khz * 1000, | |
2521 | named_mode->xres, | |
2522 | named_mode->yres, | |
2523 | named_mode->flags & DRM_MODE_FLAG_INTERLACE); | |
2524 | } | |
2525 | ||
2526 | return NULL; | |
2527 | } | |
2528 | ||
f5aabb97 SV |
2529 | /** |
2530 | * drm_mode_create_from_cmdline_mode - convert a command line modeline into a DRM display mode | |
2531 | * @dev: DRM device to create the new mode for | |
2532 | * @cmd: input command line modeline | |
2533 | * | |
2534 | * Returns: | |
2535 | * Pointer to converted mode on success, NULL on error. | |
2536 | */ | |
1794d257 CW |
2537 | struct drm_display_mode * |
2538 | drm_mode_create_from_cmdline_mode(struct drm_device *dev, | |
2539 | struct drm_cmdline_mode *cmd) | |
2540 | { | |
2541 | struct drm_display_mode *mode; | |
2542 | ||
19a9a0ef VS |
2543 | if (cmd->xres == 0 || cmd->yres == 0) |
2544 | return NULL; | |
2545 | ||
fedcaf72 MR |
2546 | if (strlen(cmd->name)) |
2547 | mode = drm_named_mode(dev, cmd); | |
2548 | else if (cmd->cvt) | |
1794d257 CW |
2549 | mode = drm_cvt_mode(dev, |
2550 | cmd->xres, cmd->yres, | |
2551 | cmd->refresh_specified ? cmd->refresh : 60, | |
2552 | cmd->rb, cmd->interlace, | |
2553 | cmd->margins); | |
2554 | else | |
2555 | mode = drm_gtf_mode(dev, | |
2556 | cmd->xres, cmd->yres, | |
2557 | cmd->refresh_specified ? cmd->refresh : 60, | |
2558 | cmd->interlace, | |
2559 | cmd->margins); | |
2560 | if (!mode) | |
2561 | return NULL; | |
2562 | ||
eaf99c74 | 2563 | mode->type |= DRM_MODE_TYPE_USERDEF; |
fdf35a6b | 2564 | /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */ |
969218fe TI |
2565 | if (cmd->xres == 1366) |
2566 | drm_mode_fixup_1366x768(mode); | |
1794d257 CW |
2567 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
2568 | return mode; | |
2569 | } | |
2570 | EXPORT_SYMBOL(drm_mode_create_from_cmdline_mode); | |
934a8a89 DS |
2571 | |
2572 | /** | |
e9d2871f | 2573 | * drm_mode_convert_to_umode - convert a drm_display_mode into a modeinfo |
934a8a89 DS |
2574 | * @out: drm_mode_modeinfo struct to return to the user |
2575 | * @in: drm_display_mode to use | |
2576 | * | |
2577 | * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to | |
2578 | * the user. | |
2579 | */ | |
2580 | void drm_mode_convert_to_umode(struct drm_mode_modeinfo *out, | |
2581 | const struct drm_display_mode *in) | |
2582 | { | |
934a8a89 DS |
2583 | out->clock = in->clock; |
2584 | out->hdisplay = in->hdisplay; | |
2585 | out->hsync_start = in->hsync_start; | |
2586 | out->hsync_end = in->hsync_end; | |
2587 | out->htotal = in->htotal; | |
2588 | out->hskew = in->hskew; | |
2589 | out->vdisplay = in->vdisplay; | |
2590 | out->vsync_start = in->vsync_start; | |
2591 | out->vsync_end = in->vsync_end; | |
2592 | out->vtotal = in->vtotal; | |
2593 | out->vscan = in->vscan; | |
0425662f | 2594 | out->vrefresh = drm_mode_vrefresh(in); |
934a8a89 DS |
2595 | out->flags = in->flags; |
2596 | out->type = in->type; | |
222ec161 SS |
2597 | |
2598 | switch (in->picture_aspect_ratio) { | |
2599 | case HDMI_PICTURE_ASPECT_4_3: | |
2600 | out->flags |= DRM_MODE_FLAG_PIC_AR_4_3; | |
2601 | break; | |
2602 | case HDMI_PICTURE_ASPECT_16_9: | |
2603 | out->flags |= DRM_MODE_FLAG_PIC_AR_16_9; | |
2604 | break; | |
900aa8ad SS |
2605 | case HDMI_PICTURE_ASPECT_64_27: |
2606 | out->flags |= DRM_MODE_FLAG_PIC_AR_64_27; | |
2607 | break; | |
2608 | case HDMI_PICTURE_ASPECT_256_135: | |
2609 | out->flags |= DRM_MODE_FLAG_PIC_AR_256_135; | |
2610 | break; | |
222ec161 | 2611 | default: |
9f396ab4 VS |
2612 | WARN(1, "Invalid aspect ratio (0%x) on mode\n", |
2613 | in->picture_aspect_ratio); | |
df561f66 | 2614 | fallthrough; |
9f396ab4 | 2615 | case HDMI_PICTURE_ASPECT_NONE: |
222ec161 SS |
2616 | out->flags |= DRM_MODE_FLAG_PIC_AR_NONE; |
2617 | break; | |
2618 | } | |
2619 | ||
3b2894c9 | 2620 | strscpy_pad(out->name, in->name, sizeof(out->name)); |
934a8a89 DS |
2621 | } |
2622 | ||
2623 | /** | |
e9d2871f | 2624 | * drm_mode_convert_umode - convert a modeinfo into a drm_display_mode |
75a655e0 | 2625 | * @dev: drm device |
934a8a89 DS |
2626 | * @out: drm_display_mode to return to the user |
2627 | * @in: drm_mode_modeinfo to use | |
2628 | * | |
2629 | * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to | |
2630 | * the caller. | |
2631 | * | |
2632 | * Returns: | |
2633 | * Zero on success, negative errno on failure. | |
2634 | */ | |
75a655e0 VS |
2635 | int drm_mode_convert_umode(struct drm_device *dev, |
2636 | struct drm_display_mode *out, | |
934a8a89 DS |
2637 | const struct drm_mode_modeinfo *in) |
2638 | { | |
df550548 VS |
2639 | if (in->clock > INT_MAX || in->vrefresh > INT_MAX) |
2640 | return -ERANGE; | |
934a8a89 | 2641 | |
934a8a89 DS |
2642 | out->clock = in->clock; |
2643 | out->hdisplay = in->hdisplay; | |
2644 | out->hsync_start = in->hsync_start; | |
2645 | out->hsync_end = in->hsync_end; | |
2646 | out->htotal = in->htotal; | |
2647 | out->hskew = in->hskew; | |
2648 | out->vdisplay = in->vdisplay; | |
2649 | out->vsync_start = in->vsync_start; | |
2650 | out->vsync_end = in->vsync_end; | |
2651 | out->vtotal = in->vtotal; | |
2652 | out->vscan = in->vscan; | |
934a8a89 | 2653 | out->flags = in->flags; |
a01c4773 VS |
2654 | /* |
2655 | * Old xf86-video-vmware (possibly others too) used to | |
0ae865ef | 2656 | * leave 'type' uninitialized. Just ignore any bits we |
a01c4773 VS |
2657 | * don't like. It's a just hint after all, and more |
2658 | * useful for the kernel->userspace direction anyway. | |
2659 | */ | |
2660 | out->type = in->type & DRM_MODE_TYPE_ALL; | |
3b2894c9 | 2661 | strscpy_pad(out->name, in->name, sizeof(out->name)); |
934a8a89 | 2662 | |
222ec161 SS |
2663 | /* Clearing picture aspect ratio bits from out flags, |
2664 | * as the aspect-ratio information is not stored in | |
2665 | * flags for kernel-mode, but in picture_aspect_ratio. | |
2666 | */ | |
2667 | out->flags &= ~DRM_MODE_FLAG_PIC_AR_MASK; | |
2668 | ||
2669 | switch (in->flags & DRM_MODE_FLAG_PIC_AR_MASK) { | |
2670 | case DRM_MODE_FLAG_PIC_AR_4_3: | |
700710e9 | 2671 | out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3; |
222ec161 SS |
2672 | break; |
2673 | case DRM_MODE_FLAG_PIC_AR_16_9: | |
700710e9 | 2674 | out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9; |
222ec161 | 2675 | break; |
900aa8ad | 2676 | case DRM_MODE_FLAG_PIC_AR_64_27: |
700710e9 | 2677 | out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27; |
900aa8ad SS |
2678 | break; |
2679 | case DRM_MODE_FLAG_PIC_AR_256_135: | |
700710e9 | 2680 | out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135; |
900aa8ad | 2681 | break; |
993a8067 | 2682 | case DRM_MODE_FLAG_PIC_AR_NONE: |
222ec161 SS |
2683 | out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE; |
2684 | break; | |
993a8067 VS |
2685 | default: |
2686 | return -EINVAL; | |
222ec161 SS |
2687 | } |
2688 | ||
75a655e0 | 2689 | out->status = drm_mode_validate_driver(dev, out); |
934a8a89 | 2690 | if (out->status != MODE_OK) |
df550548 | 2691 | return -EINVAL; |
934a8a89 | 2692 | |
b201e743 TV |
2693 | drm_mode_set_crtcinfo(out, CRTC_INTERLACE_HALVE_V); |
2694 | ||
df550548 | 2695 | return 0; |
f03d8ede | 2696 | } |
2570fe25 SS |
2697 | |
2698 | /** | |
2699 | * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420 | |
2700 | * output format | |
2701 | * | |
8d0873a2 | 2702 | * @display: display under action |
2570fe25 SS |
2703 | * @mode: video mode to be tested. |
2704 | * | |
2705 | * Returns: | |
2706 | * true if the mode can be supported in YCBCR420 format | |
2707 | * false if not. | |
2708 | */ | |
2709 | bool drm_mode_is_420_only(const struct drm_display_info *display, | |
2710 | const struct drm_display_mode *mode) | |
2711 | { | |
2712 | u8 vic = drm_match_cea_mode(mode); | |
2713 | ||
2714 | return test_bit(vic, display->hdmi.y420_vdb_modes); | |
2715 | } | |
2716 | EXPORT_SYMBOL(drm_mode_is_420_only); | |
2717 | ||
2718 | /** | |
2719 | * drm_mode_is_420_also - if a given videomode can be supported in YCBCR420 | |
2720 | * output format also (along with RGB/YCBCR444/422) | |
2721 | * | |
2722 | * @display: display under action. | |
2723 | * @mode: video mode to be tested. | |
2724 | * | |
2725 | * Returns: | |
2726 | * true if the mode can be support YCBCR420 format | |
2727 | * false if not. | |
2728 | */ | |
2729 | bool drm_mode_is_420_also(const struct drm_display_info *display, | |
2730 | const struct drm_display_mode *mode) | |
2731 | { | |
2732 | u8 vic = drm_match_cea_mode(mode); | |
2733 | ||
2734 | return test_bit(vic, display->hdmi.y420_cmdb_modes); | |
2735 | } | |
2736 | EXPORT_SYMBOL(drm_mode_is_420_also); | |
2737 | /** | |
2738 | * drm_mode_is_420 - if a given videomode can be supported in YCBCR420 | |
2739 | * output format | |
2740 | * | |
2741 | * @display: display under action. | |
2742 | * @mode: video mode to be tested. | |
2743 | * | |
2744 | * Returns: | |
2745 | * true if the mode can be supported in YCBCR420 format | |
2746 | * false if not. | |
2747 | */ | |
2748 | bool drm_mode_is_420(const struct drm_display_info *display, | |
2749 | const struct drm_display_mode *mode) | |
2750 | { | |
2751 | return drm_mode_is_420_only(display, mode) || | |
2752 | drm_mode_is_420_also(display, mode); | |
2753 | } | |
2754 | EXPORT_SYMBOL(drm_mode_is_420); |