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