1 /* $NetBSD: edid.c,v 1.13 2014/11/17 00:46:04 jmcneill Exp $ */
4 * Copyright (c) 2006 Itronix Inc.
7 * Written by Garrett D'Amore for Itronix Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of Itronix Inc. may not be used to endorse
18 * or promote products derived from this software without specific
19 * prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: edid.c,v 1.13 2014/11/17 00:46:04 jmcneill Exp $");
37 #include <sys/param.h>
43 #define aprint_debug if (0) printf
44 #endif /* !aprint_debug */
46 #include <sys/systm.h>
47 #include <sys/device.h>
48 #include <sys/kernel.h>
49 #include <sys/malloc.h>
50 #endif /* !defined(__minix) */
51 #include <dev/videomode/videomode.h>
52 #include <dev/videomode/ediddevs.h>
53 #include <dev/videomode/edidreg.h>
54 #include <dev/videomode/edidvar.h>
55 #include <dev/videomode/vesagtf.h>
58 #define DIVIDE(x,y) (((x) + ((y) / 2)) / (y))
60 /* These are reversed established timing order */
61 static const char *_edid_modes
[] = {
67 "832x624x74", /* rounding error, 74.55 Hz aka "832x624x75" */
76 "720x400x87", /* rounding error, 87.85 Hz aka "720x400x88" */
92 #include <dev/videomode/ediddevs_data.h>
93 #endif /* EDIDVERBOSE */
96 edid_findvendor(const char *vendor
)
101 for (n
= 0; n
< edid_nvendors
; n
++)
102 if (memcmp(edid_vendors
[n
].vendor
, vendor
, 3) == 0)
103 return edid_vendors
[n
].name
;
109 edid_findproduct(const char *vendor
, uint16_t product
)
114 for (n
= 0; n
< edid_nproducts
; n
++)
115 if (edid_products
[n
].product
== product
&&
116 memcmp(edid_products
[n
].vendor
, vendor
, 3) == 0)
117 return edid_products
[n
].name
;
118 #endif /* EDIDVERBOSE */
124 edid_strchomp(char *ptr
)
140 edid_is_valid(uint8_t *d
)
143 uint8_t sig
[8] = EDID_SIGNATURE
;
145 if (memcmp(d
, sig
, 8) != 0)
148 for (i
= 0; i
< 128; i
++)
150 if ((sum
& 0xff) != 0)
157 edid_print(struct edid_info
*edid
)
163 printf("Vendor: [%s] %s\n", edid
->edid_vendor
, edid
->edid_vendorname
);
164 printf("Product: [%04X] %s\n", edid
->edid_product
,
165 edid
->edid_productname
);
166 printf("Serial number: %s\n", edid
->edid_serial
);
167 printf("Manufactured %d Week %d\n",
168 edid
->edid_year
, edid
->edid_week
);
169 printf("EDID Version %d.%d\n", edid
->edid_version
,
170 edid
->edid_revision
);
171 printf("EDID Comment: %s\n", edid
->edid_comment
);
173 printf("Video Input: %x\n", edid
->edid_video_input
);
174 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_DIGITAL
) {
176 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_DFP1_COMPAT
)
177 printf(" (DFP 1.x compatible)");
180 printf("\tAnalog\n");
181 switch (EDID_VIDEO_INPUT_LEVEL(edid
->edid_video_input
)) {
183 printf("\t-0.7, 0.3V\n");
186 printf("\t-0.714, 0.286V\n");
189 printf("\t-1.0, 0.4V\n");
192 printf("\t-0.7, 0.0V\n");
195 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_BLANK_TO_BLACK
)
196 printf("\tBlank-to-black setup\n");
197 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_SEPARATE_SYNCS
)
198 printf("\tSeperate syncs\n");
199 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_COMPOSITE_SYNC
)
200 printf("\tComposite sync\n");
201 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_SYNC_ON_GRN
)
202 printf("\tSync on green\n");
203 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_SERRATION
)
204 printf("\tSerration vsync\n");
207 printf("Gamma: %d.%02d\n",
208 edid
->edid_gamma
/ 100, edid
->edid_gamma
% 100);
210 printf("Max Size: %d cm x %d cm\n",
211 edid
->edid_max_hsize
, edid
->edid_max_vsize
);
213 printf("Features: %x\n", edid
->edid_features
);
214 if (edid
->edid_features
& EDID_FEATURES_STANDBY
)
215 printf("\tDPMS standby\n");
216 if (edid
->edid_features
& EDID_FEATURES_SUSPEND
)
217 printf("\tDPMS suspend\n");
218 if (edid
->edid_features
& EDID_FEATURES_ACTIVE_OFF
)
219 printf("\tDPMS active-off\n");
220 switch (EDID_FEATURES_DISP_TYPE(edid
->edid_features
)) {
221 case EDID_FEATURES_DISP_TYPE_MONO
:
222 printf("\tMonochrome\n");
224 case EDID_FEATURES_DISP_TYPE_RGB
:
227 case EDID_FEATURES_DISP_TYPE_NON_RGB
:
228 printf("\tMulticolor\n");
230 case EDID_FEATURES_DISP_TYPE_UNDEFINED
:
231 printf("\tUndefined monitor type\n");
234 if (edid
->edid_features
& EDID_FEATURES_STD_COLOR
)
235 printf("\tStandard color space\n");
236 if (edid
->edid_features
& EDID_FEATURES_PREFERRED_TIMING
)
237 printf("\tPreferred timing\n");
238 if (edid
->edid_features
& EDID_FEATURES_DEFAULT_GTF
)
239 printf("\tDefault GTF supported\n");
241 printf("Chroma Info:\n");
242 printf("\tRed X: 0.%03d\n", edid
->edid_chroma
.ec_redx
);
243 printf("\tRed Y: 0.%03d\n", edid
->edid_chroma
.ec_redy
);
244 printf("\tGrn X: 0.%03d\n", edid
->edid_chroma
.ec_greenx
);
245 printf("\tGrn Y: 0.%03d\n", edid
->edid_chroma
.ec_greeny
);
246 printf("\tBlu X: 0.%03d\n", edid
->edid_chroma
.ec_bluex
);
247 printf("\tBlu Y: 0.%03d\n", edid
->edid_chroma
.ec_bluey
);
248 printf("\tWht X: 0.%03d\n", edid
->edid_chroma
.ec_whitex
);
249 printf("\tWht Y: 0.%03d\n", edid
->edid_chroma
.ec_whitey
);
251 if (edid
->edid_have_range
) {
253 printf("\tHorizontal: %d - %d kHz\n",
254 edid
->edid_range
.er_min_hfreq
,
255 edid
->edid_range
.er_max_hfreq
);
256 printf("\tVertical: %d - %d Hz\n",
257 edid
->edid_range
.er_min_vfreq
,
258 edid
->edid_range
.er_max_vfreq
);
259 printf("\tMax Dot Clock: %d MHz\n",
260 edid
->edid_range
.er_max_clock
);
261 if (edid
->edid_range
.er_have_gtf2
) {
262 printf("\tGTF2 hfreq: %d\n",
263 edid
->edid_range
.er_gtf2_hfreq
);
264 printf("\tGTF2 C: %d\n", edid
->edid_range
.er_gtf2_c
);
265 printf("\tGTF2 M: %d\n", edid
->edid_range
.er_gtf2_m
);
266 printf("\tGTF2 J: %d\n", edid
->edid_range
.er_gtf2_j
);
267 printf("\tGTF2 K: %d\n", edid
->edid_range
.er_gtf2_k
);
270 printf("Video modes:\n");
271 for (i
= 0; i
< edid
->edid_nmodes
; i
++) {
272 printf("\t%dx%d @ %dHz",
273 edid
->edid_modes
[i
].hdisplay
,
274 edid
->edid_modes
[i
].vdisplay
,
275 DIVIDE(DIVIDE(edid
->edid_modes
[i
].dot_clock
* 1000,
276 edid
->edid_modes
[i
].htotal
), edid
->edid_modes
[i
].vtotal
));
277 printf(" (%d %d %d %d %d %d %d",
278 edid
->edid_modes
[i
].dot_clock
,
279 edid
->edid_modes
[i
].hsync_start
,
280 edid
->edid_modes
[i
].hsync_end
,
281 edid
->edid_modes
[i
].htotal
,
282 edid
->edid_modes
[i
].vsync_start
,
283 edid
->edid_modes
[i
].vsync_end
,
284 edid
->edid_modes
[i
].vtotal
);
285 printf(" %s%sH %s%sV)\n",
286 edid
->edid_modes
[i
].flags
& VID_PHSYNC
? "+" : "",
287 edid
->edid_modes
[i
].flags
& VID_NHSYNC
? "-" : "",
288 edid
->edid_modes
[i
].flags
& VID_PVSYNC
? "+" : "",
289 edid
->edid_modes
[i
].flags
& VID_NVSYNC
? "-" : "");
291 if (edid
->edid_preferred_mode
)
292 printf("Preferred mode: %dx%d @ %dHz\n",
293 edid
->edid_preferred_mode
->hdisplay
,
294 edid
->edid_preferred_mode
->vdisplay
,
295 DIVIDE(DIVIDE(edid
->edid_preferred_mode
->dot_clock
* 1000,
296 edid
->edid_preferred_mode
->htotal
),
297 edid
->edid_preferred_mode
->vtotal
));
299 printf("Number of extension blocks: %d\n", edid
->edid_ext_block_count
);
302 static const struct videomode
*
303 edid_mode_lookup_list(const char *name
)
307 for (i
= 0; i
< videomode_count
; i
++)
308 if (strcmp(name
, videomode_list
[i
].name
) == 0)
309 return &videomode_list
[i
];
313 static struct videomode
*
314 edid_search_mode(struct edid_info
*edid
, const struct videomode
*mode
)
318 refresh
= DIVIDE(DIVIDE(mode
->dot_clock
* 1000,
319 mode
->htotal
), mode
->vtotal
);
320 for (i
= 0; i
< edid
->edid_nmodes
; i
++) {
321 if (mode
->hdisplay
== edid
->edid_modes
[i
].hdisplay
&&
322 mode
->vdisplay
== edid
->edid_modes
[i
].vdisplay
&&
323 refresh
== DIVIDE(DIVIDE(
324 edid
->edid_modes
[i
].dot_clock
* 1000,
325 edid
->edid_modes
[i
].htotal
), edid
->edid_modes
[i
].vtotal
)) {
326 return &edid
->edid_modes
[i
];
333 edid_std_timing(uint8_t *data
, struct videomode
*vmp
)
336 const struct videomode
*lookup
;
339 if ((data
[0] == 1 && data
[1] == 1) ||
340 (data
[0] == 0 && data
[1] == 0) ||
341 (data
[0] == 0x20 && data
[1] == 0x20))
344 x
= EDID_STD_TIMING_HRES(data
);
345 switch (EDID_STD_TIMING_RATIO(data
)) {
346 case EDID_STD_TIMING_RATIO_16_10
:
349 case EDID_STD_TIMING_RATIO_4_3
:
352 case EDID_STD_TIMING_RATIO_5_4
:
355 case EDID_STD_TIMING_RATIO_16_9
:
360 f
= EDID_STD_TIMING_VFREQ(data
);
362 /* first try to lookup the mode as a DMT timing */
363 snprintf(name
, sizeof(name
), "%dx%dx%d", x
, y
, f
);
364 if ((lookup
= edid_mode_lookup_list(name
)) != NULL
) {
367 /* failing that, calculate it using gtf */
369 * Hmm. I'm not using alternate GTF timings, which
370 * could, in theory, be present.
372 vesagtf_mode(x
, y
, f
, vmp
);
378 edid_det_timing(uint8_t *data
, struct videomode
*vmp
)
380 unsigned hactive
, hblank
, hsyncwid
, hsyncoff
;
381 unsigned vactive
, vblank
, vsyncwid
, vsyncoff
;
384 flags
= EDID_DET_TIMING_FLAGS(data
);
386 /* we don't support stereo modes (for now) */
387 if (flags
& (EDID_DET_TIMING_FLAG_STEREO
|
388 EDID_DET_TIMING_FLAG_STEREO_MODE
))
391 vmp
->dot_clock
= EDID_DET_TIMING_DOT_CLOCK(data
) / 1000;
393 hactive
= EDID_DET_TIMING_HACTIVE(data
);
394 hblank
= EDID_DET_TIMING_HBLANK(data
);
395 hsyncwid
= EDID_DET_TIMING_HSYNC_WIDTH(data
);
396 hsyncoff
= EDID_DET_TIMING_HSYNC_OFFSET(data
);
398 vactive
= EDID_DET_TIMING_VACTIVE(data
);
399 vblank
= EDID_DET_TIMING_VBLANK(data
);
400 vsyncwid
= EDID_DET_TIMING_VSYNC_WIDTH(data
);
401 vsyncoff
= EDID_DET_TIMING_VSYNC_OFFSET(data
);
403 /* Borders are contained within the blank areas. */
405 vmp
->hdisplay
= hactive
;
406 vmp
->htotal
= hactive
+ hblank
;
407 vmp
->hsync_start
= hactive
+ hsyncoff
;
408 vmp
->hsync_end
= vmp
->hsync_start
+ hsyncwid
;
410 vmp
->vdisplay
= vactive
;
411 vmp
->vtotal
= vactive
+ vblank
;
412 vmp
->vsync_start
= vactive
+ vsyncoff
;
413 vmp
->vsync_end
= vmp
->vsync_start
+ vsyncwid
;
417 if (flags
& EDID_DET_TIMING_FLAG_INTERLACE
)
418 vmp
->flags
|= VID_INTERLACE
;
419 if (flags
& EDID_DET_TIMING_FLAG_HSYNC_POSITIVE
)
420 vmp
->flags
|= VID_PHSYNC
;
422 vmp
->flags
|= VID_NHSYNC
;
424 if (flags
& EDID_DET_TIMING_FLAG_VSYNC_POSITIVE
)
425 vmp
->flags
|= VID_PVSYNC
;
427 vmp
->flags
|= VID_NVSYNC
;
433 edid_block(struct edid_info
*edid
, uint8_t *data
)
436 struct videomode mode
, *exist_mode
;
438 if (EDID_BLOCK_IS_DET_TIMING(data
)) {
439 if (!edid_det_timing(data
, &mode
))
441 /* Does this mode already exist? */
442 exist_mode
= edid_search_mode(edid
, &mode
);
443 if (exist_mode
!= NULL
) {
445 if (edid
->edid_preferred_mode
== NULL
)
446 edid
->edid_preferred_mode
= exist_mode
;
448 edid
->edid_modes
[edid
->edid_nmodes
] = mode
;
449 if (edid
->edid_preferred_mode
== NULL
)
450 edid
->edid_preferred_mode
=
451 &edid
->edid_modes
[edid
->edid_nmodes
];
457 switch (EDID_BLOCK_TYPE(data
)) {
458 case EDID_DESC_BLOCK_TYPE_SERIAL
:
459 memcpy(edid
->edid_serial
, data
+ EDID_DESC_ASCII_DATA_OFFSET
,
460 EDID_DESC_ASCII_DATA_LEN
);
461 edid
->edid_serial
[sizeof(edid
->edid_serial
) - 1] = 0;
464 case EDID_DESC_BLOCK_TYPE_ASCII
:
465 memcpy(edid
->edid_comment
, data
+ EDID_DESC_ASCII_DATA_OFFSET
,
466 EDID_DESC_ASCII_DATA_LEN
);
467 edid
->edid_comment
[sizeof(edid
->edid_comment
) - 1] = 0;
470 case EDID_DESC_BLOCK_TYPE_RANGE
:
471 edid
->edid_have_range
= 1;
472 edid
->edid_range
.er_min_vfreq
= EDID_DESC_RANGE_MIN_VFREQ(data
);
473 edid
->edid_range
.er_max_vfreq
= EDID_DESC_RANGE_MAX_VFREQ(data
);
474 edid
->edid_range
.er_min_hfreq
= EDID_DESC_RANGE_MIN_HFREQ(data
);
475 edid
->edid_range
.er_max_hfreq
= EDID_DESC_RANGE_MAX_HFREQ(data
);
476 edid
->edid_range
.er_max_clock
= EDID_DESC_RANGE_MAX_CLOCK(data
);
477 if (!EDID_DESC_RANGE_HAVE_GTF2(data
))
479 edid
->edid_range
.er_have_gtf2
= 1;
480 edid
->edid_range
.er_gtf2_hfreq
=
481 EDID_DESC_RANGE_GTF2_HFREQ(data
);
482 edid
->edid_range
.er_gtf2_c
= EDID_DESC_RANGE_GTF2_C(data
);
483 edid
->edid_range
.er_gtf2_m
= EDID_DESC_RANGE_GTF2_M(data
);
484 edid
->edid_range
.er_gtf2_j
= EDID_DESC_RANGE_GTF2_J(data
);
485 edid
->edid_range
.er_gtf2_k
= EDID_DESC_RANGE_GTF2_K(data
);
488 case EDID_DESC_BLOCK_TYPE_NAME
:
489 /* copy the product name into place */
490 memcpy(edid
->edid_productname
,
491 data
+ EDID_DESC_ASCII_DATA_OFFSET
,
492 EDID_DESC_ASCII_DATA_LEN
);
495 case EDID_DESC_BLOCK_TYPE_STD_TIMING
:
496 data
+= EDID_DESC_STD_TIMING_START
;
497 for (i
= 0; i
< EDID_DESC_STD_TIMING_COUNT
; i
++) {
498 if (edid_std_timing(data
, &mode
)) {
499 /* Does this mode already exist? */
500 exist_mode
= edid_search_mode(edid
, &mode
);
501 if (exist_mode
== NULL
) {
502 edid
->edid_modes
[edid
->edid_nmodes
] =
511 case EDID_DESC_BLOCK_TYPE_COLOR_POINT
:
512 /* XXX: not implemented yet */
518 * Gets EDID version in BCD, e.g. EDID v1.3 returned as 0x0103
521 edid_parse(uint8_t *data
, struct edid_info
*edid
)
523 uint16_t manfid
, estmodes
;
524 const struct videomode
*vmp
;
527 int max_dotclock
= 0;
530 if (edid_is_valid(data
) != 0)
533 /* get product identification */
534 manfid
= EDID_VENDOR_ID(data
);
535 edid
->edid_vendor
[0] = EDID_MANFID_0(manfid
);
536 edid
->edid_vendor
[1] = EDID_MANFID_1(manfid
);
537 edid
->edid_vendor
[2] = EDID_MANFID_2(manfid
);
538 edid
->edid_vendor
[3] = 0; /* null terminate for convenience */
540 edid
->edid_product
= data
[EDID_OFFSET_PRODUCT_ID
] +
541 (data
[EDID_OFFSET_PRODUCT_ID
+ 1] << 8);
543 name
= edid_findvendor(edid
->edid_vendor
);
545 strlcpy(edid
->edid_vendorname
, name
,
546 sizeof(edid
->edid_vendorname
));
548 edid
->edid_vendorname
[0] = '\0';
550 name
= edid_findproduct(edid
->edid_vendor
, edid
->edid_product
);
552 strlcpy(edid
->edid_productname
, name
,
553 sizeof(edid
->edid_productname
));
555 edid
->edid_productname
[0] = '\0';
557 snprintf(edid
->edid_serial
, sizeof(edid
->edid_serial
), "%08x",
558 EDID_SERIAL_NUMBER(data
));
560 edid
->edid_week
= EDID_WEEK(data
);
561 edid
->edid_year
= EDID_YEAR(data
);
563 /* get edid revision */
564 edid
->edid_version
= EDID_VERSION(data
);
565 edid
->edid_revision
= EDID_REVISION(data
);
567 edid
->edid_video_input
= EDID_VIDEO_INPUT(data
);
568 edid
->edid_max_hsize
= EDID_MAX_HSIZE(data
);
569 edid
->edid_max_vsize
= EDID_MAX_VSIZE(data
);
571 edid
->edid_gamma
= EDID_GAMMA(data
);
572 edid
->edid_features
= EDID_FEATURES(data
);
574 edid
->edid_chroma
.ec_redx
= EDID_CHROMA_REDX(data
);
575 edid
->edid_chroma
.ec_redy
= EDID_CHROMA_REDX(data
);
576 edid
->edid_chroma
.ec_greenx
= EDID_CHROMA_GREENX(data
);
577 edid
->edid_chroma
.ec_greeny
= EDID_CHROMA_GREENY(data
);
578 edid
->edid_chroma
.ec_bluex
= EDID_CHROMA_BLUEX(data
);
579 edid
->edid_chroma
.ec_bluey
= EDID_CHROMA_BLUEY(data
);
580 edid
->edid_chroma
.ec_whitex
= EDID_CHROMA_WHITEX(data
);
581 edid
->edid_chroma
.ec_whitey
= EDID_CHROMA_WHITEY(data
);
583 edid
->edid_ext_block_count
= EDID_EXT_BLOCK_COUNT(data
);
585 /* lookup established modes */
586 edid
->edid_nmodes
= 0;
587 edid
->edid_preferred_mode
= NULL
;
588 estmodes
= EDID_EST_TIMING(data
);
589 /* Iterate in esztablished timing order */
590 for (i
= 15; i
>= 0; i
--) {
591 if (estmodes
& (1 << i
)) {
592 vmp
= edid_mode_lookup_list(_edid_modes
[i
]);
594 edid
->edid_modes
[edid
->edid_nmodes
] = *vmp
;
599 printf("no data for est. mode %s\n",
605 /* do standard timing section */
606 for (i
= 0; i
< EDID_STD_TIMING_COUNT
; i
++) {
607 struct videomode mode
, *exist_mode
;
608 if (edid_std_timing(data
+ EDID_OFFSET_STD_TIMING
+ i
* 2,
610 /* Does this mode already exist? */
611 exist_mode
= edid_search_mode(edid
, &mode
);
612 if (exist_mode
== NULL
) {
613 edid
->edid_modes
[edid
->edid_nmodes
] = mode
;
619 /* do detailed timings and descriptors */
620 for (i
= 0; i
< EDID_BLOCK_COUNT
; i
++) {
621 edid_block(edid
, data
+ EDID_OFFSET_DESC_BLOCK
+
622 i
* EDID_BLOCK_SIZE
);
625 edid_strchomp(edid
->edid_vendorname
);
626 edid_strchomp(edid
->edid_productname
);
627 edid_strchomp(edid
->edid_serial
);
628 edid_strchomp(edid
->edid_comment
);
632 * some monitors lie about their maximum supported dot clock
633 * by claiming to support modes which need a higher dot clock
634 * than the stated maximum.
635 * For sanity's sake we bump it to the highest dot clock we find
636 * in the list of supported modes
638 for (i
= 0; i
< edid
->edid_nmodes
; i
++)
639 if (edid
->edid_modes
[i
].dot_clock
> max_dotclock
)
640 max_dotclock
= edid
->edid_modes
[i
].dot_clock
;
642 aprint_debug("max_dotclock according to supported modes: %d\n",
645 mhz
= (max_dotclock
+ 999) / 1000;
647 if (edid
->edid_have_range
) {
648 if (mhz
> edid
->edid_range
.er_max_clock
)
649 edid
->edid_range
.er_max_clock
= mhz
;
651 edid
->edid_range
.er_max_clock
= mhz
;