interpret_edid.c revision 35c4bbdf
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 *, 43 struct established_timings *); 44static void get_std_timing_section(Uchar *, struct std_timings *, 45 struct edid_version *); 46static void fetch_detailed_block(Uchar * c, struct edid_version *ver, 47 struct detailed_monitor_section *det_mon); 48static void get_dt_md_section(Uchar *, struct edid_version *, 49 struct detailed_monitor_section *det_mon); 50static void copy_string(Uchar *, Uchar *); 51static void get_dst_timing_section(Uchar *, struct std_timings *, 52 struct edid_version *); 53static void get_monitor_ranges(Uchar *, struct monitor_ranges *); 54static void get_whitepoint_section(Uchar *, struct whitePoints *); 55static void get_detailed_timing_section(Uchar *, struct detailed_timings *); 56static Bool validate_version(int scrnIndex, struct edid_version *); 57 58static void 59find_ranges_section(struct detailed_monitor_section *det, void *ranges) 60{ 61 if (det->type == DS_RANGES && det->section.ranges.max_clock) 62 *(struct monitor_ranges **) ranges = &det->section.ranges; 63} 64 65static void 66find_max_detailed_clock(struct detailed_monitor_section *det, void *ret) 67{ 68 if (det->type == DT) { 69 *(int *) ret = max(*((int *) ret), 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 90 xf86ForEachDetailedBlock(m, find_max_detailed_clock, &clock); 91 if (clock && (ranges->max_clock * 1e6 < clock)) { 92 xf86Msg(X_WARNING, "EDID timing clock %.2f exceeds claimed max " 93 "%dMHz, fixing\n", clock / 1.0e6, ranges->max_clock); 94 ranges->max_clock = (clock + 999999) / 1e6; 95 } 96 } 97} 98 99struct det_hv_parameter { 100 int real_hsize; 101 int real_vsize; 102 float target_aspect; 103}; 104 105static void 106handle_detailed_hvsize(struct detailed_monitor_section *det_mon, void *data) 107{ 108 struct det_hv_parameter *p = (struct det_hv_parameter *) data; 109 float timing_aspect; 110 111 if (det_mon->type == DT) { 112 struct detailed_timings *timing; 113 114 timing = &det_mon->section.d_timings; 115 116 if (!timing->v_size) 117 return; 118 119 timing_aspect = (float) timing->h_size / timing->v_size; 120 if (fabs(1 - (timing_aspect / p->target_aspect)) < 0.05) { 121 p->real_hsize = max(p->real_hsize, timing->h_size); 122 p->real_vsize = max(p->real_vsize, timing->v_size); 123 } 124 } 125} 126 127static void 128encode_aspect_ratio(xf86MonPtr m) 129{ 130 /* 131 * some monitors encode the aspect ratio instead of the physical size. 132 * try to find the largest detailed timing that matches that aspect 133 * ratio and use that to fill in the feature section. 134 */ 135 if ((m->features.hsize == 16 && m->features.vsize == 9) || 136 (m->features.hsize == 16 && m->features.vsize == 10) || 137 (m->features.hsize == 4 && m->features.vsize == 3) || 138 (m->features.hsize == 5 && m->features.vsize == 4)) { 139 140 struct det_hv_parameter p; 141 142 p.real_hsize = 0; 143 p.real_vsize = 0; 144 p.target_aspect = (float) m->features.hsize / m->features.vsize; 145 146 xf86ForEachDetailedBlock(m, handle_detailed_hvsize, &p); 147 148 if (!p.real_hsize || !p.real_vsize) { 149 m->features.hsize = m->features.vsize = 0; 150 } 151 else if ((m->features.hsize * 10 == p.real_hsize) && 152 (m->features.vsize * 10 == p.real_vsize)) { 153 /* exact match is just unlikely, should do a better check though */ 154 m->features.hsize = m->features.vsize = 0; 155 } 156 else { 157 /* convert mm to cm */ 158 m->features.hsize = (p.real_hsize + 5) / 10; 159 m->features.vsize = (p.real_vsize + 5) / 10; 160 } 161 162 xf86Msg(X_INFO, "Quirked EDID physical size to %dx%d cm\n", 163 m->features.hsize, m->features.vsize); 164 } 165} 166 167xf86MonPtr 168xf86InterpretEDID(int scrnIndex, Uchar * block) 169{ 170 xf86MonPtr m; 171 172 if (!block) 173 return NULL; 174 if (!(m = xnfcalloc(sizeof(xf86Monitor), 1))) 175 return NULL; 176 m->scrnIndex = scrnIndex; 177 m->rawData = block; 178 179 get_vendor_section(SECTION(VENDOR_SECTION, block), &m->vendor); 180 get_version_section(SECTION(VERSION_SECTION, block), &m->ver); 181 if (!validate_version(scrnIndex, &m->ver)) 182 goto error; 183 get_display_section(SECTION(DISPLAY_SECTION, block), &m->features, &m->ver); 184 get_established_timing_section(SECTION(ESTABLISHED_TIMING_SECTION, block), 185 &m->timings1); 186 get_std_timing_section(SECTION(STD_TIMING_SECTION, block), m->timings2, 187 &m->ver); 188 get_dt_md_section(SECTION(DET_TIMING_SECTION, block), &m->ver, m->det_mon); 189 m->no_sections = (int) *(char *) SECTION(NO_EDID, block); 190 191 handle_edid_quirks(m); 192 encode_aspect_ratio(m); 193 194 return m; 195 196 error: 197 free(m); 198 return NULL; 199} 200 201static int 202get_cea_detail_timing(Uchar * blk, xf86MonPtr mon, 203 struct detailed_monitor_section *det_mon) 204{ 205 int dt_num; 206 int dt_offset = ((struct cea_ext_body *) blk)->dt_offset; 207 208 dt_num = 0; 209 210 if (dt_offset < CEA_EXT_MIN_DATA_OFFSET) 211 return dt_num; 212 213 for (; dt_offset < (CEA_EXT_MAX_DATA_OFFSET - DET_TIMING_INFO_LEN) && 214 dt_num < CEA_EXT_DET_TIMING_NUM; _NEXT_DT_MD_SECTION(dt_offset)) { 215 216 fetch_detailed_block(blk + dt_offset, &mon->ver, det_mon + dt_num); 217 dt_num = dt_num + 1; 218 } 219 220 return dt_num; 221} 222 223static void 224handle_cea_detail_block(Uchar * ext, xf86MonPtr mon, 225 handle_detailed_fn fn, void *data) 226{ 227 int i; 228 struct detailed_monitor_section det_mon[CEA_EXT_DET_TIMING_NUM]; 229 int det_mon_num; 230 231 det_mon_num = get_cea_detail_timing(ext, mon, det_mon); 232 233 for (i = 0; i < det_mon_num; i++) 234 fn(det_mon + i, data); 235} 236 237void 238xf86ForEachDetailedBlock(xf86MonPtr mon, handle_detailed_fn fn, void *data) 239{ 240 int i; 241 Uchar *ext; 242 243 if (mon == NULL) 244 return; 245 246 for (i = 0; i < DET_TIMINGS; i++) 247 fn(mon->det_mon + i, data); 248 249 for (i = 0; i < mon->no_sections; i++) { 250 ext = mon->rawData + EDID1_LEN * (i + 1); 251 switch (ext[EXT_TAG]) { 252 case CEA_EXT: 253 handle_cea_detail_block(ext, mon, fn, data); 254 break; 255 case VTB_EXT: 256 case DI_EXT: 257 case LS_EXT: 258 case MI_EXT: 259 break; 260 } 261 } 262} 263 264static struct cea_data_block * 265extract_cea_data_block(Uchar * ext, int data_type) 266{ 267 struct cea_ext_body *cea; 268 struct cea_data_block *data_collection; 269 struct cea_data_block *data_end; 270 271 cea = (struct cea_ext_body *) ext; 272 273 if (cea->dt_offset <= CEA_EXT_MIN_DATA_OFFSET) 274 return NULL; 275 276 data_collection = &cea->data_collection; 277 data_end = (struct cea_data_block *) (cea->dt_offset + ext); 278 279 for (; data_collection < data_end;) { 280 281 if (data_type == data_collection->tag) { 282 return data_collection; 283 } 284 data_collection = (void *) ((unsigned char *) data_collection + 285 data_collection->len + 1); 286 } 287 288 return NULL; 289} 290 291static void 292handle_cea_video_block(Uchar * ext, handle_video_fn fn, void *data) 293{ 294 struct cea_video_block *video; 295 struct cea_video_block *video_end; 296 struct cea_data_block *data_collection; 297 298 data_collection = extract_cea_data_block(ext, CEA_VIDEO_BLK); 299 if (data_collection == NULL) 300 return; 301 302 video = &data_collection->u.video; 303 video_end = (struct cea_video_block *) 304 ((Uchar *) video + data_collection->len); 305 306 for (; video < video_end; video = video + 1) { 307 fn(video, data); 308 } 309} 310 311void 312xf86ForEachVideoBlock(xf86MonPtr mon, handle_video_fn fn, void *data) 313{ 314 int i; 315 Uchar *ext; 316 317 if (mon == NULL) 318 return; 319 320 for (i = 0; i < mon->no_sections; i++) { 321 ext = mon->rawData + EDID1_LEN * (i + 1); 322 switch (ext[EXT_TAG]) { 323 case CEA_EXT: 324 handle_cea_video_block(ext, fn, data); 325 break; 326 case VTB_EXT: 327 case DI_EXT: 328 case LS_EXT: 329 case MI_EXT: 330 break; 331 } 332 } 333} 334 335static Bool 336cea_db_offsets(Uchar *cea, int *start, int *end) 337{ 338 /* Data block offset in CEA extension block */ 339 *start = CEA_EXT_MIN_DATA_OFFSET; 340 *end = cea[2]; 341 if (*end == 0) 342 *end = CEA_EXT_MAX_DATA_OFFSET; 343 if (*end < CEA_EXT_MIN_DATA_OFFSET || *end > CEA_EXT_MAX_DATA_OFFSET) 344 return FALSE; 345 return TRUE; 346} 347 348static int 349cea_db_len(Uchar *db) 350{ 351 return db[0] & 0x1f; 352} 353 354static int 355cea_db_tag(Uchar *db) 356{ 357 return db[0] >> 5; 358} 359 360typedef void (*handle_cea_db_fn) (Uchar *, void *); 361 362static void 363cea_for_each_db(xf86MonPtr mon, handle_cea_db_fn fn, void *data) 364{ 365 int i; 366 367 if (!mon) 368 return; 369 370 if (!(mon->flags & EDID_COMPLETE_RAWDATA)) 371 return; 372 373 if (!mon->no_sections) 374 return; 375 376 if (!mon->rawData) 377 return; 378 379 for (i = 0; i < mon->no_sections; i++) { 380 int start, end, offset; 381 Uchar *ext; 382 383 ext = mon->rawData + EDID1_LEN * (i + 1); 384 if (ext[EXT_TAG] != CEA_EXT) 385 continue; 386 387 if (!cea_db_offsets(ext, &start, &end)) 388 continue; 389 390 for (offset = start; 391 offset < end && offset + cea_db_len(&ext[offset]) < end; 392 offset += cea_db_len(&ext[offset]) + 1) 393 fn(&ext[offset], data); 394 } 395} 396 397struct find_hdmi_block_data { 398 struct cea_data_block *hdmi; 399}; 400 401static void find_hdmi_block(Uchar *db, void *data) 402{ 403 struct find_hdmi_block_data *result = data; 404 int oui; 405 406 if (cea_db_tag(db) != CEA_VENDOR_BLK) 407 return; 408 409 if (cea_db_len(db) < 5) 410 return; 411 412 oui = (db[3] << 16) | (db[2] << 8) | db[1]; 413 if (oui == IEEE_ID_HDMI) 414 result->hdmi = (struct cea_data_block *)db; 415} 416 417struct cea_data_block *xf86MonitorFindHDMIBlock(xf86MonPtr mon) 418{ 419 struct find_hdmi_block_data result = { NULL }; 420 421 cea_for_each_db(mon, find_hdmi_block, &result); 422 423 return result.hdmi; 424} 425 426xf86MonPtr 427xf86InterpretEEDID(int scrnIndex, Uchar * block) 428{ 429 xf86MonPtr m; 430 431 m = xf86InterpretEDID(scrnIndex, block); 432 if (!m) 433 return NULL; 434 435 /* extension parse */ 436 437 return m; 438} 439 440static void 441get_vendor_section(Uchar * c, struct vendor *r) 442{ 443 r->name[0] = L1; 444 r->name[1] = L2; 445 r->name[2] = L3; 446 r->name[3] = '\0'; 447 448 r->prod_id = PROD_ID; 449 r->serial = SERIAL_NO; 450 r->week = WEEK; 451 r->year = YEAR; 452} 453 454static void 455get_version_section(Uchar * c, struct edid_version *r) 456{ 457 r->version = VERSION; 458 r->revision = REVISION; 459} 460 461static void 462get_display_section(Uchar * c, struct disp_features *r, struct edid_version *v) 463{ 464 r->input_type = INPUT_TYPE; 465 if (!DIGITAL(r->input_type)) { 466 r->input_voltage = INPUT_VOLTAGE; 467 r->input_setup = SETUP; 468 r->input_sync = SYNC; 469 } 470 else if (v->revision == 2 || v->revision == 3) { 471 r->input_dfp = DFP; 472 } 473 else if (v->revision >= 4) { 474 r->input_bpc = BPC; 475 r->input_interface = DIGITAL_INTERFACE; 476 } 477 r->hsize = HSIZE_MAX; 478 r->vsize = VSIZE_MAX; 479 r->gamma = GAMMA; 480 r->dpms = DPMS; 481 r->display_type = DISPLAY_TYPE; 482 r->msc = MSC; 483 r->redx = REDX; 484 r->redy = REDY; 485 r->greenx = GREENX; 486 r->greeny = GREENY; 487 r->bluex = BLUEX; 488 r->bluey = BLUEY; 489 r->whitex = WHITEX; 490 r->whitey = WHITEY; 491} 492 493static void 494get_established_timing_section(Uchar * c, struct established_timings *r) 495{ 496 r->t1 = T1; 497 r->t2 = T2; 498 r->t_manu = T_MANU; 499} 500 501static void 502get_cvt_timing_section(Uchar * c, struct cvt_timings *r) 503{ 504 int i; 505 506 for (i = 0; i < 4; i++) { 507 if (c[0] && c[1] && c[2]) { 508 r[i].height = (c[0] + ((c[1] & 0xF0) << 8) + 1) * 2; 509 switch (c[1] & 0xc0) { 510 case 0x00: 511 r[i].width = r[i].height * 4 / 3; 512 break; 513 case 0x40: 514 r[i].width = r[i].height * 16 / 9; 515 break; 516 case 0x80: 517 r[i].width = r[i].height * 16 / 10; 518 break; 519 case 0xc0: 520 r[i].width = r[i].height * 15 / 9; 521 break; 522 } 523 switch (c[2] & 0x60) { 524 case 0x00: 525 r[i].rate = 50; 526 break; 527 case 0x20: 528 r[i].rate = 60; 529 break; 530 case 0x40: 531 r[i].rate = 75; 532 break; 533 case 0x60: 534 r[i].rate = 85; 535 break; 536 } 537 r[i].rates = c[2] & 0x1f; 538 } 539 else { 540 return; 541 } 542 c += 3; 543 } 544} 545 546static void 547get_std_timing_section(Uchar * c, struct std_timings *r, struct edid_version *v) 548{ 549 int i; 550 551 for (i = 0; i < STD_TIMINGS; i++) { 552 if (VALID_TIMING) { 553 r[i].hsize = HSIZE1; 554 VSIZE1(r[i].vsize); 555 r[i].refresh = REFRESH_R; 556 r[i].id = STD_TIMING_ID; 557 } 558 else { 559 r[i].hsize = r[i].vsize = r[i].refresh = r[i].id = 0; 560 } 561 NEXT_STD_TIMING; 562 } 563} 564 565static const unsigned char empty_block[18]; 566 567static void 568fetch_detailed_block(Uchar * c, struct edid_version *ver, 569 struct detailed_monitor_section *det_mon) 570{ 571 if (ver->version == 1 && ver->revision >= 1 && IS_MONITOR_DESC) { 572 switch (MONITOR_DESC_TYPE) { 573 case SERIAL_NUMBER: 574 det_mon->type = DS_SERIAL; 575 copy_string(c, det_mon->section.serial); 576 break; 577 case ASCII_STR: 578 det_mon->type = DS_ASCII_STR; 579 copy_string(c, det_mon->section.ascii_data); 580 break; 581 case MONITOR_RANGES: 582 det_mon->type = DS_RANGES; 583 get_monitor_ranges(c, &det_mon->section.ranges); 584 break; 585 case MONITOR_NAME: 586 det_mon->type = DS_NAME; 587 copy_string(c, det_mon->section.name); 588 break; 589 case ADD_COLOR_POINT: 590 det_mon->type = DS_WHITE_P; 591 get_whitepoint_section(c, det_mon->section.wp); 592 break; 593 case ADD_STD_TIMINGS: 594 det_mon->type = DS_STD_TIMINGS; 595 get_dst_timing_section(c, det_mon->section.std_t, ver); 596 break; 597 case COLOR_MANAGEMENT_DATA: 598 det_mon->type = DS_CMD; 599 break; 600 case CVT_3BYTE_DATA: 601 det_mon->type = DS_CVT; 602 get_cvt_timing_section(c, det_mon->section.cvt); 603 break; 604 case ADD_EST_TIMINGS: 605 det_mon->type = DS_EST_III; 606 memcpy(det_mon->section.est_iii, c + 6, 6); 607 break; 608 case ADD_DUMMY: 609 det_mon->type = DS_DUMMY; 610 break; 611 default: 612 det_mon->type = DS_UNKOWN; 613 break; 614 } 615 if (c[3] <= 0x0F && memcmp(c, empty_block, sizeof(empty_block))) { 616 det_mon->type = DS_VENDOR + c[3]; 617 } 618 } 619 else { 620 det_mon->type = DT; 621 get_detailed_timing_section(c, &det_mon->section.d_timings); 622 } 623} 624 625static void 626get_dt_md_section(Uchar * c, struct edid_version *ver, 627 struct detailed_monitor_section *det_mon) 628{ 629 int i; 630 631 for (i = 0; i < DET_TIMINGS; i++) { 632 fetch_detailed_block(c, ver, det_mon + i); 633 NEXT_DT_MD_SECTION; 634 } 635} 636 637static void 638copy_string(Uchar * c, Uchar * s) 639{ 640 int i; 641 642 c = c + 5; 643 for (i = 0; (i < 13 && *c != 0x0A); i++) 644 *(s++) = *(c++); 645 *s = 0; 646 while (i-- && (*--s == 0x20)) 647 *s = 0; 648} 649 650static void 651get_dst_timing_section(Uchar * c, struct std_timings *t, struct edid_version *v) 652{ 653 int j; 654 655 c = c + 5; 656 for (j = 0; j < 5; j++) { 657 t[j].hsize = HSIZE1; 658 VSIZE1(t[j].vsize); 659 t[j].refresh = REFRESH_R; 660 t[j].id = STD_TIMING_ID; 661 NEXT_STD_TIMING; 662 } 663} 664 665static void 666get_monitor_ranges(Uchar * c, struct monitor_ranges *r) 667{ 668 r->min_v = MIN_V; 669 r->max_v = MAX_V; 670 r->min_h = MIN_H; 671 r->max_h = MAX_H; 672 r->max_clock = 0; 673 if (MAX_CLOCK != 0xff) /* is specified? */ 674 r->max_clock = MAX_CLOCK * 10 + 5; 675 if (HAVE_2ND_GTF) { 676 r->gtf_2nd_f = F_2ND_GTF; 677 r->gtf_2nd_c = C_2ND_GTF; 678 r->gtf_2nd_m = M_2ND_GTF; 679 r->gtf_2nd_k = K_2ND_GTF; 680 r->gtf_2nd_j = J_2ND_GTF; 681 } 682 else { 683 r->gtf_2nd_f = 0; 684 } 685 if (HAVE_CVT) { 686 r->max_clock_khz = MAX_CLOCK_KHZ; 687 r->max_clock = r->max_clock_khz / 1000; 688 r->maxwidth = MAXWIDTH; 689 r->supported_aspect = SUPPORTED_ASPECT; 690 r->preferred_aspect = PREFERRED_ASPECT; 691 r->supported_blanking = SUPPORTED_BLANKING; 692 r->supported_scaling = SUPPORTED_SCALING; 693 r->preferred_refresh = PREFERRED_REFRESH; 694 } 695 else { 696 r->max_clock_khz = 0; 697 } 698} 699 700static void 701get_whitepoint_section(Uchar * c, struct whitePoints *wp) 702{ 703 wp[0].white_x = WHITEX1; 704 wp[0].white_y = WHITEY1; 705 wp[1].white_x = WHITEX2; 706 wp[1].white_y = WHITEY2; 707 wp[0].index = WHITE_INDEX1; 708 wp[1].index = WHITE_INDEX2; 709 wp[0].white_gamma = WHITE_GAMMA1; 710 wp[1].white_gamma = WHITE_GAMMA2; 711} 712 713static void 714get_detailed_timing_section(Uchar * c, struct detailed_timings *r) 715{ 716 r->clock = PIXEL_CLOCK; 717 r->h_active = H_ACTIVE; 718 r->h_blanking = H_BLANK; 719 r->v_active = V_ACTIVE; 720 r->v_blanking = V_BLANK; 721 r->h_sync_off = H_SYNC_OFF; 722 r->h_sync_width = H_SYNC_WIDTH; 723 r->v_sync_off = V_SYNC_OFF; 724 r->v_sync_width = V_SYNC_WIDTH; 725 r->h_size = H_SIZE; 726 r->v_size = V_SIZE; 727 r->h_border = H_BORDER; 728 r->v_border = V_BORDER; 729 r->interlaced = INTERLACED; 730 r->stereo = STEREO; 731 r->stereo_1 = STEREO1; 732 r->sync = SYNC_T; 733 r->misc = MISC; 734} 735 736#define MAX_EDID_MINOR 4 737 738static Bool 739validate_version(int scrnIndex, struct edid_version *r) 740{ 741 if (r->version != 1) { 742 xf86DrvMsg(scrnIndex, X_ERROR, "Unknown EDID version %d\n", r->version); 743 return FALSE; 744 } 745 746 if (r->revision > MAX_EDID_MINOR) 747 xf86DrvMsg(scrnIndex, X_WARNING, 748 "Assuming version 1.%d is compatible with 1.%d\n", 749 r->revision, MAX_EDID_MINOR); 750 751 return TRUE; 752} 753 754/* 755 * Returns true if HDMI, false if definitely not or unknown. 756 */ 757Bool 758xf86MonitorIsHDMI(xf86MonPtr mon) 759{ 760 return xf86MonitorFindHDMIBlock(mon) != NULL; 761} 762