1 /* $OpenBSD: edid.c,v 1.8 2024/11/06 09:34:10 miod Exp $ */
2 /* $NetBSD: edid.c,v 1.15 2020/01/25 15:59:11 maxv Exp $ */
3
4 /*-
5 * Copyright (c) 2006 Itronix Inc.
6 * All rights reserved.
7 *
8 * Written by Garrett D'Amore for Itronix Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. The name of Itronix Inc. may not be used to endorse
19 * or promote products derived from this software without specific
20 * prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND ANY EXPRESS
23 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
26 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
28 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/device.h>
38 #include <sys/kernel.h>
39 #include <dev/videomode/videomode.h>
40 #include <dev/videomode/ediddevs.h>
41 #include <dev/videomode/edidreg.h>
42 #include <dev/videomode/edidvar.h>
43 #include <dev/videomode/vesagtf.h>
44
45 const char *edid_findvendor(const char *);
46 const char *edid_findproduct(const char *, uint16_t);
47 void edid_strchomp(char *);
48 const struct videomode *edid_mode_lookup_list(const char *);
49 struct videomode *edid_search_mode(struct edid_info *,
50 const struct videomode *);
51 int edid_std_timing(uint8_t *, struct videomode *);
52 int edid_det_timing(uint8_t *, struct videomode *);
53 void bump_preferred_mode(struct edid_info *, struct videomode *);
54 void edid_block(struct edid_info *, uint8_t *);
55
56 /* #define EDID_DEBUG */
57 #define EDIDVERBOSE 1
58 #define DIVIDE(x,y) (((x) + ((y) / 2)) / (y))
59
60 /* These are reversed established timing order */
61 static const char *_edid_modes[] = {
62 "1280x1024x75",
63 "1024x768x75",
64 "1024x768x70",
65 "1024x768x60",
66 "1024x768x87i",
67 "832x624x74", /* rounding error, 74.55 Hz aka "832x624x75" */
68 "800x600x75",
69 "800x600x72",
70 "800x600x60",
71 "800x600x56",
72 "640x480x75",
73 "640x480x72",
74 "640x480x67",
75 "640x480x60",
76 "720x400x87", /* rounding error, 87.85 Hz aka "720x400x88" */
77 "720x400x70",
78 };
79
80 #ifdef EDIDVERBOSE
81 struct edid_vendor {
82 const char *vendor;
83 const char *name;
84 };
85
86 struct edid_product {
87 const char *vendor;
88 uint16_t product;
89 const char *name;
90 };
91
92 #include <dev/videomode/ediddevs_data.h>
93 #endif /* EDIDVERBOSE */
94
95 const char *
edid_findvendor(const char * vendor)96 edid_findvendor(const char *vendor)
97 {
98 #ifdef EDIDVERBOSE
99 int n;
100
101 for (n = 0; n < edid_nvendors; n++)
102 if (memcmp(edid_vendors[n].vendor, vendor, 3) == 0)
103 return edid_vendors[n].name;
104 #endif
105 return NULL;
106 }
107
108 const char *
edid_findproduct(const char * vendor,uint16_t product)109 edid_findproduct(const char *vendor, uint16_t product)
110 {
111 #ifdef EDIDVERBOSE
112 int n;
113
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 */
119 return NULL;
120
121 }
122
123 void
edid_strchomp(char * ptr)124 edid_strchomp(char *ptr)
125 {
126 for (;;) {
127 switch (*ptr) {
128 case '\0':
129 return;
130 case '\r':
131 case '\n':
132 *ptr = '\0';
133 return;
134 }
135 ptr++;
136 }
137 }
138
139 int
edid_is_valid(uint8_t * d)140 edid_is_valid(uint8_t *d)
141 {
142 int sum = 0, i;
143 uint8_t sig[8] = EDID_SIGNATURE;
144
145 if (memcmp(d, sig, 8) != 0)
146 return EINVAL;
147
148 for (i = 0; i < 128; i++)
149 sum += d[i];
150 if ((sum & 0xff) != 0)
151 return EINVAL;
152
153 return 0;
154 }
155
156 #ifdef EDID_DEBUG
157 void
edid_print(struct edid_info * edid)158 edid_print(struct edid_info *edid)
159 {
160 int i;
161
162 if (edid == NULL)
163 return;
164 printf("Vendor: [%s] %s\n", edid->edid_vendor, edid->edid_vendorname);
165 printf("Product: [%04X] %s\n", edid->edid_product,
166 edid->edid_productname);
167 printf("Serial number: %s\n", edid->edid_serial);
168 printf("Manufactured %d Week %d\n",
169 edid->edid_year, edid->edid_week);
170 printf("EDID Version %d.%d\n", edid->edid_version,
171 edid->edid_revision);
172 printf("EDID Comment: %s\n", edid->edid_comment);
173
174 printf("Video Input: %x\n", edid->edid_video_input);
175 if (edid->edid_video_input & EDID_VIDEO_INPUT_DIGITAL) {
176 printf("\tDigital");
177 if (edid->edid_video_input & EDID_VIDEO_INPUT_DFP1_COMPAT)
178 printf(" (DFP 1.x compatible)");
179 printf("\n");
180 } else {
181 printf("\tAnalog\n");
182 switch (EDID_VIDEO_INPUT_LEVEL(edid->edid_video_input)) {
183 case 0:
184 printf("\t-0.7, 0.3V\n");
185 break;
186 case 1:
187 printf("\t-0.714, 0.286V\n");
188 break;
189 case 2:
190 printf("\t-1.0, 0.4V\n");
191 break;
192 case 3:
193 printf("\t-0.7, 0.0V\n");
194 break;
195 }
196 if (edid->edid_video_input & EDID_VIDEO_INPUT_BLANK_TO_BLACK)
197 printf("\tBlank-to-black setup\n");
198 if (edid->edid_video_input & EDID_VIDEO_INPUT_SEPARATE_SYNCS)
199 printf("\tSeparate syncs\n");
200 if (edid->edid_video_input & EDID_VIDEO_INPUT_COMPOSITE_SYNC)
201 printf("\tComposite sync\n");
202 if (edid->edid_video_input & EDID_VIDEO_INPUT_SYNC_ON_GRN)
203 printf("\tSync on green\n");
204 if (edid->edid_video_input & EDID_VIDEO_INPUT_SERRATION)
205 printf("\tSerration vsync\n");
206 }
207
208 printf("Gamma: %d.%02d\n",
209 edid->edid_gamma / 100, edid->edid_gamma % 100);
210
211 printf("Max Size: %d cm x %d cm\n",
212 edid->edid_max_hsize, edid->edid_max_vsize);
213
214 printf("Features: %x\n", edid->edid_features);
215 if (edid->edid_features & EDID_FEATURES_STANDBY)
216 printf("\tDPMS standby\n");
217 if (edid->edid_features & EDID_FEATURES_SUSPEND)
218 printf("\tDPMS suspend\n");
219 if (edid->edid_features & EDID_FEATURES_ACTIVE_OFF)
220 printf("\tDPMS active-off\n");
221 switch (EDID_FEATURES_DISP_TYPE(edid->edid_features)) {
222 case EDID_FEATURES_DISP_TYPE_MONO:
223 printf("\tMonochrome\n");
224 break;
225 case EDID_FEATURES_DISP_TYPE_RGB:
226 printf("\tRGB\n");
227 break;
228 case EDID_FEATURES_DISP_TYPE_NON_RGB:
229 printf("\tMulticolor\n");
230 break;
231 case EDID_FEATURES_DISP_TYPE_UNDEFINED:
232 printf("\tUndefined monitor type\n");
233 break;
234 }
235 if (edid->edid_features & EDID_FEATURES_STD_COLOR)
236 printf("\tStandard color space\n");
237 if (edid->edid_features & EDID_FEATURES_PREFERRED_TIMING)
238 printf("\tPreferred timing\n");
239 if (edid->edid_features & EDID_FEATURES_DEFAULT_GTF)
240 printf("\tDefault GTF supported\n");
241
242 printf("Chroma Info:\n");
243 printf("\tRed X: 0.%03d\n", edid->edid_chroma.ec_redx);
244 printf("\tRed Y: 0.%03d\n", edid->edid_chroma.ec_redy);
245 printf("\tGrn X: 0.%03d\n", edid->edid_chroma.ec_greenx);
246 printf("\tGrn Y: 0.%03d\n", edid->edid_chroma.ec_greeny);
247 printf("\tBlu X: 0.%03d\n", edid->edid_chroma.ec_bluex);
248 printf("\tBlu Y: 0.%03d\n", edid->edid_chroma.ec_bluey);
249 printf("\tWht X: 0.%03d\n", edid->edid_chroma.ec_whitex);
250 printf("\tWht Y: 0.%03d\n", edid->edid_chroma.ec_whitey);
251
252 if (edid->edid_have_range) {
253 printf("Range:\n");
254 printf("\tHorizontal: %d - %d kHz\n",
255 edid->edid_range.er_min_hfreq,
256 edid->edid_range.er_max_hfreq);
257 printf("\tVertical: %d - %d Hz\n",
258 edid->edid_range.er_min_vfreq,
259 edid->edid_range.er_max_vfreq);
260 printf("\tMax Dot Clock: %d MHz\n",
261 edid->edid_range.er_max_clock);
262 if (edid->edid_range.er_have_gtf2) {
263 printf("\tGTF2 hfreq: %d\n",
264 edid->edid_range.er_gtf2_hfreq);
265 printf("\tGTF2 C: %d\n", edid->edid_range.er_gtf2_c);
266 printf("\tGTF2 M: %d\n", edid->edid_range.er_gtf2_m);
267 printf("\tGTF2 J: %d\n", edid->edid_range.er_gtf2_j);
268 printf("\tGTF2 K: %d\n", edid->edid_range.er_gtf2_k);
269 }
270 }
271 printf("Video modes:\n");
272 for (i = 0; i < edid->edid_nmodes; i++) {
273 printf("\t%dx%d @ %dHz",
274 edid->edid_modes[i].hdisplay,
275 edid->edid_modes[i].vdisplay,
276 DIVIDE(DIVIDE(edid->edid_modes[i].dot_clock * 1000,
277 edid->edid_modes[i].htotal),
278 edid->edid_modes[i].vtotal));
279 printf(" (%d %d %d %d %d %d %d",
280 edid->edid_modes[i].dot_clock,
281 edid->edid_modes[i].hsync_start,
282 edid->edid_modes[i].hsync_end,
283 edid->edid_modes[i].htotal,
284 edid->edid_modes[i].vsync_start,
285 edid->edid_modes[i].vsync_end,
286 edid->edid_modes[i].vtotal);
287 printf(" %s%sH %s%sV)\n",
288 edid->edid_modes[i].flags & VID_PHSYNC ? "+" : "",
289 edid->edid_modes[i].flags & VID_NHSYNC ? "-" : "",
290 edid->edid_modes[i].flags & VID_PVSYNC ? "+" : "",
291 edid->edid_modes[i].flags & VID_NVSYNC ? "-" : "");
292 }
293 if (edid->edid_preferred_mode)
294 printf("Preferred mode: %dx%d @ %dHz\n",
295 edid->edid_preferred_mode->hdisplay,
296 edid->edid_preferred_mode->vdisplay,
297 DIVIDE(DIVIDE(edid->edid_preferred_mode->dot_clock * 1000,
298 edid->edid_preferred_mode->htotal),
299 edid->edid_preferred_mode->vtotal));
300
301 printf("Number of extension blocks: %d\n", edid->edid_ext_block_count);
302 }
303 #endif
304
305 const struct videomode *
edid_mode_lookup_list(const char * name)306 edid_mode_lookup_list(const char *name)
307 {
308 int i;
309
310 for (i = 0; i < videomode_count; i++)
311 if (strcmp(name, videomode_list[i].name) == 0)
312 return &videomode_list[i];
313 return NULL;
314 }
315
316 struct videomode *
edid_search_mode(struct edid_info * edid,const struct videomode * mode)317 edid_search_mode(struct edid_info *edid, const struct videomode *mode)
318 {
319 int refresh, i;
320
321 refresh = DIVIDE(DIVIDE(mode->dot_clock * 1000,
322 mode->htotal), mode->vtotal);
323 for (i = 0; i < edid->edid_nmodes; i++) {
324 if (mode->hdisplay == edid->edid_modes[i].hdisplay &&
325 mode->vdisplay == edid->edid_modes[i].vdisplay &&
326 refresh == DIVIDE(DIVIDE(
327 edid->edid_modes[i].dot_clock * 1000,
328 edid->edid_modes[i].htotal),
329 edid->edid_modes[i].vtotal)) {
330 return &edid->edid_modes[i];
331 }
332 }
333 return NULL;
334 }
335
336 int
edid_std_timing(uint8_t * data,struct videomode * vmp)337 edid_std_timing(uint8_t *data, struct videomode *vmp)
338 {
339 unsigned x, y, f;
340 const struct videomode *lookup;
341 char name[80];
342
343 if ((data[0] == 1 && data[1] == 1) ||
344 (data[0] == 0 && data[1] == 0) ||
345 (data[0] == 0x20 && data[1] == 0x20))
346 return 0;
347
348 x = EDID_STD_TIMING_HRES(data);
349 switch (EDID_STD_TIMING_RATIO(data)) {
350 case EDID_STD_TIMING_RATIO_16_10:
351 y = x * 10 / 16;
352 break;
353 case EDID_STD_TIMING_RATIO_4_3:
354 y = x * 3 / 4;
355 break;
356 case EDID_STD_TIMING_RATIO_5_4:
357 y = x * 4 / 5;
358 break;
359 case EDID_STD_TIMING_RATIO_16_9:
360 default:
361 y = x * 9 / 16;
362 break;
363 }
364 f = EDID_STD_TIMING_VFREQ(data);
365
366 /* first try to lookup the mode as a DMT timing */
367 snprintf(name, sizeof(name), "%dx%dx%d", x, y, f);
368 if ((lookup = edid_mode_lookup_list(name)) != NULL) {
369 *vmp = *lookup;
370 } else {
371 /* failing that, calculate it using gtf */
372 /*
373 * Hmm. I'm not using alternate GTF timings, which
374 * could, in theory, be present.
375 */
376 vesagtf_mode(x, y, f, vmp);
377 }
378 return 1;
379 }
380
381 int
edid_det_timing(uint8_t * data,struct videomode * vmp)382 edid_det_timing(uint8_t *data, struct videomode *vmp)
383 {
384 unsigned hactive, hblank, hsyncwid, hsyncoff;
385 unsigned vactive, vblank, vsyncwid, vsyncoff;
386 uint8_t flags;
387
388 flags = EDID_DET_TIMING_FLAGS(data);
389
390 /* we don't support stereo modes (for now) */
391 if (flags & (EDID_DET_TIMING_FLAG_STEREO |
392 EDID_DET_TIMING_FLAG_STEREO_MODE))
393 return 0;
394
395 vmp->dot_clock = EDID_DET_TIMING_DOT_CLOCK(data) / 1000;
396
397 hactive = EDID_DET_TIMING_HACTIVE(data);
398 hblank = EDID_DET_TIMING_HBLANK(data);
399 hsyncwid = EDID_DET_TIMING_HSYNC_WIDTH(data);
400 hsyncoff = EDID_DET_TIMING_HSYNC_OFFSET(data);
401
402 vactive = EDID_DET_TIMING_VACTIVE(data);
403 vblank = EDID_DET_TIMING_VBLANK(data);
404 vsyncwid = EDID_DET_TIMING_VSYNC_WIDTH(data);
405 vsyncoff = EDID_DET_TIMING_VSYNC_OFFSET(data);
406
407 /* Borders are contained within the blank areas. */
408
409 vmp->hdisplay = hactive;
410 vmp->htotal = hactive + hblank;
411 vmp->hsync_start = hactive + hsyncoff;
412 vmp->hsync_end = vmp->hsync_start + hsyncwid;
413
414 vmp->vdisplay = vactive;
415 vmp->vtotal = vactive + vblank;
416 vmp->vsync_start = vactive + vsyncoff;
417 vmp->vsync_end = vmp->vsync_start + vsyncwid;
418
419 vmp->flags = 0;
420
421 if (flags & EDID_DET_TIMING_FLAG_INTERLACE)
422 vmp->flags |= VID_INTERLACE;
423 if (flags & EDID_DET_TIMING_FLAG_HSYNC_POSITIVE)
424 vmp->flags |= VID_PHSYNC;
425 else
426 vmp->flags |= VID_NHSYNC;
427
428 if (flags & EDID_DET_TIMING_FLAG_VSYNC_POSITIVE)
429 vmp->flags |= VID_PVSYNC;
430 else
431 vmp->flags |= VID_NVSYNC;
432
433 return 1;
434 }
435
bump_preferred_mode(struct edid_info * edid,struct videomode * m)436 void bump_preferred_mode(struct edid_info *edid, struct videomode *m)
437 {
438 /*
439 * XXX
440 * Iiyama 4800 series monitors may have their native resolution in the
441 * 2nd detailed timing descriptor instead of the 1st. Try to detect
442 * that here and pick the native mode anyway.
443 */
444 if (edid->edid_preferred_mode == NULL) {
445 edid->edid_preferred_mode = m;
446 } else if ((strncmp(edid->edid_vendor, "IVM", 3) == 0) &&
447 (edid->edid_product == 0x4800) &&
448 (edid->edid_preferred_mode->dot_clock < m->dot_clock))
449 edid->edid_preferred_mode = m;
450 }
451
452 void
edid_block(struct edid_info * edid,uint8_t * data)453 edid_block(struct edid_info *edid, uint8_t *data)
454 {
455 int i;
456 struct videomode mode, *exist_mode;
457
458 if (EDID_BLOCK_IS_DET_TIMING(data)) {
459 if (!edid_det_timing(data, &mode))
460 return;
461 /* Does this mode already exist? */
462 exist_mode = edid_search_mode(edid, &mode);
463 if (exist_mode != NULL) {
464 *exist_mode = mode;
465 bump_preferred_mode(edid, exist_mode);
466 } else {
467 edid->edid_modes[edid->edid_nmodes] = mode;
468 bump_preferred_mode(edid,
469 &edid->edid_modes[edid->edid_nmodes]);
470 edid->edid_nmodes++;
471 }
472 return;
473 }
474
475 switch (EDID_BLOCK_TYPE(data)) {
476 case EDID_DESC_BLOCK_TYPE_SERIAL:
477 memcpy(edid->edid_serial, data + EDID_DESC_ASCII_DATA_OFFSET,
478 EDID_DESC_ASCII_DATA_LEN);
479 edid->edid_serial[EDID_DESC_ASCII_DATA_LEN] = 0;
480 break;
481
482 case EDID_DESC_BLOCK_TYPE_ASCII:
483 memcpy(edid->edid_comment, data + EDID_DESC_ASCII_DATA_OFFSET,
484 EDID_DESC_ASCII_DATA_LEN);
485 edid->edid_comment[EDID_DESC_ASCII_DATA_LEN] = 0;
486 break;
487
488 case EDID_DESC_BLOCK_TYPE_RANGE:
489 edid->edid_have_range = 1;
490 edid->edid_range.er_min_vfreq = EDID_DESC_RANGE_MIN_VFREQ(data);
491 edid->edid_range.er_max_vfreq = EDID_DESC_RANGE_MAX_VFREQ(data);
492 edid->edid_range.er_min_hfreq = EDID_DESC_RANGE_MIN_HFREQ(data);
493 edid->edid_range.er_max_hfreq = EDID_DESC_RANGE_MAX_HFREQ(data);
494 edid->edid_range.er_max_clock = EDID_DESC_RANGE_MAX_CLOCK(data);
495 if (!EDID_DESC_RANGE_HAVE_GTF2(data))
496 break;
497 edid->edid_range.er_have_gtf2 = 1;
498 edid->edid_range.er_gtf2_hfreq =
499 EDID_DESC_RANGE_GTF2_HFREQ(data);
500 edid->edid_range.er_gtf2_c = EDID_DESC_RANGE_GTF2_C(data);
501 edid->edid_range.er_gtf2_m = EDID_DESC_RANGE_GTF2_M(data);
502 edid->edid_range.er_gtf2_j = EDID_DESC_RANGE_GTF2_J(data);
503 edid->edid_range.er_gtf2_k = EDID_DESC_RANGE_GTF2_K(data);
504 break;
505
506 case EDID_DESC_BLOCK_TYPE_NAME:
507 /* copy the product name into place */
508 memcpy(edid->edid_productname,
509 data + EDID_DESC_ASCII_DATA_OFFSET,
510 EDID_DESC_ASCII_DATA_LEN);
511 edid->edid_productname[EDID_DESC_ASCII_DATA_LEN] = '\0';
512 break;
513
514 case EDID_DESC_BLOCK_TYPE_STD_TIMING:
515 data += EDID_DESC_STD_TIMING_START;
516 for (i = 0; i < EDID_DESC_STD_TIMING_COUNT; i++) {
517 if (edid_std_timing(data, &mode)) {
518 /* Does this mode already exist? */
519 exist_mode = edid_search_mode(edid, &mode);
520 if (exist_mode == NULL) {
521 edid->edid_modes[edid->edid_nmodes] =
522 mode;
523 edid->edid_nmodes++;
524 }
525 }
526 data += 2;
527 }
528 break;
529
530 case EDID_DESC_BLOCK_TYPE_COLOR_POINT:
531 /* XXX: not implemented yet */
532 break;
533 }
534 }
535
536 /*
537 * Gets EDID version in BCD, e.g. EDID v1.3 returned as 0x0103
538 */
539 int
edid_parse(const char * devname,uint8_t * data,struct edid_info * edid)540 edid_parse(const char *devname, uint8_t *data, struct edid_info *edid)
541 {
542 uint16_t manfid, estmodes;
543 const struct videomode *vmp;
544 int i;
545 const char *name;
546 int max_dotclock = 0;
547 int mhz;
548
549 if (edid_is_valid(data) != 0)
550 return -1;
551
552 /* get product identification */
553 manfid = EDID_VENDOR_ID(data);
554 edid->edid_vendor[0] = EDID_MANFID_0(manfid);
555 edid->edid_vendor[1] = EDID_MANFID_1(manfid);
556 edid->edid_vendor[2] = EDID_MANFID_2(manfid);
557 edid->edid_vendor[3] = 0; /* null terminate for convenience */
558
559 edid->edid_product = data[EDID_OFFSET_PRODUCT_ID] +
560 (data[EDID_OFFSET_PRODUCT_ID + 1] << 8);
561
562 name = edid_findvendor(edid->edid_vendor);
563 if (name != NULL) {
564 strlcpy(edid->edid_vendorname, name,
565 sizeof(edid->edid_vendorname));
566 } else
567 edid->edid_vendorname[0] = '\0';
568
569 name = edid_findproduct(edid->edid_vendor, edid->edid_product);
570 if (name != NULL) {
571 strlcpy(edid->edid_productname, name,
572 sizeof(edid->edid_productname));
573 } else
574 edid->edid_productname[0] = '\0';
575
576 edid->edid_comment[0] = '\0';
577
578 snprintf(edid->edid_serial, sizeof(edid->edid_serial), "%08x",
579 EDID_SERIAL_NUMBER(data));
580
581 edid->edid_week = EDID_WEEK(data);
582 edid->edid_year = EDID_YEAR(data);
583
584 /* get edid revision */
585 edid->edid_version = EDID_VERSION(data);
586 edid->edid_revision = EDID_REVISION(data);
587
588 edid->edid_video_input = EDID_VIDEO_INPUT(data);
589 edid->edid_max_hsize = EDID_MAX_HSIZE(data);
590 edid->edid_max_vsize = EDID_MAX_VSIZE(data);
591
592 edid->edid_gamma = EDID_GAMMA(data);
593 edid->edid_features = EDID_FEATURES(data);
594
595 edid->edid_chroma.ec_redx = EDID_CHROMA_REDX(data);
596 edid->edid_chroma.ec_redy = EDID_CHROMA_REDX(data);
597 edid->edid_chroma.ec_greenx = EDID_CHROMA_GREENX(data);
598 edid->edid_chroma.ec_greeny = EDID_CHROMA_GREENY(data);
599 edid->edid_chroma.ec_bluex = EDID_CHROMA_BLUEX(data);
600 edid->edid_chroma.ec_bluey = EDID_CHROMA_BLUEY(data);
601 edid->edid_chroma.ec_whitex = EDID_CHROMA_WHITEX(data);
602 edid->edid_chroma.ec_whitey = EDID_CHROMA_WHITEY(data);
603
604 edid->edid_ext_block_count = EDID_EXT_BLOCK_COUNT(data);
605
606 /* lookup established modes */
607 edid->edid_nmodes = 0;
608 edid->edid_preferred_mode = NULL;
609 estmodes = EDID_EST_TIMING(data);
610 /* Iterate in established timing order */
611 for (i = 15; i >= 0; i--) {
612 if (estmodes & (1 << i)) {
613 vmp = edid_mode_lookup_list(_edid_modes[i]);
614 if (vmp != NULL) {
615 edid->edid_modes[edid->edid_nmodes] = *vmp;
616 edid->edid_nmodes++;
617 }
618 #ifdef DIAGNOSTIC
619 else
620 printf("%s: no data for est. mode %s\n",
621 devname, _edid_modes[i]);
622 #endif
623 }
624 }
625
626 /* do standard timing section */
627 for (i = 0; i < EDID_STD_TIMING_COUNT; i++) {
628 struct videomode mode, *exist_mode;
629 if (edid_std_timing(data + EDID_OFFSET_STD_TIMING + i * 2,
630 &mode)) {
631 /* Does this mode already exist? */
632 exist_mode = edid_search_mode(edid, &mode);
633 if (exist_mode == NULL) {
634 edid->edid_modes[edid->edid_nmodes] = mode;
635 edid->edid_nmodes++;
636 }
637 }
638 }
639 /* do detailed timings and descriptors */
640 for (i = 0; i < EDID_BLOCK_COUNT; i++) {
641 edid_block(edid, data + EDID_OFFSET_DESC_BLOCK +
642 i * EDID_BLOCK_SIZE);
643 }
644
645 edid_strchomp(edid->edid_vendorname);
646 edid_strchomp(edid->edid_productname);
647 edid_strchomp(edid->edid_serial);
648 edid_strchomp(edid->edid_comment);
649
650 /*
651 * XXX
652 * some monitors lie about their maximum supported dot clock
653 * by claiming to support modes which need a higher dot clock
654 * than the stated maximum.
655 * For sanity's sake we bump it to the highest dot clock we find
656 * in the list of supported modes
657 */
658 for (i = 0; i < edid->edid_nmodes; i++)
659 if (edid->edid_modes[i].dot_clock > max_dotclock)
660 max_dotclock = edid->edid_modes[i].dot_clock;
661
662 #ifdef DIAGNOSTIC
663 printf("%s: max_dotclock according to supported modes: %d\n",
664 devname, max_dotclock);
665 #endif
666
667 mhz = (max_dotclock + 999) / 1000;
668
669 if (edid->edid_have_range) {
670 if (mhz > edid->edid_range.er_max_clock)
671 edid->edid_range.er_max_clock = mhz;
672 } else
673 edid->edid_range.er_max_clock = mhz;
674
675 return 0;
676 }
677
678