interpret_edid.c revision 706f2543
1/* 2 * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE> 3 * Copyright 2007 Red Hat, Inc. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 * 24 * interpret_edid.c: interpret a primary EDID block 25 */ 26 27#ifdef HAVE_XORG_CONFIG_H 28#include <xorg-config.h> 29#endif 30 31#include "misc.h" 32#include "xf86.h" 33#include "xf86_OSproc.h" 34#define _PARSE_EDID_ 35#include "xf86DDC.h" 36#include <string.h> 37 38static void get_vendor_section(Uchar*, struct vendor *); 39static void get_version_section(Uchar*, struct edid_version *); 40static void get_display_section(Uchar*, struct disp_features *, 41 struct edid_version *); 42static void get_established_timing_section(Uchar*, struct established_timings *); 43static void get_std_timing_section(Uchar*, struct std_timings *, 44 struct edid_version *); 45static void fetch_detailed_block(Uchar *c, struct edid_version *ver, 46 struct detailed_monitor_section *det_mon); 47static void get_dt_md_section(Uchar *, struct edid_version *, 48 struct detailed_monitor_section *det_mon); 49static void copy_string(Uchar *, Uchar *); 50static void get_dst_timing_section(Uchar *, struct std_timings *, 51 struct edid_version *); 52static void get_monitor_ranges(Uchar *, struct monitor_ranges *); 53static void get_whitepoint_section(Uchar *, struct whitePoints *); 54static void get_detailed_timing_section(Uchar*, struct detailed_timings *); 55static Bool validate_version(int scrnIndex, struct edid_version *); 56 57static void 58find_ranges_section(struct detailed_monitor_section *det, void *ranges) 59{ 60 if (det->type == DS_RANGES && det->section.ranges.max_clock) 61 *(struct monitor_ranges **)ranges = &det->section.ranges; 62} 63 64static void 65find_max_detailed_clock(struct detailed_monitor_section *det, void *ret) 66{ 67 if (det->type == DT) { 68 *(int *)ret = max(*((int *)ret), 69 det->section.d_timings.clock); 70 } 71} 72 73static void 74handle_edid_quirks(xf86MonPtr m) 75{ 76 struct monitor_ranges *ranges = NULL; 77 78 /* 79 * max_clock is only encoded in EDID in tens of MHz, so occasionally we 80 * find a monitor claiming a max of 160 with a mode requiring 162, or 81 * similar. Strictly we should refuse to round up too far, but let's 82 * see how well this works. 83 */ 84 85 /* Try to find Monitor Range and max clock, then re-set range value*/ 86 xf86ForEachDetailedBlock(m, find_ranges_section, &ranges); 87 if (ranges && ranges->max_clock) { 88 int clock = 0; 89 xf86ForEachDetailedBlock(m, find_max_detailed_clock, &clock); 90 if (clock && (ranges->max_clock * 1e6 < clock)) { 91 xf86Msg(X_WARNING, "EDID timing clock %.2f exceeds claimed max " 92 "%dMHz, fixing\n", clock / 1.0e6, ranges->max_clock); 93 ranges->max_clock = (clock+999999)/1e6; 94 } 95 } 96} 97 98struct det_hv_parameter { 99 int real_hsize; 100 int real_vsize; 101 float target_aspect; 102}; 103 104static void handle_detailed_hvsize(struct detailed_monitor_section *det_mon, 105 void *data) 106{ 107 struct det_hv_parameter *p = (struct det_hv_parameter *)data; 108 float timing_aspect; 109 110 if (det_mon->type == DT) { 111 struct detailed_timings *timing; 112 timing = &det_mon->section.d_timings; 113 114 if (!timing->v_size) 115 return; 116 117 timing_aspect = (float)timing->h_size / timing->v_size; 118 if (fabs(1 - (timing_aspect / p->target_aspect)) < 0.05) { 119 p->real_hsize = max(p->real_hsize, timing->h_size); 120 p->real_vsize = max(p->real_vsize, timing->v_size); 121 } 122 } 123} 124 125static void encode_aspect_ratio(xf86MonPtr m) 126{ 127 /* 128 * some monitors encode the aspect ratio instead of the physical size. 129 * try to find the largest detailed timing that matches that aspect 130 * ratio and use that to fill in the feature section. 131 */ 132 if ((m->features.hsize == 16 && m->features.vsize == 9) || 133 (m->features.hsize == 16 && m->features.vsize == 10) || 134 (m->features.hsize == 4 && m->features.vsize == 3) || 135 (m->features.hsize == 5 && m->features.vsize == 4)) { 136 137 struct det_hv_parameter p; 138 p.real_hsize = 0; 139 p.real_vsize = 0; 140 p.target_aspect = (float)m->features.hsize /m->features.vsize; 141 142 xf86ForEachDetailedBlock(m, handle_detailed_hvsize, &p); 143 144 if (!p.real_hsize || !p.real_vsize) { 145 m->features.hsize = m->features.vsize = 0; 146 } else if ((m->features.hsize * 10 == p.real_hsize) && 147 (m->features.vsize * 10 == p.real_vsize)) { 148 /* exact match is just unlikely, should do a better check though */ 149 m->features.hsize = m->features.vsize = 0; 150 } else { 151 /* convert mm to cm */ 152 m->features.hsize = (p.real_hsize + 5) / 10; 153 m->features.vsize = (p.real_vsize + 5) / 10; 154 } 155 156 xf86Msg(X_INFO, "Quirked EDID physical size to %dx%d cm\n", 157 m->features.hsize, m->features.vsize); 158 } 159} 160 161xf86MonPtr 162xf86InterpretEDID(int scrnIndex, Uchar *block) 163{ 164 xf86MonPtr m; 165 166 if (!block) return NULL; 167 if (! (m = xnfcalloc(sizeof(xf86Monitor),1))) return NULL; 168 m->scrnIndex = scrnIndex; 169 m->rawData = block; 170 171 get_vendor_section(SECTION(VENDOR_SECTION,block),&m->vendor); 172 get_version_section(SECTION(VERSION_SECTION,block),&m->ver); 173 if (!validate_version(scrnIndex, &m->ver)) goto error; 174 get_display_section(SECTION(DISPLAY_SECTION,block),&m->features, 175 &m->ver); 176 get_established_timing_section(SECTION(ESTABLISHED_TIMING_SECTION,block), 177 &m->timings1); 178 get_std_timing_section(SECTION(STD_TIMING_SECTION,block),m->timings2, 179 &m->ver); 180 get_dt_md_section(SECTION(DET_TIMING_SECTION,block),&m->ver, m->det_mon); 181 m->no_sections = (int)*(char *)SECTION(NO_EDID,block); 182 183 handle_edid_quirks(m); 184 encode_aspect_ratio(m); 185 186 return m; 187 188 error: 189 free(m); 190 return NULL; 191} 192 193static int get_cea_detail_timing(Uchar *blk, xf86MonPtr mon, 194 struct detailed_monitor_section *det_mon) 195{ 196 int dt_num; 197 int dt_offset = ((struct cea_ext_body *)blk)->dt_offset; 198 199 dt_num = 0; 200 201 if (dt_offset < CEA_EXT_MIN_DATA_OFFSET) 202 return dt_num; 203 204 for (; dt_offset < (CEA_EXT_MAX_DATA_OFFSET - DET_TIMING_INFO_LEN) && 205 dt_num < CEA_EXT_DET_TIMING_NUM; 206 _NEXT_DT_MD_SECTION(dt_offset)) { 207 208 fetch_detailed_block(blk + dt_offset, &mon->ver, det_mon + dt_num); 209 dt_num = dt_num + 1 ; 210 } 211 212 return dt_num; 213} 214 215static void handle_cea_detail_block(Uchar *ext, xf86MonPtr mon, 216 handle_detailed_fn fn, 217 void *data) 218{ 219 int i; 220 struct detailed_monitor_section det_mon[CEA_EXT_DET_TIMING_NUM]; 221 int det_mon_num; 222 223 det_mon_num = get_cea_detail_timing(ext, mon, det_mon); 224 225 for (i = 0; i < det_mon_num; i++) 226 fn(det_mon + i, data); 227} 228 229void xf86ForEachDetailedBlock(xf86MonPtr mon, 230 handle_detailed_fn fn, 231 void *data) 232{ 233 int i; 234 Uchar *ext; 235 236 if (mon == NULL) 237 return; 238 239 for (i = 0; i < DET_TIMINGS; i++) 240 fn(mon->det_mon + i, data); 241 242 for (i = 0; i < mon->no_sections; i++) { 243 ext = mon->rawData + EDID1_LEN * (i + 1); 244 switch (ext[EXT_TAG]){ 245 case CEA_EXT: 246 handle_cea_detail_block(ext, mon, fn, data); 247 break; 248 case VTB_EXT: 249 case DI_EXT: 250 case LS_EXT: 251 case MI_EXT: 252 break; 253 } 254 } 255} 256 257static struct cea_data_block * 258extract_cea_data_block(Uchar *ext, int data_type) 259{ 260 struct cea_ext_body *cea; 261 struct cea_data_block *data_collection; 262 struct cea_data_block *data_end; 263 264 cea = (struct cea_ext_body *)ext; 265 266 if (cea->dt_offset <= CEA_EXT_MIN_DATA_OFFSET) 267 return NULL; 268 269 data_collection = &cea->data_collection; 270 data_end = (struct cea_data_block *)(cea->dt_offset + ext); 271 272 for ( ;data_collection < data_end;) { 273 274 if (data_type == data_collection->tag) { 275 return data_collection; 276 } 277 data_collection = (void *)((unsigned char *)data_collection + 278 data_collection->len + 1); 279 } 280 281 return NULL; 282} 283 284static void handle_cea_video_block(Uchar *ext, handle_video_fn fn, void *data) 285{ 286 struct cea_video_block *video; 287 struct cea_video_block *video_end; 288 struct cea_data_block *data_collection; 289 290 data_collection = extract_cea_data_block(ext, CEA_VIDEO_BLK); 291 if (data_collection == NULL) 292 return; 293 294 video = &data_collection->u.video; 295 video_end = (struct cea_video_block *) 296 ((Uchar *)video + data_collection->len); 297 298 for (; video < video_end; video = video + 1) { 299 fn(video, data); 300 } 301} 302 303void xf86ForEachVideoBlock(xf86MonPtr mon, 304 handle_video_fn fn, 305 void *data) 306{ 307 int i; 308 Uchar *ext; 309 310 if (mon == NULL) 311 return; 312 313 for (i = 0; i < mon->no_sections; i++) { 314 ext = mon->rawData + EDID1_LEN * (i + 1); 315 switch (ext[EXT_TAG]) { 316 case CEA_EXT: 317 handle_cea_video_block(ext, fn, data); 318 break; 319 case VTB_EXT: 320 case DI_EXT: 321 case LS_EXT: 322 case MI_EXT: 323 break; 324 } 325 } 326} 327 328xf86MonPtr 329xf86InterpretEEDID(int scrnIndex, Uchar *block) 330{ 331 xf86MonPtr m; 332 333 m = xf86InterpretEDID(scrnIndex, block); 334 if (!m) 335 return NULL; 336 337 /* extension parse */ 338 339 return m; 340} 341 342static void 343get_vendor_section(Uchar *c, struct vendor *r) 344{ 345 r->name[0] = L1; 346 r->name[1] = L2; 347 r->name[2] = L3; 348 r->name[3] = '\0'; 349 350 r->prod_id = PROD_ID; 351 r->serial = SERIAL_NO; 352 r->week = WEEK; 353 r->year = YEAR; 354} 355 356static void 357get_version_section(Uchar *c, struct edid_version *r) 358{ 359 r->version = VERSION; 360 r->revision = REVISION; 361} 362 363static void 364get_display_section(Uchar *c, struct disp_features *r, 365 struct edid_version *v) 366{ 367 r->input_type = INPUT_TYPE; 368 if (!DIGITAL(r->input_type)) { 369 r->input_voltage = INPUT_VOLTAGE; 370 r->input_setup = SETUP; 371 r->input_sync = SYNC; 372 } else if (v->revision == 2 || v->revision == 3) { 373 r->input_dfp = DFP; 374 } else if (v->revision >= 4) { 375 r->input_bpc = BPC; 376 r->input_interface = DIGITAL_INTERFACE; 377 } 378 r->hsize = HSIZE_MAX; 379 r->vsize = VSIZE_MAX; 380 r->gamma = GAMMA; 381 r->dpms = DPMS; 382 r->display_type = DISPLAY_TYPE; 383 r->msc = MSC; 384 r->redx = REDX; 385 r->redy = REDY; 386 r->greenx = GREENX; 387 r->greeny = GREENY; 388 r->bluex = BLUEX; 389 r->bluey = BLUEY; 390 r->whitex = WHITEX; 391 r->whitey = WHITEY; 392} 393 394static void 395get_established_timing_section(Uchar *c, struct established_timings *r) 396{ 397 r->t1 = T1; 398 r->t2 = T2; 399 r->t_manu = T_MANU; 400} 401 402static void 403get_cvt_timing_section(Uchar *c, struct cvt_timings *r) 404{ 405 int i; 406 407 for (i = 0; i < 4; i++) { 408 if (c[0] && c[1] && c[2]) { 409 r[i].height = (c[0] + ((c[1] & 0xF0) << 8) + 1) * 2; 410 switch (c[1] & 0xc0) { 411 case 0x00: r[i].width = r[i].height * 4 / 3; break; 412 case 0x40: r[i].width = r[i].height * 16 / 9; break; 413 case 0x80: r[i].width = r[i].height * 16 / 10; break; 414 case 0xc0: r[i].width = r[i].height * 15 / 9; break; 415 } 416 switch (c[2] & 0x60) { 417 case 0x00: r[i].rate = 50; break; 418 case 0x20: r[i].rate = 60; break; 419 case 0x40: r[i].rate = 75; break; 420 case 0x60: r[i].rate = 85; break; 421 } 422 r[i].rates = c[2] & 0x1f; 423 } else { 424 return; 425 } 426 c += 3; 427 } 428} 429 430static void 431get_std_timing_section(Uchar *c, struct std_timings *r, 432 struct edid_version *v) 433{ 434 int i; 435 436 for (i=0;i<STD_TIMINGS;i++){ 437 if (VALID_TIMING) { 438 r[i].hsize = HSIZE1; 439 VSIZE1(r[i].vsize); 440 r[i].refresh = REFRESH_R; 441 r[i].id = STD_TIMING_ID; 442 } else { 443 r[i].hsize = r[i].vsize = r[i].refresh = r[i].id = 0; 444 } 445 NEXT_STD_TIMING; 446 } 447} 448 449static const unsigned char empty_block[18]; 450 451static void 452fetch_detailed_block(Uchar *c, struct edid_version *ver, 453 struct detailed_monitor_section *det_mon) 454{ 455 if (ver->version == 1 && ver->revision >= 1 && IS_MONITOR_DESC) { 456 switch (MONITOR_DESC_TYPE) { 457 case SERIAL_NUMBER: 458 det_mon->type = DS_SERIAL; 459 copy_string(c,det_mon->section.serial); 460 break; 461 case ASCII_STR: 462 det_mon->type = DS_ASCII_STR; 463 copy_string(c,det_mon->section.ascii_data); 464 break; 465 case MONITOR_RANGES: 466 det_mon->type = DS_RANGES; 467 get_monitor_ranges(c,&det_mon->section.ranges); 468 break; 469 case MONITOR_NAME: 470 det_mon->type = DS_NAME; 471 copy_string(c,det_mon->section.name); 472 break; 473 case ADD_COLOR_POINT: 474 det_mon->type = DS_WHITE_P; 475 get_whitepoint_section(c,det_mon->section.wp); 476 break; 477 case ADD_STD_TIMINGS: 478 det_mon->type = DS_STD_TIMINGS; 479 get_dst_timing_section(c,det_mon->section.std_t, ver); 480 break; 481 case COLOR_MANAGEMENT_DATA: 482 det_mon->type = DS_CMD; 483 break; 484 case CVT_3BYTE_DATA: 485 det_mon->type = DS_CVT; 486 get_cvt_timing_section(c, det_mon->section.cvt); 487 break; 488 case ADD_EST_TIMINGS: 489 det_mon->type = DS_EST_III; 490 memcpy(det_mon->section.est_iii, c + 6, 6); 491 break; 492 case ADD_DUMMY: 493 det_mon->type = DS_DUMMY; 494 break; 495 default: 496 det_mon->type = DS_UNKOWN; 497 break; 498 } 499 if (c[3] <= 0x0F && memcmp(c, empty_block, sizeof(empty_block))) { 500 det_mon->type = DS_VENDOR + c[3]; 501 } 502 } else { 503 det_mon->type = DT; 504 get_detailed_timing_section(c, &det_mon->section.d_timings); 505 } 506} 507 508static void 509get_dt_md_section(Uchar *c, struct edid_version *ver, 510 struct detailed_monitor_section *det_mon) 511{ 512 int i; 513 514 for (i=0; i < DET_TIMINGS; i++) { 515 fetch_detailed_block(c, ver, det_mon + i); 516 NEXT_DT_MD_SECTION; 517 } 518} 519 520static void 521copy_string(Uchar *c, Uchar *s) 522{ 523 int i; 524 c = c + 5; 525 for (i = 0; (i < 13 && *c != 0x0A); i++) 526 *(s++) = *(c++); 527 *s = 0; 528 while (i-- && (*--s == 0x20)) *s = 0; 529} 530 531static void 532get_dst_timing_section(Uchar *c, struct std_timings *t, 533 struct edid_version *v) 534{ 535 int j; 536 c = c + 5; 537 for (j = 0; j < 5; j++) { 538 t[j].hsize = HSIZE1; 539 VSIZE1(t[j].vsize); 540 t[j].refresh = REFRESH_R; 541 t[j].id = STD_TIMING_ID; 542 NEXT_STD_TIMING; 543 } 544} 545 546static void 547get_monitor_ranges(Uchar *c, struct monitor_ranges *r) 548{ 549 r->min_v = MIN_V; 550 r->max_v = MAX_V; 551 r->min_h = MIN_H; 552 r->max_h = MAX_H; 553 r->max_clock = 0; 554 if(MAX_CLOCK != 0xff) /* is specified? */ 555 r->max_clock = MAX_CLOCK * 10 + 5; 556 if (HAVE_2ND_GTF) { 557 r->gtf_2nd_f = F_2ND_GTF; 558 r->gtf_2nd_c = C_2ND_GTF; 559 r->gtf_2nd_m = M_2ND_GTF; 560 r->gtf_2nd_k = K_2ND_GTF; 561 r->gtf_2nd_j = J_2ND_GTF; 562 } else { 563 r->gtf_2nd_f = 0; 564 } 565 if (HAVE_CVT) { 566 r->max_clock_khz = MAX_CLOCK_KHZ; 567 r->max_clock = r->max_clock_khz / 1000; 568 r->maxwidth = MAXWIDTH; 569 r->supported_aspect = SUPPORTED_ASPECT; 570 r->preferred_aspect = PREFERRED_ASPECT; 571 r->supported_blanking = SUPPORTED_BLANKING; 572 r->supported_scaling = SUPPORTED_SCALING; 573 r->preferred_refresh = PREFERRED_REFRESH; 574 } else { 575 r->max_clock_khz = 0; 576 } 577} 578 579static void 580get_whitepoint_section(Uchar *c, struct whitePoints *wp) 581{ 582 wp[0].white_x = WHITEX1; 583 wp[0].white_y = WHITEY1; 584 wp[1].white_x = WHITEX2; 585 wp[1].white_y = WHITEY2; 586 wp[0].index = WHITE_INDEX1; 587 wp[1].index = WHITE_INDEX2; 588 wp[0].white_gamma = WHITE_GAMMA1; 589 wp[1].white_gamma = WHITE_GAMMA2; 590} 591 592static void 593get_detailed_timing_section(Uchar *c, struct detailed_timings *r) 594{ 595 r->clock = PIXEL_CLOCK; 596 r->h_active = H_ACTIVE; 597 r->h_blanking = H_BLANK; 598 r->v_active = V_ACTIVE; 599 r->v_blanking = V_BLANK; 600 r->h_sync_off = H_SYNC_OFF; 601 r->h_sync_width = H_SYNC_WIDTH; 602 r->v_sync_off = V_SYNC_OFF; 603 r->v_sync_width = V_SYNC_WIDTH; 604 r->h_size = H_SIZE; 605 r->v_size = V_SIZE; 606 r->h_border = H_BORDER; 607 r->v_border = V_BORDER; 608 r->interlaced = INTERLACED; 609 r->stereo = STEREO; 610 r->stereo_1 = STEREO1; 611 r->sync = SYNC_T; 612 r->misc = MISC; 613} 614 615#define MAX_EDID_MINOR 4 616 617static Bool 618validate_version(int scrnIndex, struct edid_version *r) 619{ 620 if (r->version != 1) { 621 xf86DrvMsg(scrnIndex, X_ERROR, "Unknown EDID version %d\n", 622 r->version); 623 return FALSE; 624 } 625 626 if (r->revision > MAX_EDID_MINOR) 627 xf86DrvMsg(scrnIndex, X_WARNING, 628 "Assuming version 1.%d is compatible with 1.%d\n", 629 r->revision, MAX_EDID_MINOR); 630 631 return TRUE; 632} 633 634/* 635 * Returns true if HDMI, false if definitely not or unknown. 636 */ 637Bool 638xf86MonitorIsHDMI(xf86MonPtr mon) 639{ 640 int i = 0, version, offset; 641 char *edid = NULL; 642 643 if (!mon) 644 return FALSE; 645 646 if (!(mon->flags & EDID_COMPLETE_RAWDATA)) 647 return FALSE; 648 649 if (!mon->no_sections) 650 return FALSE; 651 652 edid = (char *)mon->rawData; 653 if (!edid) 654 return FALSE; 655 656 /* find the CEA extension block */ 657 for (i = 1; i <= mon->no_sections; i++) 658 if (edid[i * 128] == 0x02) 659 break; 660 if (i == mon->no_sections + 1) 661 return FALSE; 662 edid += (i * 128); 663 664 version = edid[1]; 665 offset = edid[2]; 666 if (version < 3 || offset < 4) 667 return FALSE; 668 669 /* walk the cea data blocks */ 670 for (i = 4; i < offset; i += (edid[i] & 0x1f) + 1) { 671 char *x = edid + i; 672 673 /* find a vendor specific block */ 674 if ((x[0] & 0xe0) >> 5 == 0x03) { 675 int oui = (x[3] << 16) + (x[2] << 8) + x[1]; 676 677 /* find the HDMI vendor OUI */ 678 if (oui == 0x000c03) 679 return TRUE; 680 } 681 } 682 683 /* guess it's not HDMI after all */ 684 return FALSE; 685} 686