1 /* $NetBSD: edid.c,v 1.5 2007/03/07 19:56:40 macallan 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.5 2007/03/07 19:56:40 macallan Exp $");
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/device.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <dev/videomode/videomode.h>
43 #include <dev/videomode/ediddevs.h>
44 #include <dev/videomode/edidreg.h>
45 #include <dev/videomode/edidvar.h>
46 #include <dev/videomode/vesagtf.h>
49 #define DIVIDE(x,y) (((x) + ((y) / 2)) / (y))
51 static const char *_edid_modes
[] = {
57 "832x768x74", /* rounding error, 74.55 Hz aka "832x624x75" */
66 "720x400x85", /* should this really be "720x400x88" ? */
67 "720x400x70", /* hmm... videmode.c doesn't have this one */
82 #include <dev/videomode/ediddevs_data.h>
83 #endif /* EDIDVERBOSE */
86 edid_findvendor(const char *vendor
)
91 for (n
= 0; n
< edid_nvendors
; n
++)
92 if (memcmp(edid_vendors
[n
].vendor
, vendor
, 3) == 0)
93 return (edid_vendors
[n
].name
);
99 edid_findproduct(const char *vendor
, uint16_t product
)
104 for (n
= 0; n
< edid_nproducts
; n
++)
105 if ((edid_products
[n
].product
== product
) &&
106 (memcmp(edid_products
[n
].vendor
, vendor
, 3) == 0))
107 return (edid_products
[n
].name
);
108 #endif /* EDIDVERBOSE */
114 edid_strchomp(char *ptr
)
130 edid_is_valid(uint8_t *d
)
133 uint8_t sig
[8] = EDID_SIGNATURE
;
135 if (memcmp(d
, sig
, 8) != 0)
138 for (i
= 0; i
< 128; i
++)
140 if ((sum
& 0xff) != 0)
147 edid_print(struct edid_info
*edid
)
153 printf("Vendor: [%s] %s\n", edid
->edid_vendor
, edid
->edid_vendorname
);
154 printf("Product: [%04X] %s\n", edid
->edid_product
,
155 edid
->edid_productname
);
156 printf("Serial number: %s\n", edid
->edid_serial
);
157 printf("Manufactured %d Week %d\n",
158 edid
->edid_year
, edid
->edid_week
);
159 printf("EDID Version %d.%d\n", edid
->edid_version
,
160 edid
->edid_revision
);
161 printf("EDID Comment: %s\n", edid
->edid_comment
);
163 printf("Video Input: %x\n", edid
->edid_video_input
);
164 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_DIGITAL
) {
166 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_DFP1_COMPAT
)
167 printf(" (DFP 1.x compatible)");
170 printf("\tAnalog\n");
171 switch (EDID_VIDEO_INPUT_LEVEL(edid
->edid_video_input
)) {
173 printf("\t-0.7, 0.3V\n");
176 printf("\t-0.714, 0.286V\n");
179 printf("\t-1.0, 0.4V\n");
182 printf("\t-0.7, 0.0V\n");
185 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_BLANK_TO_BLACK
)
186 printf("\tBlank-to-black setup\n");
187 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_SEPARATE_SYNCS
)
188 printf("\tSeperate syncs\n");
189 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_COMPOSITE_SYNC
)
190 printf("\tComposite sync\n");
191 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_SYNC_ON_GRN
)
192 printf("\tSync on green\n");
193 if (edid
->edid_video_input
& EDID_VIDEO_INPUT_SERRATION
)
194 printf("\tSerration vsync\n");
197 printf("Gamma: %d.%02d\n",
198 edid
->edid_gamma
/ 100, edid
->edid_gamma
% 100);
200 printf("Max Size: %d cm x %d cm\n",
201 edid
->edid_max_hsize
, edid
->edid_max_vsize
);
203 printf("Features: %x\n", edid
->edid_features
);
204 if (edid
->edid_features
& EDID_FEATURES_STANDBY
)
205 printf("\tDPMS standby\n");
206 if (edid
->edid_features
& EDID_FEATURES_SUSPEND
)
207 printf("\tDPMS suspend\n");
208 if (edid
->edid_features
& EDID_FEATURES_ACTIVE_OFF
)
209 printf("\tDPMS active-off\n");
210 switch (EDID_FEATURES_DISP_TYPE(edid
->edid_features
)) {
211 case EDID_FEATURES_DISP_TYPE_MONO
:
212 printf("\tMonochrome\n");
214 case EDID_FEATURES_DISP_TYPE_RGB
:
217 case EDID_FEATURES_DISP_TYPE_NON_RGB
:
218 printf("\tMulticolor\n");
220 case EDID_FEATURES_DISP_TYPE_UNDEFINED
:
221 printf("\tUndefined monitor type\n");
224 if (edid
->edid_features
& EDID_FEATURES_STD_COLOR
)
225 printf("\tStandard color space\n");
226 if (edid
->edid_features
& EDID_FEATURES_PREFERRED_TIMING
)
227 printf("\tPreferred timing\n");
228 if (edid
->edid_features
& EDID_FEATURES_DEFAULT_GTF
)
229 printf("\tDefault GTF supported\n");
231 printf("Chroma Info:\n");
232 printf("\tRed X: 0.%03d\n", edid
->edid_chroma
.ec_redx
);
233 printf("\tRed Y: 0.%03d\n", edid
->edid_chroma
.ec_redy
);
234 printf("\tGrn X: 0.%03d\n", edid
->edid_chroma
.ec_greenx
);
235 printf("\tGrn Y: 0.%03d\n", edid
->edid_chroma
.ec_greeny
);
236 printf("\tBlu X: 0.%03d\n", edid
->edid_chroma
.ec_bluex
);
237 printf("\tBlu Y: 0.%03d\n", edid
->edid_chroma
.ec_bluey
);
238 printf("\tWht X: 0.%03d\n", edid
->edid_chroma
.ec_whitex
);
239 printf("\tWht Y: 0.%03d\n", edid
->edid_chroma
.ec_whitey
);
241 if (edid
->edid_have_range
) {
243 printf("\tHorizontal: %d - %d kHz\n",
244 edid
->edid_range
.er_min_hfreq
,
245 edid
->edid_range
.er_max_hfreq
);
246 printf("\tVertical: %d - %d Hz\n",
247 edid
->edid_range
.er_min_vfreq
,
248 edid
->edid_range
.er_max_vfreq
);
249 printf("\tMax Dot Clock: %d MHz\n",
250 edid
->edid_range
.er_max_clock
);
251 if (edid
->edid_range
.er_have_gtf2
) {
252 printf("\tGTF2 hfreq: %d\n",
253 edid
->edid_range
.er_gtf2_hfreq
);
254 printf("\tGTF2 C: %d\n", edid
->edid_range
.er_gtf2_c
);
255 printf("\tGTF2 M: %d\n", edid
->edid_range
.er_gtf2_m
);
256 printf("\tGTF2 J: %d\n", edid
->edid_range
.er_gtf2_j
);
257 printf("\tGTF2 K: %d\n", edid
->edid_range
.er_gtf2_k
);
260 printf("Video modes:\n");
261 for (i
= 0; i
< edid
->edid_nmodes
; i
++) {
262 printf("\t%dx%d @ %dHz\n",
263 edid
->edid_modes
[i
].hdisplay
,
264 edid
->edid_modes
[i
].vdisplay
,
265 DIVIDE(DIVIDE(edid
->edid_modes
[i
].dot_clock
* 1000,
266 edid
->edid_modes
[i
].htotal
),
267 edid
->edid_modes
[i
].vtotal
));
269 if (edid
->edid_preferred_mode
)
270 printf("Preferred mode: %dx%d @ %dHz\n",
271 edid
->edid_preferred_mode
->hdisplay
,
272 edid
->edid_preferred_mode
->vdisplay
,
273 DIVIDE(DIVIDE(edid
->edid_preferred_mode
->dot_clock
* 1000,
274 edid
->edid_preferred_mode
->htotal
),
275 edid
->edid_preferred_mode
->vtotal
));
278 static const struct videomode
*
279 edid_mode_lookup_list(const char *name
)
283 for (i
= 0; i
< videomode_count
; i
++)
284 if (strcmp(name
, videomode_list
[i
].name
) == 0)
285 return &videomode_list
[i
];
290 edid_std_timing(uint8_t *data
, struct videomode
*vmp
)
293 const struct videomode
*lookup
;
296 if ((data
[0] == 1 && data
[1] == 1) ||
297 (data
[0] == 0 && data
[1] == 0) ||
298 (data
[0] == 0x20 && data
[1] == 0x20))
301 x
= EDID_STD_TIMING_HRES(data
);
302 switch (EDID_STD_TIMING_RATIO(data
)) {
303 case EDID_STD_TIMING_RATIO_16_10
:
306 case EDID_STD_TIMING_RATIO_4_3
:
309 case EDID_STD_TIMING_RATIO_5_4
:
312 case EDID_STD_TIMING_RATIO_16_9
:
317 f
= EDID_STD_TIMING_VFREQ(data
);
319 /* first try to lookup the mode as a DMT timing */
320 snprintf(name
, sizeof (name
), "%dx%dx%d", x
, y
, f
);
321 if ((lookup
= edid_mode_lookup_list(name
)) != NULL
) {
325 /* failing that, calculate it using gtf */
328 * Hmm. I'm not using alternate GTF timings, which
329 * could, in theory, be present.
331 vesagtf_mode(x
, y
, f
, vmp
);
337 edid_det_timing(uint8_t *data
, struct videomode
*vmp
)
339 unsigned hactive
, hblank
, hsyncwid
, hsyncoff
;
340 unsigned vactive
, vblank
, vsyncwid
, vsyncoff
;
343 flags
= EDID_DET_TIMING_FLAGS(data
);
345 /* we don't support stereo modes (for now) */
346 if (flags
& (EDID_DET_TIMING_FLAG_STEREO
|
347 EDID_DET_TIMING_FLAG_STEREO1
))
350 vmp
->dot_clock
= EDID_DET_TIMING_DOT_CLOCK(data
) / 1000;
352 hactive
= EDID_DET_TIMING_HACTIVE(data
);
353 hblank
= EDID_DET_TIMING_HBLANK(data
);
354 hsyncwid
= EDID_DET_TIMING_HSYNC_WIDTH(data
);
355 hsyncoff
= EDID_DET_TIMING_HSYNC_OFFSET(data
);
357 vactive
= EDID_DET_TIMING_VACTIVE(data
);
358 vblank
= EDID_DET_TIMING_VBLANK(data
);
359 vsyncwid
= EDID_DET_TIMING_VSYNC_WIDTH(data
);
360 vsyncoff
= EDID_DET_TIMING_VSYNC_OFFSET(data
);
362 /* XXX: I'm not doing anything with the borders, should I? */
364 vmp
->hdisplay
= hactive
;
365 vmp
->htotal
= hactive
+ hblank
;
366 vmp
->hsync_start
= hactive
+ hsyncoff
;
367 vmp
->hsync_end
= vmp
->hsync_start
+ hsyncwid
;
369 vmp
->vdisplay
= vactive
;
370 vmp
->vtotal
= vactive
+ vblank
;
371 vmp
->vsync_start
= vactive
+ vsyncoff
;
372 vmp
->vsync_end
= vmp
->vsync_start
+ vsyncwid
;
376 if (flags
& EDID_DET_TIMING_FLAG_INTERLACE
)
377 vmp
->flags
|= VID_INTERLACE
;
378 if (flags
& EDID_DET_TIMING_FLAG_HSYNC_POSITIVE
)
379 vmp
->flags
|= VID_PHSYNC
;
381 vmp
->flags
|= VID_NHSYNC
;
383 if (flags
& EDID_DET_TIMING_FLAG_VSYNC_POSITIVE
)
384 vmp
->flags
|= VID_PVSYNC
;
386 vmp
->flags
|= VID_NVSYNC
;
392 edid_block(struct edid_info
*edid
, uint8_t *data
)
395 struct videomode mode
;
397 if (EDID_BLOCK_IS_DET_TIMING(data
)) {
398 if (edid_det_timing(data
, &mode
)) {
399 edid
->edid_modes
[edid
->edid_nmodes
] = mode
;
400 if (edid
->edid_preferred_mode
== NULL
) {
401 edid
->edid_preferred_mode
=
402 &edid
->edid_modes
[edid
->edid_nmodes
];
409 switch (EDID_BLOCK_TYPE(data
)) {
410 case EDID_DESC_BLOCK_TYPE_SERIAL
:
411 memcpy(edid
->edid_serial
,
412 data
+ EDID_DESC_ASCII_DATA_OFFSET
,
413 EDID_DESC_ASCII_DATA_LEN
);
414 edid
->edid_serial
[sizeof (edid
->edid_serial
) - 1] = 0;
417 case EDID_DESC_BLOCK_TYPE_ASCII
:
418 memcpy(edid
->edid_comment
,
419 data
+ EDID_DESC_ASCII_DATA_OFFSET
,
420 EDID_DESC_ASCII_DATA_LEN
);
421 edid
->edid_comment
[sizeof (edid
->edid_comment
) - 1] = 0;
424 case EDID_DESC_BLOCK_TYPE_RANGE
:
425 edid
->edid_have_range
= 1;
426 edid
->edid_range
.er_min_vfreq
=
427 EDID_DESC_RANGE_MIN_VFREQ(data
);
428 edid
->edid_range
.er_max_vfreq
=
429 EDID_DESC_RANGE_MAX_VFREQ(data
);
430 edid
->edid_range
.er_min_hfreq
=
431 EDID_DESC_RANGE_MIN_HFREQ(data
);
432 edid
->edid_range
.er_max_hfreq
=
433 EDID_DESC_RANGE_MAX_HFREQ(data
);
434 edid
->edid_range
.er_max_clock
=
435 EDID_DESC_RANGE_MAX_CLOCK(data
);
436 if (EDID_DESC_RANGE_HAVE_GTF2(data
)) {
437 edid
->edid_range
.er_have_gtf2
= 1;
438 edid
->edid_range
.er_gtf2_hfreq
=
439 EDID_DESC_RANGE_GTF2_HFREQ(data
);
440 edid
->edid_range
.er_gtf2_c
=
441 EDID_DESC_RANGE_GTF2_C(data
);
442 edid
->edid_range
.er_gtf2_m
=
443 EDID_DESC_RANGE_GTF2_M(data
);
444 edid
->edid_range
.er_gtf2_j
=
445 EDID_DESC_RANGE_GTF2_J(data
);
446 edid
->edid_range
.er_gtf2_k
=
447 EDID_DESC_RANGE_GTF2_K(data
);
451 case EDID_DESC_BLOCK_TYPE_NAME
:
452 /* copy the product name into place */
453 memcpy(edid
->edid_productname
,
454 data
+ EDID_DESC_ASCII_DATA_OFFSET
,
455 EDID_DESC_ASCII_DATA_LEN
);
458 case EDID_DESC_BLOCK_TYPE_STD_TIMING
:
459 data
+= EDID_DESC_STD_TIMING_START
;
460 for (i
= 0; i
< EDID_DESC_STD_TIMING_COUNT
; i
++) {
461 if (edid_std_timing(data
, &mode
)) {
462 edid
->edid_modes
[edid
->edid_nmodes
] = mode
;
469 case EDID_DESC_BLOCK_TYPE_COLOR_POINT
:
470 /* XXX: not implemented yet */
476 * Gets EDID version in BCD, e.g. EDID v1.3 returned as 0x0103
479 edid_parse(uint8_t *data
, struct edid_info
*edid
)
481 uint16_t manfid
, estmodes
;
482 const struct videomode
*vmp
;
485 int max_dotclock
= 0;
488 if (edid_is_valid(data
) != 0)
491 /* get product identification */
492 manfid
= EDID_VENDOR_ID(data
);
493 edid
->edid_vendor
[0] = EDID_MANFID_0(manfid
);
494 edid
->edid_vendor
[1] = EDID_MANFID_1(manfid
);
495 edid
->edid_vendor
[2] = EDID_MANFID_2(manfid
);
496 edid
->edid_vendor
[3] = 0; /* null terminate for convenience */
498 edid
->edid_product
= data
[EDID_OFFSET_PRODUCT_ID
] +
499 (data
[EDID_OFFSET_PRODUCT_ID
+ 1] << 8);
501 name
= edid_findvendor(edid
->edid_vendor
);
503 snprintf(edid
->edid_vendorname
,
504 sizeof (edid
->edid_vendorname
), "%s", name
);
506 edid
->edid_vendorname
[sizeof (edid
->edid_vendorname
) - 1] = 0;
508 name
= edid_findproduct(edid
->edid_vendor
, edid
->edid_product
);
510 snprintf(edid
->edid_productname
,
511 sizeof (edid
->edid_productname
), "%s", name
);
513 edid
->edid_productname
[sizeof (edid
->edid_productname
) - 1] = 0;
515 snprintf(edid
->edid_serial
, sizeof (edid
->edid_serial
), "%08x",
516 EDID_SERIAL_NUMBER(data
));
518 edid
->edid_week
= EDID_WEEK(data
);
519 edid
->edid_year
= EDID_YEAR(data
);
521 /* get edid revision */
522 edid
->edid_version
= EDID_VERSION(data
);
523 edid
->edid_revision
= EDID_REVISION(data
);
525 edid
->edid_video_input
= EDID_VIDEO_INPUT(data
);
526 edid
->edid_max_hsize
= EDID_MAX_HSIZE(data
);
527 edid
->edid_max_vsize
= EDID_MAX_VSIZE(data
);
529 edid
->edid_gamma
= EDID_GAMMA(data
);
530 edid
->edid_features
= EDID_FEATURES(data
);
532 edid
->edid_chroma
.ec_redx
= EDID_CHROMA_REDX(data
);
533 edid
->edid_chroma
.ec_redy
= EDID_CHROMA_REDX(data
);
534 edid
->edid_chroma
.ec_greenx
= EDID_CHROMA_GREENX(data
);
535 edid
->edid_chroma
.ec_greeny
= EDID_CHROMA_GREENY(data
);
536 edid
->edid_chroma
.ec_bluex
= EDID_CHROMA_BLUEX(data
);
537 edid
->edid_chroma
.ec_bluey
= EDID_CHROMA_BLUEY(data
);
538 edid
->edid_chroma
.ec_whitex
= EDID_CHROMA_WHITEX(data
);
539 edid
->edid_chroma
.ec_whitey
= EDID_CHROMA_WHITEY(data
);
541 /* lookup established modes */
542 edid
->edid_nmodes
= 0;
543 edid
->edid_preferred_mode
= NULL
;
544 estmodes
= EDID_EST_TIMING(data
);
545 for (i
= 0; i
< 16; i
++) {
546 if (estmodes
& (1 << i
)) {
547 vmp
= edid_mode_lookup_list(_edid_modes
[i
]);
549 edid
->edid_modes
[edid
->edid_nmodes
] = *vmp
;
554 printf("no data for est. mode %s\n",
560 /* do standard timing section */
561 for (i
= 0; i
< EDID_STD_TIMING_COUNT
; i
++) {
562 struct videomode mode
;
563 if (edid_std_timing(data
+ EDID_OFFSET_STD_TIMING
+ i
* 2,
565 edid
->edid_modes
[edid
->edid_nmodes
] = mode
;
570 /* do detailed timings and descriptors */
571 for (i
= 0; i
< EDID_BLOCK_COUNT
; i
++) {
572 edid_block(edid
, data
+ EDID_OFFSET_DESC_BLOCK
+
573 i
* EDID_BLOCK_SIZE
);
576 edid_strchomp(edid
->edid_vendorname
);
577 edid_strchomp(edid
->edid_productname
);
578 edid_strchomp(edid
->edid_serial
);
579 edid_strchomp(edid
->edid_comment
);
583 * some monitors lie about their maximum supported dot clock
584 * by claiming to support modes which need a higher dot clock
585 * than the stated maximum.
586 * For sanity's sake we bump it to the highest dot clock we find
587 * in the list of supported modes
589 for (i
= 0; i
< edid
->edid_nmodes
; i
++)
590 if (edid
->edid_modes
[i
].dot_clock
> max_dotclock
)
591 max_dotclock
= edid
->edid_modes
[i
].dot_clock
;
593 aprint_verbose("max_dotclock according to supported modes: %d\n",
596 mhz
= (max_dotclock
+ 999) / 1000;
598 if (edid
->edid_have_range
) {
600 if (mhz
> edid
->edid_range
.er_max_clock
)
601 edid
->edid_range
.er_max_clock
= mhz
;
603 edid
->edid_range
.er_max_clock
= mhz
;