1 | /* |
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> |
6 | * Copyright 2005-2006 Luc Verhaegen |
7 | * Copyright (c) 2001, Andy Ritger aritger@nvidia.com |
8 | * |
9 | * Permission is hereby granted, free of charge, to any person obtaining a |
10 | * copy of this software and associated documentation files (the "Software"), |
11 | * to deal in the Software without restriction, including without limitation |
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
13 | * and/or sell copies of the Software, and to permit persons to whom the |
14 | * Software is furnished to do so, subject to the following conditions: |
15 | * |
16 | * The above copyright notice and this permission notice shall be included in |
17 | * all copies or substantial portions of the Software. |
18 | * |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
22 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
25 | * OTHER DEALINGS IN THE SOFTWARE. |
26 | * |
27 | * Except as contained in this notice, the name of the copyright holder(s) |
28 | * and author(s) shall not be used in advertising or otherwise to promote |
29 | * the sale, use or other dealings in this Software without prior written |
30 | * authorization from the copyright holder(s) and author(s). |
31 | */ |
32 | |
33 | #include <linux/ctype.h> |
34 | #include <linux/export.h> |
35 | #include <linux/fb.h> /* for KHZ2PICOS() */ |
36 | #include <linux/list.h> |
37 | #include <linux/list_sort.h> |
38 | #include <linux/of.h> |
39 | |
40 | #include <video/of_display_timing.h> |
41 | #include <video/of_videomode.h> |
42 | #include <video/videomode.h> |
43 | |
44 | #include <drm/drm_crtc.h> |
45 | #include <drm/drm_device.h> |
46 | #include <drm/drm_edid.h> |
47 | #include <drm/drm_modes.h> |
48 | #include <drm/drm_print.h> |
49 | |
50 | #include "drm_crtc_internal.h" |
51 | |
52 | /** |
53 | * drm_mode_debug_printmodeline - print a mode to dmesg |
54 | * @mode: mode to print |
55 | * |
56 | * Describe @mode using DRM_DEBUG. |
57 | */ |
58 | void drm_mode_debug_printmodeline(const struct drm_display_mode *mode) |
59 | { |
60 | DRM_DEBUG_KMS("Modeline " DRM_MODE_FMT "\n" , DRM_MODE_ARG(mode)); |
61 | } |
62 | EXPORT_SYMBOL(drm_mode_debug_printmodeline); |
63 | |
64 | /** |
65 | * drm_mode_create - create a new display mode |
66 | * @dev: DRM device |
67 | * |
68 | * Create a new, cleared drm_display_mode with kzalloc, allocate an ID for it |
69 | * and return it. |
70 | * |
71 | * Returns: |
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(size: sizeof(struct drm_display_mode), GFP_KERNEL); |
79 | if (!nmode) |
80 | return NULL; |
81 | |
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 | * |
91 | * Release @mode's unique ID, then free it @mode structure itself using kfree. |
92 | */ |
93 | void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) |
94 | { |
95 | if (!mode) |
96 | return; |
97 | |
98 | kfree(objp: mode); |
99 | } |
100 | EXPORT_SYMBOL(drm_mode_destroy); |
101 | |
102 | /** |
103 | * drm_mode_probed_add - add a mode to a connector's probed_mode list |
104 | * @connector: connector the new mode |
105 | * @mode: mode data |
106 | * |
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. |
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(new: &mode->head, head: &connector->probed_modes); |
117 | } |
118 | EXPORT_SYMBOL(drm_mode_probed_add); |
119 | |
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 | params: &tv_modes_parameters[analog], |
584 | pixel_clock_hz, hactive: hdisplay, vactive: 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 | |
596 | /** |
597 | * drm_cvt_mode -create a modeline based on the CVT algorithm |
598 | * @dev: drm device |
599 | * @hdisplay: hdisplay size |
600 | * @vdisplay: vdisplay size |
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) |
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 |
610 | * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls |
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. |
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. |
619 | */ |
620 | struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, |
621 | int vdisplay, int vrefresh, |
622 | bool reduced, bool interlaced, bool margins) |
623 | { |
624 | #define HV_FACTOR 1000 |
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; |
636 | unsigned int vfieldrate, hperiod; |
637 | int hdisplay_rnd, hmargin, vdisplay_rnd, vmargin, vsync; |
638 | int interlace; |
639 | u64 tmp; |
640 | |
641 | if (!hdisplay || !vdisplay) |
642 | return NULL; |
643 | |
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 | |
684 | drm_mode->vdisplay = vdisplay + 2 * vmargin; |
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; |
716 | int vsyncandback_porch, __maybe_unused vback_porch, hblank; |
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); |
756 | /* 14. find the total pixels per line */ |
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; |
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; |
798 | } |
799 | /* 15/13. Find pixel clock frequency (kHz for xf86) */ |
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; |
805 | /* 18/16. Find actual vertical frame frequency */ |
806 | /* ignore - just set the mode flag for interlaced */ |
807 | if (interlaced) { |
808 | drm_mode->vtotal *= 2; |
809 | drm_mode->flags |= DRM_MODE_FLAG_INTERLACE; |
810 | } |
811 | /* Fill the mode line name */ |
812 | drm_mode_set_name(mode: 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); |
819 | |
820 | return drm_mode; |
821 | } |
822 | EXPORT_SYMBOL(drm_cvt_mode); |
823 | |
824 | /** |
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 |
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 |
836 | * |
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. |
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. |
844 | */ |
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, */ |
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 |
861 | /* C' and M' are part of the Blanking Duty Cycle computation */ |
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) |
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; |
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; |
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 | |
879 | if (!hdisplay || !vdisplay) |
880 | return NULL; |
881 | |
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 | |
1001 | if (interlaced) { |
1002 | drm_mode->vtotal *= 2; |
1003 | drm_mode->flags |= DRM_MODE_FLAG_INTERLACE; |
1004 | } |
1005 | |
1006 | drm_mode_set_name(mode: drm_mode); |
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; |
1011 | |
1012 | return drm_mode; |
1013 | } |
1014 | EXPORT_SYMBOL(drm_gtf_mode_complex); |
1015 | |
1016 | /** |
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 |
1024 | * |
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: |
1029 | * |
1030 | * GTF Spreadsheet by Andy Morrish (1/5/97) |
1031 | * available at https://www.vesa.org |
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 | * |
1038 | * Standard GTF parameters:: |
1039 | * |
1040 | * M = 600 |
1041 | * C = 40 |
1042 | * K = 128 |
1043 | * J = 20 |
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. |
1049 | */ |
1050 | struct drm_display_mode * |
1051 | drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, |
1052 | bool interlaced, int margins) |
1053 | { |
1054 | return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh, |
1055 | interlaced, margins, |
1056 | 600, 40 * 2, 128, 20 * 2); |
1057 | } |
1058 | EXPORT_SYMBOL(drm_gtf_mode); |
1059 | |
1060 | #ifdef CONFIG_VIDEOMODE_HELPERS |
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 | */ |
1068 | void drm_display_mode_from_videomode(const struct videomode *vm, |
1069 | struct drm_display_mode *dmode) |
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; |
1084 | if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) |
1085 | dmode->flags |= DRM_MODE_FLAG_PHSYNC; |
1086 | else if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW) |
1087 | dmode->flags |= DRM_MODE_FLAG_NHSYNC; |
1088 | if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) |
1089 | dmode->flags |= DRM_MODE_FLAG_PVSYNC; |
1090 | else if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW) |
1091 | dmode->flags |= DRM_MODE_FLAG_NVSYNC; |
1092 | if (vm->flags & DISPLAY_FLAGS_INTERLACED) |
1093 | dmode->flags |= DRM_MODE_FLAG_INTERLACE; |
1094 | if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN) |
1095 | dmode->flags |= DRM_MODE_FLAG_DBLSCAN; |
1096 | if (vm->flags & DISPLAY_FLAGS_DOUBLECLK) |
1097 | dmode->flags |= DRM_MODE_FLAG_DBLCLK; |
1098 | drm_mode_set_name(mode: dmode); |
1099 | } |
1100 | EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); |
1101 | |
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 | |
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 |
1146 | * @bus_flags: information about pixelclk, sync and DE polarity will be stored |
1147 | * here |
1148 | * |
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 |
1151 | * found in @vm |
1152 | */ |
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) |
1157 | *bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; |
1158 | if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) |
1159 | *bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; |
1160 | |
1161 | if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE) |
1162 | *bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE; |
1163 | if (vm->flags & DISPLAY_FLAGS_SYNC_NEGEDGE) |
1164 | *bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE; |
1165 | |
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 | |
1173 | #ifdef CONFIG_OF |
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 |
1178 | * @bus_flags: information about pixelclk, sync and DE polarity |
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. |
1184 | * |
1185 | * Returns: |
1186 | * 0 on success, a negative errno code when no of videomode node was found. |
1187 | */ |
1188 | int of_get_drm_display_mode(struct device_node *np, |
1189 | struct drm_display_mode *dmode, u32 *bus_flags, |
1190 | int index) |
1191 | { |
1192 | struct videomode vm; |
1193 | int ret; |
1194 | |
1195 | ret = of_get_videomode(np, vm: &vm, index); |
1196 | if (ret) |
1197 | return ret; |
1198 | |
1199 | drm_display_mode_from_videomode(&vm, dmode); |
1200 | if (bus_flags) |
1201 | drm_bus_flags_from_videomode(&vm, bus_flags); |
1202 | |
1203 | pr_debug("%pOF: got %dx%d display mode\n" , |
1204 | np, vm.hactive, vm.vactive); |
1205 | drm_mode_debug_printmodeline(dmode); |
1206 | |
1207 | return 0; |
1208 | } |
1209 | EXPORT_SYMBOL_GPL(of_get_drm_display_mode); |
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 | * |
1217 | * The mandatory Device Tree properties width-mm and height-mm |
1218 | * are read and set on the display mode. |
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, name: "panel-timing" , dt: &timing); |
1232 | if (ret) |
1233 | return ret; |
1234 | |
1235 | videomode_from_timing(dt: &timing, vm: &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, propname: "width-mm" , out_value: &width_mm); |
1243 | if (ret) |
1244 | return ret; |
1245 | |
1246 | ret = of_property_read_u32(np, propname: "height-mm" , out_value: &height_mm); |
1247 | if (ret) |
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); |
1258 | #endif /* CONFIG_OF */ |
1259 | #endif /* CONFIG_VIDEOMODE_HELPERS */ |
1260 | |
1261 | /** |
1262 | * drm_mode_set_name - set the name on a mode |
1263 | * @mode: name will be set in this mode |
1264 | * |
1265 | * Set the name of @mode to a standard format which is <hdisplay>x<vdisplay> |
1266 | * with an optional 'i' suffix for interlaced modes. |
1267 | */ |
1268 | void drm_mode_set_name(struct drm_display_mode *mode) |
1269 | { |
1270 | bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); |
1271 | |
1272 | snprintf(buf: mode->name, DRM_DISPLAY_MODE_LEN, fmt: "%dx%d%s" , |
1273 | mode->hdisplay, mode->vdisplay, |
1274 | interlaced ? "i" : "" ); |
1275 | } |
1276 | EXPORT_SYMBOL(drm_mode_set_name); |
1277 | |
1278 | /** |
1279 | * drm_mode_vrefresh - get the vrefresh of a mode |
1280 | * @mode: mode |
1281 | * |
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. |
1285 | */ |
1286 | int drm_mode_vrefresh(const struct drm_display_mode *mode) |
1287 | { |
1288 | unsigned int num, den; |
1289 | |
1290 | if (mode->htotal == 0 || mode->vtotal == 0) |
1291 | return 0; |
1292 | |
1293 | num = mode->clock; |
1294 | den = mode->htotal * mode->vtotal; |
1295 | |
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; |
1302 | |
1303 | return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(num, 1000), den); |
1304 | } |
1305 | EXPORT_SYMBOL(drm_mode_vrefresh); |
1306 | |
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 | { |
1319 | struct drm_display_mode adjusted; |
1320 | |
1321 | drm_mode_init(dst: &adjusted, src: mode); |
1322 | |
1323 | drm_mode_set_crtcinfo(p: &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 | |
1329 | /** |
1330 | * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters |
1331 | * @p: mode |
1332 | * @adjust_flags: a combination of adjustment flags |
1333 | * |
1334 | * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary. |
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"). |
1341 | * - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not* |
1342 | * be performed for doublescan and vscan > 1 modes respectively. |
1343 | */ |
1344 | void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) |
1345 | { |
1346 | if (!p) |
1347 | return; |
1348 | |
1349 | p->crtc_clock = p->clock; |
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 | } |
1367 | } |
1368 | |
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 | } |
1376 | } |
1377 | |
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 | } |
1385 | } |
1386 | |
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 | |
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); |
1405 | } |
1406 | EXPORT_SYMBOL(drm_mode_set_crtcinfo); |
1407 | |
1408 | /** |
1409 | * drm_mode_copy - copy the mode |
1410 | * @dst: mode to overwrite |
1411 | * @src: mode to copy |
1412 | * |
1413 | * Copy an existing mode into another mode, preserving the |
1414 | * list head of the destination mode. |
1415 | */ |
1416 | void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src) |
1417 | { |
1418 | struct list_head head = dst->head; |
1419 | |
1420 | *dst = *src; |
1421 | dst->head = head; |
1422 | } |
1423 | EXPORT_SYMBOL(drm_mode_copy); |
1424 | |
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 | |
1442 | /** |
1443 | * drm_mode_duplicate - allocate and duplicate an existing mode |
1444 | * @dev: drm_device to allocate the duplicated mode for |
1445 | * @mode: mode to duplicate |
1446 | * |
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. |
1449 | * |
1450 | * Returns: |
1451 | * Pointer to duplicated mode on success, NULL on error. |
1452 | */ |
1453 | struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, |
1454 | const struct drm_display_mode *mode) |
1455 | { |
1456 | struct drm_display_mode *nmode; |
1457 | |
1458 | nmode = drm_mode_create(dev); |
1459 | if (!nmode) |
1460 | return NULL; |
1461 | |
1462 | drm_mode_copy(nmode, mode); |
1463 | |
1464 | return nmode; |
1465 | } |
1466 | EXPORT_SYMBOL(drm_mode_duplicate); |
1467 | |
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 | |
1516 | /** |
1517 | * drm_mode_match - test modes for (partial) equality |
1518 | * @mode1: first mode |
1519 | * @mode2: second mode |
1520 | * @match_flags: which parts need to match (DRM_MODE_MATCH_*) |
1521 | * |
1522 | * Check to see if @mode1 and @mode2 are equivalent. |
1523 | * |
1524 | * Returns: |
1525 | * True if the modes are (partially) equal, false otherwise. |
1526 | */ |
1527 | bool drm_mode_match(const struct drm_display_mode *mode1, |
1528 | const struct drm_display_mode *mode2, |
1529 | unsigned int match_flags) |
1530 | { |
1531 | if (!mode1 && !mode2) |
1532 | return true; |
1533 | |
1534 | if (!mode1 || !mode2) |
1535 | return false; |
1536 | |
1537 | if (match_flags & DRM_MODE_MATCH_TIMINGS && |
1538 | !drm_mode_match_timings(mode1, mode2)) |
1539 | return false; |
1540 | |
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 | |
1578 | DRM_MODE_MATCH_3D_FLAGS| |
1579 | DRM_MODE_MATCH_ASPECT_RATIO); |
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 | */ |
1594 | bool drm_mode_equal_no_clocks(const struct drm_display_mode *mode1, |
1595 | const struct drm_display_mode *mode2) |
1596 | { |
1597 | return drm_mode_match(mode1, mode2, |
1598 | DRM_MODE_MATCH_TIMINGS | |
1599 | DRM_MODE_MATCH_FLAGS | |
1600 | DRM_MODE_MATCH_3D_FLAGS); |
1601 | } |
1602 | EXPORT_SYMBOL(drm_mode_equal_no_clocks); |
1603 | |
1604 | /** |
1605 | * drm_mode_equal_no_clocks_no_stereo - test modes for equality |
1606 | * @mode1: first mode |
1607 | * @mode2: second mode |
1608 | * |
1609 | * Check to see if @mode1 and @mode2 are equivalent, but |
1610 | * don't check the pixel clocks nor the stereo layout. |
1611 | * |
1612 | * Returns: |
1613 | * True if the modes are equal, false otherwise. |
1614 | */ |
1615 | bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, |
1616 | const struct drm_display_mode *mode2) |
1617 | { |
1618 | return drm_mode_match(mode1, mode2, |
1619 | DRM_MODE_MATCH_TIMINGS | |
1620 | DRM_MODE_MATCH_FLAGS); |
1621 | } |
1622 | EXPORT_SYMBOL(drm_mode_equal_no_clocks_no_stereo); |
1623 | |
1624 | static enum drm_mode_status |
1625 | drm_mode_validate_basic(const struct drm_display_mode *mode) |
1626 | { |
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 | |
1633 | if ((mode->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX) |
1634 | return MODE_BAD; |
1635 | |
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 | } |
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); |
1682 | |
1683 | /** |
1684 | * drm_mode_validate_size - make sure modes adhere to size constraints |
1685 | * @mode: mode to check |
1686 | * @maxX: maximum width |
1687 | * @maxY: maximum height |
1688 | * |
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 |
1691 | * member is updated with the appropriate validation failure code. The list |
1692 | * itself is not changed. |
1693 | * |
1694 | * Returns: |
1695 | * The mode status |
1696 | */ |
1697 | enum drm_mode_status |
1698 | drm_mode_validate_size(const struct drm_display_mode *mode, |
1699 | int maxX, int maxY) |
1700 | { |
1701 | if (maxX > 0 && mode->hdisplay > maxX) |
1702 | return MODE_VIRTUAL_X; |
1703 | |
1704 | if (maxY > 0 && mode->vdisplay > maxY) |
1705 | return MODE_VIRTUAL_Y; |
1706 | |
1707 | return MODE_OK; |
1708 | } |
1709 | EXPORT_SYMBOL(drm_mode_validate_size); |
1710 | |
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 | { |
1726 | if (!connector->ycbcr_420_allowed && |
1727 | drm_mode_is_420_only(display: &connector->display_info, mode)) |
1728 | return MODE_NO_420; |
1729 | |
1730 | return MODE_OK; |
1731 | } |
1732 | EXPORT_SYMBOL(drm_mode_validate_ycbcr420); |
1733 | |
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), |
1773 | MODE_STATUS(NO_420), |
1774 | MODE_STATUS(STALE), |
1775 | MODE_STATUS(BAD), |
1776 | MODE_STATUS(ERROR), |
1777 | }; |
1778 | |
1779 | #undef MODE_STATUS |
1780 | |
1781 | const char *drm_get_mode_status_name(enum drm_mode_status status) |
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 | |
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 | * |
1797 | * This helper function can be used to prune a display mode list after |
1798 | * validation has been completed. All modes whose status is not MODE_OK will be |
1799 | * removed from the list, and if @verbose the status code and mode name is also |
1800 | * printed to dmesg. |
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(entry: &mode->head); |
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 | } |
1814 | if (verbose) { |
1815 | drm_mode_debug_printmodeline(mode); |
1816 | DRM_DEBUG_KMS("Not using %s mode: %s\n" , |
1817 | mode->name, |
1818 | drm_get_mode_status_name(mode->status)); |
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 |
1828 | * @priv: unused |
1829 | * @lh_a: list_head for first mode |
1830 | * @lh_b: list_head for second mode |
1831 | * |
1832 | * Compare two modes, given by @lh_a and @lh_b, returning a value indicating |
1833 | * which is better. |
1834 | * |
1835 | * Returns: |
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 | */ |
1839 | static int drm_mode_compare(void *priv, const struct list_head *lh_a, |
1840 | const struct list_head *lh_b) |
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; |
1853 | |
1854 | diff = drm_mode_vrefresh(b) - drm_mode_vrefresh(a); |
1855 | if (diff) |
1856 | return diff; |
1857 | |
1858 | diff = b->clock - a->clock; |
1859 | return diff; |
1860 | } |
1861 | |
1862 | /** |
1863 | * drm_mode_sort - sort mode list |
1864 | * @mode_list: list of drm_display_mode structures to sort |
1865 | * |
1866 | * Sort @mode_list by favorability, moving good modes to the head of the list. |
1867 | */ |
1868 | void drm_mode_sort(struct list_head *mode_list) |
1869 | { |
1870 | list_sort(NULL, head: mode_list, cmp: drm_mode_compare); |
1871 | } |
1872 | EXPORT_SYMBOL(drm_mode_sort); |
1873 | |
1874 | /** |
1875 | * drm_connector_list_update - update the mode list for the connector |
1876 | * @connector: the connector to update |
1877 | * |
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 |
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. |
1884 | */ |
1885 | void drm_connector_list_update(struct drm_connector *connector) |
1886 | { |
1887 | struct drm_display_mode *pmode, *pt; |
1888 | |
1889 | WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex)); |
1890 | |
1891 | list_for_each_entry_safe(pmode, pt, &connector->probed_modes, head) { |
1892 | struct drm_display_mode *mode; |
1893 | bool found_it = false; |
1894 | |
1895 | /* go through current modes checking for the new probed mode */ |
1896 | list_for_each_entry(mode, &connector->modes, head) { |
1897 | if (!drm_mode_equal(pmode, mode)) |
1898 | continue; |
1899 | |
1900 | found_it = true; |
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) { |
1918 | pmode->type |= mode->type; |
1919 | drm_mode_copy(mode, pmode); |
1920 | } else { |
1921 | mode->type |= pmode->type; |
1922 | } |
1923 | |
1924 | list_del(entry: &pmode->head); |
1925 | drm_mode_destroy(connector->dev, pmode); |
1926 | break; |
1927 | } |
1928 | |
1929 | if (!found_it) { |
1930 | list_move_tail(list: &pmode->head, head: &connector->modes); |
1931 | } |
1932 | } |
1933 | } |
1934 | EXPORT_SYMBOL(drm_connector_list_update); |
1935 | |
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 (const char *str, int length, |
1975 | bool freestanding, |
1976 | const struct drm_connector *connector, |
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': |
1984 | if (freestanding) |
1985 | return -EINVAL; |
1986 | |
1987 | mode->interlace = true; |
1988 | break; |
1989 | case 'm': |
1990 | if (freestanding) |
1991 | return -EINVAL; |
1992 | |
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 , |
2027 | const struct drm_connector *connector, |
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(str: end_ptr + i, |
2069 | length: 1, |
2070 | freestanding: false, |
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 | |
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 |
2096 | * if delim points to the terminating zero, then delim + 1 will point |
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 | |
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 | |
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(name: value, len: 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 | |
2163 | static int drm_mode_parse_cmdline_options(const char *str, |
2164 | bool freestanding, |
2165 | const struct drm_connector *connector, |
2166 | struct drm_cmdline_mode *mode) |
2167 | { |
2168 | unsigned int deg, margin, rotation = 0; |
2169 | const char *delim, *option, *sep; |
2170 | |
2171 | option = str; |
2172 | do { |
2173 | delim = strchr(option, '='); |
2174 | if (!delim) { |
2175 | delim = strchr(option, ','); |
2176 | |
2177 | if (!delim) |
2178 | delim = option + strlen(option); |
2179 | } |
2180 | |
2181 | if (!strncmp(option, "rotate" , delim - option)) { |
2182 | if (drm_mode_parse_cmdline_int(delim, int_ret: °)) |
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; |
2207 | } else if (!strncmp(option, "reflect_y" , delim - option)) { |
2208 | rotation |= DRM_MODE_REFLECT_Y; |
2209 | } else if (!strncmp(option, "margin_right" , delim - option)) { |
2210 | if (drm_mode_parse_cmdline_int(delim, int_ret: &margin)) |
2211 | return -EINVAL; |
2212 | |
2213 | mode->tv_margins.right = margin; |
2214 | } else if (!strncmp(option, "margin_left" , delim - option)) { |
2215 | if (drm_mode_parse_cmdline_int(delim, int_ret: &margin)) |
2216 | return -EINVAL; |
2217 | |
2218 | mode->tv_margins.left = margin; |
2219 | } else if (!strncmp(option, "margin_top" , delim - option)) { |
2220 | if (drm_mode_parse_cmdline_int(delim, int_ret: &margin)) |
2221 | return -EINVAL; |
2222 | |
2223 | mode->tv_margins.top = margin; |
2224 | } else if (!strncmp(option, "margin_bottom" , delim - option)) { |
2225 | if (drm_mode_parse_cmdline_int(delim, int_ret: &margin)) |
2226 | return -EINVAL; |
2227 | |
2228 | mode->tv_margins.bottom = margin; |
2229 | } else if (!strncmp(option, "panel_orientation" , delim - option)) { |
2230 | if (drm_mode_parse_panel_orientation(delim, mode)) |
2231 | return -EINVAL; |
2232 | } else if (!strncmp(option, "tv_mode" , delim - option)) { |
2233 | if (drm_mode_parse_tv_mode(delim, mode)) |
2234 | return -EINVAL; |
2235 | } else { |
2236 | return -EINVAL; |
2237 | } |
2238 | sep = strchr(delim, ','); |
2239 | option = sep + 1; |
2240 | } while (sep); |
2241 | |
2242 | if (rotation && freestanding) |
2243 | return -EINVAL; |
2244 | |
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(n: rotation & DRM_MODE_ROTATE_MASK)) |
2250 | return -EINVAL; |
2251 | |
2252 | mode->rotation_reflection = rotation; |
2253 | |
2254 | return 0; |
2255 | } |
2256 | |
2257 | struct drm_named_mode { |
2258 | const char *name; |
2259 | unsigned int pixel_clock_khz; |
2260 | unsigned int xres; |
2261 | unsigned int yres; |
2262 | unsigned int flags; |
2263 | unsigned int tv_mode; |
2264 | }; |
2265 | |
2266 | #define NAMED_MODE(_name, _pclk, _x, _y, _flags, _mode) \ |
2267 | { \ |
2268 | .name = _name, \ |
2269 | .pixel_clock_khz = _pclk, \ |
2270 | .xres = _x, \ |
2271 | .yres = _y, \ |
2272 | .flags = _flags, \ |
2273 | .tv_mode = _mode, \ |
2274 | } |
2275 | |
2276 | static const struct drm_named_mode drm_named_modes[] = { |
2277 | NAMED_MODE("NTSC" , 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC), |
2278 | NAMED_MODE("NTSC-J" , 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC_J), |
2279 | NAMED_MODE("PAL" , 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL), |
2280 | NAMED_MODE("PAL-M" , 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL_M), |
2281 | }; |
2282 | |
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(c: 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 | */ |
2312 | for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) { |
2313 | const struct drm_named_mode *mode = &drm_named_modes[i]; |
2314 | int ret; |
2315 | |
2316 | ret = str_has_prefix(str: name, prefix: mode->name); |
2317 | if (ret != name_end) |
2318 | continue; |
2319 | |
2320 | strscpy(p: cmdline_mode->name, q: mode->name, size: sizeof(cmdline_mode->name)); |
2321 | cmdline_mode->pixel_clock = mode->pixel_clock_khz; |
2322 | cmdline_mode->xres = mode->xres; |
2323 | cmdline_mode->yres = mode->yres; |
2324 | cmdline_mode->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); |
2325 | cmdline_mode->tv_mode = mode->tv_mode; |
2326 | cmdline_mode->tv_mode_specified = true; |
2327 | cmdline_mode->specified = true; |
2328 | |
2329 | return 1; |
2330 | } |
2331 | |
2332 | return -EINVAL; |
2333 | } |
2334 | |
2335 | /** |
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 |
2342 | * configure the connector. |
2343 | * |
2344 | * This uses the same parameters as the fb modedb.c, except for an extra |
2345 | * force-enable, force-enable-digital and force-disable bit at the end:: |
2346 | * |
2347 | * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] |
2348 | * |
2349 | * Additionals options can be provided following the mode, using a comma to |
2350 | * separate each option. Valid options can be found in |
2351 | * Documentation/fb/modedb.rst. |
2352 | * |
2353 | * The intermediate drm_cmdline_mode structure is required to store additional |
2354 | * options from the command line modline like the force-enable/disable flag. |
2355 | * |
2356 | * Returns: |
2357 | * True if a valid modeline has been parsed, false otherwise. |
2358 | */ |
2359 | bool drm_mode_parse_command_line_for_connector(const char *mode_option, |
2360 | const struct drm_connector *connector, |
2361 | struct drm_cmdline_mode *mode) |
2362 | { |
2363 | const char *name; |
2364 | bool freestanding = false, = false; |
2365 | unsigned int bpp_off = 0, refresh_off = 0, options_off = 0; |
2366 | unsigned int mode_end = 0; |
2367 | const char *bpp_ptr = NULL, *refresh_ptr = NULL, * = NULL; |
2368 | const char *options_ptr = NULL; |
2369 | char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL; |
2370 | int len, ret; |
2371 | |
2372 | memset(mode, 0, sizeof(*mode)); |
2373 | mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; |
2374 | |
2375 | if (!mode_option) |
2376 | return false; |
2377 | |
2378 | name = mode_option; |
2379 | |
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 | |
2387 | /* Try to locate the bpp and refresh specifiers, if any */ |
2388 | bpp_ptr = strnchr(name, options_off, '-'); |
2389 | while (bpp_ptr && !isdigit(c: bpp_ptr[1])) |
2390 | bpp_ptr = strnchr(bpp_ptr + 1, options_off, '-'); |
2391 | if (bpp_ptr) |
2392 | bpp_off = bpp_ptr - name; |
2393 | |
2394 | refresh_ptr = strnchr(name, options_off, '@'); |
2395 | if (refresh_ptr) |
2396 | refresh_off = refresh_ptr - name; |
2397 | |
2398 | /* Locate the end of the name / resolution, and parse it */ |
2399 | if (bpp_ptr) { |
2400 | mode_end = bpp_off; |
2401 | } else if (refresh_ptr) { |
2402 | mode_end = refresh_off; |
2403 | } else if (options_ptr) { |
2404 | mode_end = options_off; |
2405 | parse_extras = true; |
2406 | } else { |
2407 | mode_end = strlen(name); |
2408 | parse_extras = true; |
2409 | } |
2410 | |
2411 | if (!mode_end) |
2412 | return false; |
2413 | |
2414 | ret = drm_mode_parse_cmdline_named_mode(name, name_end: mode_end, cmdline_mode: 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; |
2424 | |
2425 | /* No named mode? Check for a normal mode argument, e.g. 1024x768 */ |
2426 | if (!mode->specified && isdigit(c: name[0])) { |
2427 | ret = drm_mode_parse_cmdline_res_mode(str: name, length: mode_end, |
2428 | extras: parse_extras, |
2429 | connector, |
2430 | mode); |
2431 | if (ret) |
2432 | return false; |
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; |
2450 | } |
2451 | |
2452 | if (bpp_ptr) { |
2453 | ret = drm_mode_parse_cmdline_bpp(str: bpp_ptr, end_ptr: &bpp_end_ptr, mode); |
2454 | if (ret) |
2455 | return false; |
2456 | |
2457 | mode->bpp_specified = true; |
2458 | } |
2459 | |
2460 | if (refresh_ptr) { |
2461 | ret = drm_mode_parse_cmdline_refresh(str: refresh_ptr, |
2462 | end_ptr: &refresh_end_ptr, mode); |
2463 | if (ret) |
2464 | return false; |
2465 | |
2466 | mode->refresh_specified = true; |
2467 | } |
2468 | |
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 | |
2480 | if (extra_ptr) { |
2481 | if (options_ptr) |
2482 | len = options_ptr - extra_ptr; |
2483 | else |
2484 | len = strlen(extra_ptr); |
2485 | |
2486 | ret = drm_mode_parse_cmdline_extra(str: extra_ptr, length: len, freestanding, |
2487 | connector, mode); |
2488 | if (ret) |
2489 | return false; |
2490 | } |
2491 | |
2492 | if (options_ptr) { |
2493 | ret = drm_mode_parse_cmdline_options(str: options_ptr + 1, |
2494 | freestanding, |
2495 | connector, mode); |
2496 | if (ret) |
2497 | return false; |
2498 | } |
2499 | |
2500 | return true; |
2501 | } |
2502 | EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector); |
2503 | |
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 | |
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 | */ |
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 | |
2543 | if (cmd->xres == 0 || cmd->yres == 0) |
2544 | return NULL; |
2545 | |
2546 | if (strlen(cmd->name)) |
2547 | mode = drm_named_mode(dev, cmd); |
2548 | else if (cmd->cvt) |
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 | |
2563 | mode->type |= DRM_MODE_TYPE_USERDEF; |
2564 | /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */ |
2565 | if (cmd->xres == 1366) |
2566 | drm_mode_fixup_1366x768(mode); |
2567 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
2568 | return mode; |
2569 | } |
2570 | EXPORT_SYMBOL(drm_mode_create_from_cmdline_mode); |
2571 | |
2572 | /** |
2573 | * drm_mode_convert_to_umode - convert a drm_display_mode into a modeinfo |
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 | { |
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; |
2594 | out->vrefresh = drm_mode_vrefresh(in); |
2595 | out->flags = in->flags; |
2596 | out->type = in->type; |
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; |
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; |
2611 | default: |
2612 | WARN(1, "Invalid aspect ratio (0%x) on mode\n" , |
2613 | in->picture_aspect_ratio); |
2614 | fallthrough; |
2615 | case HDMI_PICTURE_ASPECT_NONE: |
2616 | out->flags |= DRM_MODE_FLAG_PIC_AR_NONE; |
2617 | break; |
2618 | } |
2619 | |
2620 | strncpy(p: out->name, q: in->name, DRM_DISPLAY_MODE_LEN); |
2621 | out->name[DRM_DISPLAY_MODE_LEN-1] = 0; |
2622 | } |
2623 | |
2624 | /** |
2625 | * drm_mode_convert_umode - convert a modeinfo into a drm_display_mode |
2626 | * @dev: drm device |
2627 | * @out: drm_display_mode to return to the user |
2628 | * @in: drm_mode_modeinfo to use |
2629 | * |
2630 | * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to |
2631 | * the caller. |
2632 | * |
2633 | * Returns: |
2634 | * Zero on success, negative errno on failure. |
2635 | */ |
2636 | int drm_mode_convert_umode(struct drm_device *dev, |
2637 | struct drm_display_mode *out, |
2638 | const struct drm_mode_modeinfo *in) |
2639 | { |
2640 | if (in->clock > INT_MAX || in->vrefresh > INT_MAX) |
2641 | return -ERANGE; |
2642 | |
2643 | out->clock = in->clock; |
2644 | out->hdisplay = in->hdisplay; |
2645 | out->hsync_start = in->hsync_start; |
2646 | out->hsync_end = in->hsync_end; |
2647 | out->htotal = in->htotal; |
2648 | out->hskew = in->hskew; |
2649 | out->vdisplay = in->vdisplay; |
2650 | out->vsync_start = in->vsync_start; |
2651 | out->vsync_end = in->vsync_end; |
2652 | out->vtotal = in->vtotal; |
2653 | out->vscan = in->vscan; |
2654 | out->flags = in->flags; |
2655 | /* |
2656 | * Old xf86-video-vmware (possibly others too) used to |
2657 | * leave 'type' uninitialized. Just ignore any bits we |
2658 | * don't like. It's a just hint after all, and more |
2659 | * useful for the kernel->userspace direction anyway. |
2660 | */ |
2661 | out->type = in->type & DRM_MODE_TYPE_ALL; |
2662 | strncpy(p: out->name, q: in->name, DRM_DISPLAY_MODE_LEN); |
2663 | out->name[DRM_DISPLAY_MODE_LEN-1] = 0; |
2664 | |
2665 | /* Clearing picture aspect ratio bits from out flags, |
2666 | * as the aspect-ratio information is not stored in |
2667 | * flags for kernel-mode, but in picture_aspect_ratio. |
2668 | */ |
2669 | out->flags &= ~DRM_MODE_FLAG_PIC_AR_MASK; |
2670 | |
2671 | switch (in->flags & DRM_MODE_FLAG_PIC_AR_MASK) { |
2672 | case DRM_MODE_FLAG_PIC_AR_4_3: |
2673 | out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3; |
2674 | break; |
2675 | case DRM_MODE_FLAG_PIC_AR_16_9: |
2676 | out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9; |
2677 | break; |
2678 | case DRM_MODE_FLAG_PIC_AR_64_27: |
2679 | out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27; |
2680 | break; |
2681 | case DRM_MODE_FLAG_PIC_AR_256_135: |
2682 | out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135; |
2683 | break; |
2684 | case DRM_MODE_FLAG_PIC_AR_NONE: |
2685 | out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE; |
2686 | break; |
2687 | default: |
2688 | return -EINVAL; |
2689 | } |
2690 | |
2691 | out->status = drm_mode_validate_driver(dev, out); |
2692 | if (out->status != MODE_OK) |
2693 | return -EINVAL; |
2694 | |
2695 | drm_mode_set_crtcinfo(out, CRTC_INTERLACE_HALVE_V); |
2696 | |
2697 | return 0; |
2698 | } |
2699 | |
2700 | /** |
2701 | * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420 |
2702 | * output format |
2703 | * |
2704 | * @display: display under action |
2705 | * @mode: video mode to be tested. |
2706 | * |
2707 | * Returns: |
2708 | * true if the mode can be supported in YCBCR420 format |
2709 | * false if not. |
2710 | */ |
2711 | bool drm_mode_is_420_only(const struct drm_display_info *display, |
2712 | const struct drm_display_mode *mode) |
2713 | { |
2714 | u8 vic = drm_match_cea_mode(to_match: mode); |
2715 | |
2716 | return test_bit(vic, display->hdmi.y420_vdb_modes); |
2717 | } |
2718 | EXPORT_SYMBOL(drm_mode_is_420_only); |
2719 | |
2720 | /** |
2721 | * drm_mode_is_420_also - if a given videomode can be supported in YCBCR420 |
2722 | * output format also (along with RGB/YCBCR444/422) |
2723 | * |
2724 | * @display: display under action. |
2725 | * @mode: video mode to be tested. |
2726 | * |
2727 | * Returns: |
2728 | * true if the mode can be support YCBCR420 format |
2729 | * false if not. |
2730 | */ |
2731 | bool drm_mode_is_420_also(const struct drm_display_info *display, |
2732 | const struct drm_display_mode *mode) |
2733 | { |
2734 | u8 vic = drm_match_cea_mode(to_match: mode); |
2735 | |
2736 | return test_bit(vic, display->hdmi.y420_cmdb_modes); |
2737 | } |
2738 | EXPORT_SYMBOL(drm_mode_is_420_also); |
2739 | /** |
2740 | * drm_mode_is_420 - if a given videomode can be supported in YCBCR420 |
2741 | * output format |
2742 | * |
2743 | * @display: display under action. |
2744 | * @mode: video mode to be tested. |
2745 | * |
2746 | * Returns: |
2747 | * true if the mode can be supported in YCBCR420 format |
2748 | * false if not. |
2749 | */ |
2750 | bool drm_mode_is_420(const struct drm_display_info *display, |
2751 | const struct drm_display_mode *mode) |
2752 | { |
2753 | return drm_mode_is_420_only(display, mode) || |
2754 | drm_mode_is_420_also(display, mode); |
2755 | } |
2756 | EXPORT_SYMBOL(drm_mode_is_420); |
2757 | |