interpret_edid.c revision 52397711
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 * on the rights to use, copy, modify, merge, publish, distribute, sub 9 * license, and/or sell copies of the Software, and to permit persons to whom 10 * them 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 MERCHANTIBILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER 20 * IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 * 23 * interpret_edid.c: interpret a primary EDID block 24 */ 25 26#ifdef HAVE_XORG_CONFIG_H 27#include <xorg-config.h> 28#endif 29 30#include "misc.h" 31#include "xf86.h" 32#include "xf86_OSproc.h" 33#define _PARSE_EDID_ 34#include "xf86DDC.h" 35#include <string.h> 36 37static void get_vendor_section(Uchar*, struct vendor *); 38static void get_version_section(Uchar*, struct edid_version *); 39static void get_display_section(Uchar*, struct disp_features *, 40 struct edid_version *); 41static void get_established_timing_section(Uchar*, struct established_timings *); 42static void get_std_timing_section(Uchar*, struct std_timings *, 43 struct edid_version *); 44static void get_dt_md_section(Uchar *, struct edid_version *, 45 struct detailed_monitor_section *det_mon); 46static void copy_string(Uchar *, Uchar *); 47static void get_dst_timing_section(Uchar *, struct std_timings *, 48 struct edid_version *); 49static void get_monitor_ranges(Uchar *, struct monitor_ranges *); 50static void get_whitepoint_section(Uchar *, struct whitePoints *); 51static void get_detailed_timing_section(Uchar*, struct detailed_timings *); 52static Bool validate_version(int scrnIndex, struct edid_version *); 53 54static void 55handle_edid_quirks(xf86MonPtr m) 56{ 57 int i, j; 58 struct detailed_timings *preferred_timing; 59 struct monitor_ranges *ranges; 60 61 /* 62 * max_clock is only encoded in EDID in tens of MHz, so occasionally we 63 * find a monitor claiming a max of 160 with a mode requiring 162, or 64 * similar. Strictly we should refuse to round up too far, but let's 65 * see how well this works. 66 */ 67 for (i = 0; i < 4; i++) { 68 if (m->det_mon[i].type == DS_RANGES) { 69 ranges = &m->det_mon[i].section.ranges; 70 for (j = 0; j < 4; j++) { 71 if (m->det_mon[j].type == DT) { 72 preferred_timing = &m->det_mon[j].section.d_timings; 73 if (!ranges->max_clock) continue; /* zero is legal */ 74 if (ranges->max_clock * 1000000 < preferred_timing->clock) { 75 xf86Msg(X_WARNING, 76 "EDID preferred timing clock %.2fMHz exceeds " 77 "claimed max %dMHz, fixing\n", 78 preferred_timing->clock / 1.0e6, 79 ranges->max_clock); 80 ranges->max_clock = 81 (preferred_timing->clock+999999)/1000000; 82 return; 83 } 84 } 85 } 86 } 87 } 88 89 /* 90 * some monitors encode the aspect ratio instead of the physical size. 91 * try to find the largest detailed timing that matches that aspect 92 * ratio and use that to fill in the feature section. 93 */ 94 if ((m->features.hsize == 16 && m->features.vsize == 9) || 95 (m->features.hsize == 16 && m->features.vsize == 10) || 96 (m->features.hsize == 4 && m->features.vsize == 3) || 97 (m->features.hsize == 5 && m->features.vsize == 4)) { 98 int real_hsize = 0, real_vsize = 0; 99 float target_aspect, timing_aspect; 100 101 target_aspect = (float)m->features.hsize / (float)m->features.vsize; 102 for (i = 0; i < 4; i++) { 103 if (m->det_mon[i].type == DT) { 104 struct detailed_timings *timing; 105 timing = &m->det_mon[i].section.d_timings; 106 107 if (!timing->v_size) 108 continue; 109 110 timing_aspect = (float)timing->h_size / (float)timing->v_size; 111 if (fabs(1 - (timing_aspect / target_aspect)) < 0.05) { 112 real_hsize = max(real_hsize, timing->h_size); 113 real_vsize = max(real_vsize, timing->v_size); 114 } 115 } 116 } 117 118 if (!real_hsize || !real_vsize) { 119 m->features.hsize = m->features.vsize = 0; 120 } else if ((m->features.hsize * 10 == real_hsize) && 121 (m->features.vsize * 10 == real_vsize)) { 122 /* exact match is just unlikely, should do a better check though */ 123 m->features.hsize = m->features.vsize = 0; 124 } else { 125 /* convert mm to cm */ 126 m->features.hsize = (real_hsize + 5) / 10; 127 m->features.vsize = (real_vsize + 5) / 10; 128 } 129 130 xf86Msg(X_INFO, "Quirked EDID physical size to %dx%d cm\n", 131 m->features.hsize, m->features.vsize); 132 } 133} 134 135xf86MonPtr 136xf86InterpretEDID(int scrnIndex, Uchar *block) 137{ 138 xf86MonPtr m; 139 140 if (!block) return NULL; 141 if (! (m = xnfcalloc(sizeof(xf86Monitor),1))) return NULL; 142 m->scrnIndex = scrnIndex; 143 m->rawData = block; 144 145 get_vendor_section(SECTION(VENDOR_SECTION,block),&m->vendor); 146 get_version_section(SECTION(VERSION_SECTION,block),&m->ver); 147 if (!validate_version(scrnIndex, &m->ver)) goto error; 148 get_display_section(SECTION(DISPLAY_SECTION,block),&m->features, 149 &m->ver); 150 get_established_timing_section(SECTION(ESTABLISHED_TIMING_SECTION,block), 151 &m->timings1); 152 get_std_timing_section(SECTION(STD_TIMING_SECTION,block),m->timings2, 153 &m->ver); 154 get_dt_md_section(SECTION(DET_TIMING_SECTION,block),&m->ver, m->det_mon); 155 m->no_sections = (int)*(char *)SECTION(NO_EDID,block); 156 157 handle_edid_quirks(m); 158 159 return (m); 160 161 error: 162 xfree(m); 163 return NULL; 164} 165 166xf86MonPtr 167xf86InterpretEEDID(int scrnIndex, Uchar *block) 168{ 169 xf86MonPtr m; 170 171 m = xf86InterpretEDID(scrnIndex, block); 172 if (!m) 173 return NULL; 174 175 /* extension parse */ 176 177 return m; 178} 179 180static void 181get_vendor_section(Uchar *c, struct vendor *r) 182{ 183 r->name[0] = L1; 184 r->name[1] = L2; 185 r->name[2] = L3; 186 r->name[3] = '\0'; 187 188 r->prod_id = PROD_ID; 189 r->serial = SERIAL_NO; 190 r->week = WEEK; 191 r->year = YEAR; 192} 193 194static void 195get_version_section(Uchar *c, struct edid_version *r) 196{ 197 r->version = VERSION; 198 r->revision = REVISION; 199} 200 201static void 202get_display_section(Uchar *c, struct disp_features *r, 203 struct edid_version *v) 204{ 205 r->input_type = INPUT_TYPE; 206 if (!DIGITAL(r->input_type)) { 207 r->input_voltage = INPUT_VOLTAGE; 208 r->input_setup = SETUP; 209 r->input_sync = SYNC; 210 } else if (v->revision == 2 || v->revision == 3) { 211 r->input_dfp = DFP; 212 } else if (v->revision >= 4) { 213 r->input_bpc = BPC; 214 r->input_interface = DIGITAL_INTERFACE; 215 } 216 r->hsize = HSIZE_MAX; 217 r->vsize = VSIZE_MAX; 218 r->gamma = GAMMA; 219 r->dpms = DPMS; 220 r->display_type = DISPLAY_TYPE; 221 r->msc = MSC; 222 r->redx = REDX; 223 r->redy = REDY; 224 r->greenx = GREENX; 225 r->greeny = GREENY; 226 r->bluex = BLUEX; 227 r->bluey = BLUEY; 228 r->whitex = WHITEX; 229 r->whitey = WHITEY; 230} 231 232static void 233get_established_timing_section(Uchar *c, struct established_timings *r) 234{ 235 r->t1 = T1; 236 r->t2 = T2; 237 r->t_manu = T_MANU; 238} 239 240static void 241get_cvt_timing_section(Uchar *c, struct cvt_timings *r) 242{ 243 int i; 244 245 for (i = 0; i < 4; i++) { 246 if (c[0] && c[1] && c[2]) { 247 r[i].height = (c[0] + ((c[1] & 0xF0) << 8) + 1) * 2; 248 switch (c[1] & 0xc0) { 249 case 0x00: r[i].width = r[i].height * 4 / 3; break; 250 case 0x40: r[i].width = r[i].height * 16 / 9; break; 251 case 0x80: r[i].width = r[i].height * 16 / 10; break; 252 case 0xc0: r[i].width = r[i].height * 15 / 9; break; 253 } 254 switch (c[2] & 0x60) { 255 case 0x00: r[i].rate = 50; break; 256 case 0x20: r[i].rate = 60; break; 257 case 0x40: r[i].rate = 75; break; 258 case 0x60: r[i].rate = 85; break; 259 } 260 r[i].rates = c[2] & 0x1f; 261 } else { 262 return; 263 } 264 c += 3; 265 } 266} 267 268static void 269get_std_timing_section(Uchar *c, struct std_timings *r, 270 struct edid_version *v) 271{ 272 int i; 273 274 for (i=0;i<STD_TIMINGS;i++){ 275 if (VALID_TIMING) { 276 r[i].hsize = HSIZE1; 277 VSIZE1(r[i].vsize); 278 r[i].refresh = REFRESH_R; 279 r[i].id = STD_TIMING_ID; 280 } else { 281 r[i].hsize = r[i].vsize = r[i].refresh = r[i].id = 0; 282 } 283 NEXT_STD_TIMING; 284 } 285} 286 287static const unsigned char empty_block[18]; 288 289static void 290get_dt_md_section(Uchar *c, struct edid_version *ver, 291 struct detailed_monitor_section *det_mon) 292{ 293 int i; 294 295 for (i=0;i<DET_TIMINGS;i++) { 296 if (ver->version == 1 && ver->revision >= 1 && IS_MONITOR_DESC) { 297 298 switch (MONITOR_DESC_TYPE) { 299 case SERIAL_NUMBER: 300 det_mon[i].type = DS_SERIAL; 301 copy_string(c,det_mon[i].section.serial); 302 break; 303 case ASCII_STR: 304 det_mon[i].type = DS_ASCII_STR; 305 copy_string(c,det_mon[i].section.ascii_data); 306 break; 307 case MONITOR_RANGES: 308 det_mon[i].type = DS_RANGES; 309 get_monitor_ranges(c,&det_mon[i].section.ranges); 310 break; 311 case MONITOR_NAME: 312 det_mon[i].type = DS_NAME; 313 copy_string(c,det_mon[i].section.name); 314 break; 315 case ADD_COLOR_POINT: 316 det_mon[i].type = DS_WHITE_P; 317 get_whitepoint_section(c,det_mon[i].section.wp); 318 break; 319 case ADD_STD_TIMINGS: 320 det_mon[i].type = DS_STD_TIMINGS; 321 get_dst_timing_section(c,det_mon[i].section.std_t, ver); 322 break; 323 case COLOR_MANAGEMENT_DATA: 324 det_mon[i].type = DS_CMD; 325 break; 326 case CVT_3BYTE_DATA: 327 det_mon[i].type = DS_CVT; 328 get_cvt_timing_section(c, det_mon[i].section.cvt); 329 break; 330 case ADD_EST_TIMINGS: 331 det_mon[i].type = DS_EST_III; 332 memcpy(det_mon[i].section.est_iii, c + 6, 6); 333 break; 334 case ADD_DUMMY: 335 det_mon[i].type = DS_DUMMY; 336 break; 337 default: 338 det_mon[i].type = DS_UNKOWN; 339 break; 340 } 341 if (c[3] <= 0x0F && memcmp(c, empty_block, sizeof(empty_block))) { 342 det_mon[i].type = DS_VENDOR + c[3]; 343 } 344 } else { 345 det_mon[i].type = DT; 346 get_detailed_timing_section(c,&det_mon[i].section.d_timings); 347 } 348 NEXT_DT_MD_SECTION; 349 } 350} 351 352static void 353copy_string(Uchar *c, Uchar *s) 354{ 355 int i; 356 c = c + 5; 357 for (i = 0; (i < 13 && *c != 0x0A); i++) 358 *(s++) = *(c++); 359 *s = 0; 360 while (i-- && (*--s == 0x20)) *s = 0; 361} 362 363static void 364get_dst_timing_section(Uchar *c, struct std_timings *t, 365 struct edid_version *v) 366{ 367 int j; 368 c = c + 5; 369 for (j = 0; j < 5; j++) { 370 t[j].hsize = HSIZE1; 371 VSIZE1(t[j].vsize); 372 t[j].refresh = REFRESH_R; 373 t[j].id = STD_TIMING_ID; 374 NEXT_STD_TIMING; 375 } 376} 377 378static void 379get_monitor_ranges(Uchar *c, struct monitor_ranges *r) 380{ 381 r->min_v = MIN_V; 382 r->max_v = MAX_V; 383 r->min_h = MIN_H; 384 r->max_h = MAX_H; 385 r->max_clock = 0; 386 if(MAX_CLOCK != 0xff) /* is specified? */ 387 r->max_clock = MAX_CLOCK * 10; 388 if (HAVE_2ND_GTF) { 389 r->gtf_2nd_f = F_2ND_GTF; 390 r->gtf_2nd_c = C_2ND_GTF; 391 r->gtf_2nd_m = M_2ND_GTF; 392 r->gtf_2nd_k = K_2ND_GTF; 393 r->gtf_2nd_j = J_2ND_GTF; 394 } else { 395 r->gtf_2nd_f = 0; 396 } 397 if (HAVE_CVT) { 398 r->max_clock_khz = MAX_CLOCK_KHZ; 399 r->max_clock = r->max_clock_khz / 1000; 400 r->maxwidth = MAXWIDTH; 401 r->supported_aspect = SUPPORTED_ASPECT; 402 r->preferred_aspect = PREFERRED_ASPECT; 403 r->supported_blanking = SUPPORTED_BLANKING; 404 r->supported_scaling = SUPPORTED_SCALING; 405 r->preferred_refresh = PREFERRED_REFRESH; 406 } else { 407 r->max_clock_khz = 0; 408 } 409} 410 411static void 412get_whitepoint_section(Uchar *c, struct whitePoints *wp) 413{ 414 wp[0].white_x = WHITEX1; 415 wp[0].white_y = WHITEY1; 416 wp[1].white_x = WHITEX2; 417 wp[1].white_y = WHITEY2; 418 wp[0].index = WHITE_INDEX1; 419 wp[1].index = WHITE_INDEX2; 420 wp[0].white_gamma = WHITE_GAMMA1; 421 wp[1].white_gamma = WHITE_GAMMA2; 422} 423 424static void 425get_detailed_timing_section(Uchar *c, struct detailed_timings *r) 426{ 427 r->clock = PIXEL_CLOCK; 428 r->h_active = H_ACTIVE; 429 r->h_blanking = H_BLANK; 430 r->v_active = V_ACTIVE; 431 r->v_blanking = V_BLANK; 432 r->h_sync_off = H_SYNC_OFF; 433 r->h_sync_width = H_SYNC_WIDTH; 434 r->v_sync_off = V_SYNC_OFF; 435 r->v_sync_width = V_SYNC_WIDTH; 436 r->h_size = H_SIZE; 437 r->v_size = V_SIZE; 438 r->h_border = H_BORDER; 439 r->v_border = V_BORDER; 440 r->interlaced = INTERLACED; 441 r->stereo = STEREO; 442 r->stereo_1 = STEREO1; 443 r->sync = SYNC_T; 444 r->misc = MISC; 445} 446 447#define MAX_EDID_MINOR 4 448 449static Bool 450validate_version(int scrnIndex, struct edid_version *r) 451{ 452 if (r->version != 1) { 453 xf86DrvMsg(scrnIndex, X_ERROR, "Unknown EDID version %d\n", 454 r->version); 455 return FALSE; 456 } 457 458 if (r->revision > MAX_EDID_MINOR) 459 xf86DrvMsg(scrnIndex, X_WARNING, 460 "Assuming version 1.%d is compatible with 1.%d\n", 461 r->revision, MAX_EDID_MINOR); 462 463 return TRUE; 464} 465 466/* 467 * Returns true if HDMI, false if definitely not or unknown. 468 */ 469_X_EXPORT Bool 470xf86MonitorIsHDMI(xf86MonPtr mon) 471{ 472 int i = 0, version, offset; 473 char *edid = NULL; 474 475 if (!mon) 476 return FALSE; 477 478 if (!(mon->flags & EDID_COMPLETE_RAWDATA)) 479 return FALSE; 480 481 if (!mon->no_sections) 482 return FALSE; 483 484 edid = (char *)mon->rawData; 485 if (!edid) 486 return FALSE; 487 488 /* find the CEA extension block */ 489 for (i = 1; i <= mon->no_sections; i++) 490 if (edid[i * 128] == 0x02) 491 break; 492 if (i == mon->no_sections + 1) 493 return FALSE; 494 edid += (i * 128); 495 496 version = edid[1]; 497 offset = edid[2]; 498 if (version < 3 || offset < 4) 499 return FALSE; 500 501 /* walk the cea data blocks */ 502 for (i = 4; i < offset; i += (edid[i] & 0x1f) + 1) { 503 char *x = edid + i; 504 505 /* find a vendor specific block */ 506 if ((x[0] & 0xe0) >> 5 == 0x03) { 507 int oui = (x[3] << 16) + (x[2] << 8) + x[1]; 508 509 /* find the HDMI vendor OUI */ 510 if (oui == 0x000c03) 511 return TRUE; 512 } 513 } 514 515 /* guess it's not HDMI after all */ 516 return FALSE; 517} 518