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 * print_edid.c: print out all information retrieved from display device 25 */ 26 27#ifdef HAVE_XORG_CONFIG_H 28#include <xorg-config.h> 29#endif 30 31/* XXX kinda gross */ 32#define _PARSE_EDID_ 33 34#include "misc.h" 35#include "xf86.h" 36#include "xf86_OSproc.h" 37#include "xf86DDC.h" 38#include "edid.h" 39 40#define EDID_WIDTH 16 41 42static void 43print_vendor(int scrnIndex, struct vendor *c) 44{ 45 xf86DrvMsg(scrnIndex, X_INFO, "Manufacturer: %s Model: %x Serial#: %u\n", 46 (char *) &c->name, c->prod_id, c->serial); 47 xf86DrvMsg(scrnIndex, X_INFO, "Year: %u Week: %u\n", c->year, c->week); 48} 49 50static void 51print_version(int scrnIndex, struct edid_version *c) 52{ 53 xf86DrvMsg(scrnIndex, X_INFO, "EDID Version: %u.%u\n", c->version, 54 c->revision); 55} 56 57static const char *digital_interfaces[] = { 58 "undefined", 59 "DVI", 60 "HDMI-a", 61 "HDMI-b", 62 "MDDI", 63 "DisplayPort", 64 "unknown" 65}; 66 67static void 68print_input_features(int scrnIndex, struct disp_features *c, 69 struct edid_version *v) 70{ 71 if (DIGITAL(c->input_type)) { 72 xf86DrvMsg(scrnIndex, X_INFO, "Digital Display Input\n"); 73 if (v->revision == 2 || v->revision == 3) { 74 if (DFP1(c->input_dfp)) 75 xf86DrvMsg(scrnIndex, X_INFO, "DFP 1.x compatible TMDS\n"); 76 } 77 else if (v->revision >= 4) { 78 int interface = c->input_interface; 79 int bpc = c->input_bpc; 80 81 if (interface > 6) 82 interface = 6; /* unknown */ 83 if (bpc == 0 || bpc == 7) 84 xf86DrvMsg(scrnIndex, X_INFO, "Undefined color depth\n"); 85 else 86 xf86DrvMsg(scrnIndex, X_INFO, "%d bits per channel\n", 87 bpc * 2 + 4); 88 xf86DrvMsg(scrnIndex, X_INFO, "Digital interface is %s\n", 89 digital_interfaces[interface]); 90 } 91 } 92 else { 93 xf86DrvMsg(scrnIndex, X_INFO, "Analog Display Input, "); 94 xf86ErrorF("Input Voltage Level: "); 95 switch (c->input_voltage) { 96 case V070: 97 xf86ErrorF("0.700/0.300 V\n"); 98 break; 99 case V071: 100 xf86ErrorF("0.714/0.286 V\n"); 101 break; 102 case V100: 103 xf86ErrorF("1.000/0.400 V\n"); 104 break; 105 case V007: 106 xf86ErrorF("0.700/0.700 V\n"); 107 break; 108 default: 109 xf86ErrorF("undefined\n"); 110 } 111 if (SIG_SETUP(c->input_setup)) 112 xf86DrvMsg(scrnIndex, X_INFO, "Signal levels configurable\n"); 113 xf86DrvMsg(scrnIndex, X_INFO, "Sync:"); 114 if (SEP_SYNC(c->input_sync)) 115 xf86ErrorF(" Separate"); 116 if (COMP_SYNC(c->input_sync)) 117 xf86ErrorF(" Composite"); 118 if (SYNC_O_GREEN(c->input_sync)) 119 xf86ErrorF(" SyncOnGreen"); 120 if (SYNC_SERR(c->input_sync)) 121 xf86ErrorF("Serration on. " 122 "V.Sync Pulse req. if CompSync or SyncOnGreen\n"); 123 else 124 xf86ErrorF("\n"); 125 } 126} 127 128static void 129print_dpms_features(int scrnIndex, struct disp_features *c, 130 struct edid_version *v) 131{ 132 if (c->dpms) { 133 xf86DrvMsg(scrnIndex, X_INFO, "DPMS capabilities:"); 134 if (DPMS_STANDBY(c->dpms)) 135 xf86ErrorF(" StandBy"); 136 if (DPMS_SUSPEND(c->dpms)) 137 xf86ErrorF(" Suspend"); 138 if (DPMS_OFF(c->dpms)) 139 xf86ErrorF(" Off"); 140 } 141 else 142 xf86DrvMsg(scrnIndex, X_INFO, "No DPMS capabilities specified"); 143 if (!c->input_type) { /* analog */ 144 switch (c->display_type) { 145 case DISP_MONO: 146 xf86ErrorF("; Monochrome/GrayScale Display\n"); 147 break; 148 case DISP_RGB: 149 xf86ErrorF("; RGB/Color Display\n"); 150 break; 151 case DISP_MULTCOLOR: 152 xf86ErrorF("; Non RGB Multicolor Display\n"); 153 break; 154 default: 155 xf86ErrorF("\n"); 156 break; 157 } 158 } 159 else { 160 int enc = c->display_type; 161 162 xf86ErrorF("\n"); 163 xf86DrvMsg(scrnIndex, X_INFO, "Supported color encodings: " 164 "RGB 4:4:4 %s%s\n", 165 enc & DISP_YCRCB444 ? "YCrCb 4:4:4 " : "", 166 enc & DISP_YCRCB422 ? "YCrCb 4:2:2" : ""); 167 } 168 169 if (STD_COLOR_SPACE(c->msc)) 170 xf86DrvMsg(scrnIndex, X_INFO, 171 "Default color space is primary color space\n"); 172 173 if (PREFERRED_TIMING_MODE(c->msc) || v->revision >= 4) { 174 xf86DrvMsg(scrnIndex, X_INFO, 175 "First detailed timing is preferred mode\n"); 176 if (v->revision >= 4) 177 xf86DrvMsg(scrnIndex, X_INFO, 178 "Preferred mode is native pixel format and refresh rate\n"); 179 } 180 else if (v->revision == 3) { 181 xf86DrvMsg(scrnIndex, X_INFO, 182 "First detailed timing not preferred " 183 "mode in violation of standard!\n"); 184 } 185 186 if (v->revision >= 4) { 187 if (GFT_SUPPORTED(c->msc)) { 188 xf86DrvMsg(scrnIndex, X_INFO, "Display is continuous-frequency\n"); 189 } 190 } 191 else { 192 if (GFT_SUPPORTED(c->msc)) 193 xf86DrvMsg(scrnIndex, X_INFO, "GTF timings supported\n"); 194 } 195} 196 197static void 198print_whitepoint(int scrnIndex, struct disp_features *disp) 199{ 200 xf86DrvMsg(scrnIndex, X_INFO, "redX: %.3f redY: %.3f ", 201 disp->redx, disp->redy); 202 xf86ErrorF("greenX: %.3f greenY: %.3f\n", disp->greenx, disp->greeny); 203 xf86DrvMsg(scrnIndex, X_INFO, "blueX: %.3f blueY: %.3f ", 204 disp->bluex, disp->bluey); 205 xf86ErrorF("whiteX: %.3f whiteY: %.3f\n", disp->whitex, disp->whitey); 206} 207 208static void 209print_display(int scrnIndex, struct disp_features *disp, struct edid_version *v) 210{ 211 print_input_features(scrnIndex, disp, v); 212 if (disp->hsize && disp->vsize) { 213 xf86DrvMsg(scrnIndex, X_INFO, "Max Image Size [cm]: "); 214 xf86ErrorF("horiz.: %i ", disp->hsize); 215 xf86ErrorF("vert.: %i\n", disp->vsize); 216 } 217 else if (v->revision >= 4 && (disp->hsize || disp->vsize)) { 218 if (disp->hsize) 219 xf86DrvMsg(scrnIndex, X_INFO, "Aspect ratio: %.2f (landscape)\n", 220 (disp->hsize + 99) / 100.0); 221 if (disp->vsize) 222 xf86DrvMsg(scrnIndex, X_INFO, "Aspect ratio: %.2f (portrait)\n", 223 100.0 / (float) (disp->vsize + 99)); 224 225 } 226 else { 227 xf86DrvMsg(scrnIndex, X_INFO, "Indeterminate output size\n"); 228 } 229 230 if (!disp->gamma && v->revision >= 1.4) 231 xf86DrvMsg(scrnIndex, X_INFO, "Gamma defined in extension block\n"); 232 else 233 xf86DrvMsg(scrnIndex, X_INFO, "Gamma: %.2f\n", disp->gamma); 234 235 print_dpms_features(scrnIndex, disp, v); 236 print_whitepoint(scrnIndex, disp); 237} 238 239static void 240print_established_timings(int scrnIndex, struct established_timings *t) 241{ 242 unsigned char c; 243 244 if (t->t1 || t->t2 || t->t_manu) 245 xf86DrvMsg(scrnIndex, X_INFO, "Supported established timings:\n"); 246 c = t->t1; 247 if (c & 0x80) 248 xf86DrvMsg(scrnIndex, X_INFO, "720x400@70Hz\n"); 249 if (c & 0x40) 250 xf86DrvMsg(scrnIndex, X_INFO, "720x400@88Hz\n"); 251 if (c & 0x20) 252 xf86DrvMsg(scrnIndex, X_INFO, "640x480@60Hz\n"); 253 if (c & 0x10) 254 xf86DrvMsg(scrnIndex, X_INFO, "640x480@67Hz\n"); 255 if (c & 0x08) 256 xf86DrvMsg(scrnIndex, X_INFO, "640x480@72Hz\n"); 257 if (c & 0x04) 258 xf86DrvMsg(scrnIndex, X_INFO, "640x480@75Hz\n"); 259 if (c & 0x02) 260 xf86DrvMsg(scrnIndex, X_INFO, "800x600@56Hz\n"); 261 if (c & 0x01) 262 xf86DrvMsg(scrnIndex, X_INFO, "800x600@60Hz\n"); 263 c = t->t2; 264 if (c & 0x80) 265 xf86DrvMsg(scrnIndex, X_INFO, "800x600@72Hz\n"); 266 if (c & 0x40) 267 xf86DrvMsg(scrnIndex, X_INFO, "800x600@75Hz\n"); 268 if (c & 0x20) 269 xf86DrvMsg(scrnIndex, X_INFO, "832x624@75Hz\n"); 270 if (c & 0x10) 271 xf86DrvMsg(scrnIndex, X_INFO, "1024x768@87Hz (interlaced)\n"); 272 if (c & 0x08) 273 xf86DrvMsg(scrnIndex, X_INFO, "1024x768@60Hz\n"); 274 if (c & 0x04) 275 xf86DrvMsg(scrnIndex, X_INFO, "1024x768@70Hz\n"); 276 if (c & 0x02) 277 xf86DrvMsg(scrnIndex, X_INFO, "1024x768@75Hz\n"); 278 if (c & 0x01) 279 xf86DrvMsg(scrnIndex, X_INFO, "1280x1024@75Hz\n"); 280 c = t->t_manu; 281 if (c & 0x80) 282 xf86DrvMsg(scrnIndex, X_INFO, "1152x864@75Hz\n"); 283 xf86DrvMsg(scrnIndex, X_INFO, "Manufacturer's mask: %X\n", c & 0x7F); 284} 285 286static void 287print_std_timings(int scrnIndex, struct std_timings *t) 288{ 289 int i; 290 char done = 0; 291 292 for (i = 0; i < STD_TIMINGS; i++) { 293 if (t[i].hsize > 256) { /* sanity check */ 294 if (!done) { 295 xf86DrvMsg(scrnIndex, X_INFO, "Supported standard timings:\n"); 296 done = 1; 297 } 298 xf86DrvMsg(scrnIndex, X_INFO, 299 "#%i: hsize: %i vsize %i refresh: %i vid: %i\n", 300 i, t[i].hsize, t[i].vsize, t[i].refresh, t[i].id); 301 } 302 } 303} 304 305static void 306print_cvt_timings(int si, struct cvt_timings *t) 307{ 308 int i; 309 310 for (i = 0; i < 4; i++) { 311 if (t[i].height) { 312 xf86DrvMsg(si, X_INFO, "%dx%d @ %s%s%s%s%s Hz\n", 313 t[i].width, t[i].height, 314 t[i].rates & 0x10 ? "50," : "", 315 t[i].rates & 0x08 ? "60," : "", 316 t[i].rates & 0x04 ? "75," : "", 317 t[i].rates & 0x02 ? "85," : "", 318 t[i].rates & 0x01 ? "60RB" : ""); 319 } 320 else 321 break; 322 } 323} 324 325static void 326print_detailed_timings(int scrnIndex, struct detailed_timings *t) 327{ 328 329 if (t->clock > 15000000) { /* sanity check */ 330 xf86DrvMsg(scrnIndex, X_INFO, "Supported detailed timing:\n"); 331 xf86DrvMsg(scrnIndex, X_INFO, "clock: %.1f MHz ", 332 t->clock / 1000000.0); 333 xf86ErrorF("Image Size: %i x %i mm\n", t->h_size, t->v_size); 334 xf86DrvMsg(scrnIndex, X_INFO, 335 "h_active: %i h_sync: %i h_sync_end %i h_blank_end %i ", 336 t->h_active, t->h_sync_off + t->h_active, 337 t->h_sync_off + t->h_sync_width + t->h_active, 338 t->h_active + t->h_blanking); 339 xf86ErrorF("h_border: %i\n", t->h_border); 340 xf86DrvMsg(scrnIndex, X_INFO, 341 "v_active: %i v_sync: %i v_sync_end %i v_blanking: %i ", 342 t->v_active, t->v_sync_off + t->v_active, 343 t->v_sync_off + t->v_sync_width + t->v_active, 344 t->v_active + t->v_blanking); 345 xf86ErrorF("v_border: %i\n", t->v_border); 346 if (IS_STEREO(t->stereo)) { 347 xf86DrvMsg(scrnIndex, X_INFO, "Stereo: "); 348 if (IS_RIGHT_STEREO(t->stereo)) { 349 if (!t->stereo_1) 350 xf86ErrorF("right channel on sync\n"); 351 else 352 xf86ErrorF("left channel on sync\n"); 353 } 354 else if (IS_LEFT_STEREO(t->stereo)) { 355 if (!t->stereo_1) 356 xf86ErrorF("right channel on even line\n"); 357 else 358 xf86ErrorF("left channel on evel line\n"); 359 } 360 if (IS_4WAY_STEREO(t->stereo)) { 361 if (!t->stereo_1) 362 xf86ErrorF("4-way interleaved\n"); 363 else 364 xf86ErrorF("side-by-side interleaved"); 365 } 366 } 367 } 368} 369 370/* This function handle all detailed patchs, 371 * including EDID and EDID-extension 372 */ 373struct det_print_parameter { 374 xf86MonPtr m; 375 int index; 376 ddc_quirk_t quirks; 377}; 378 379static void 380handle_detailed_print(struct detailed_monitor_section *det_mon, void *data) 381{ 382 int j, scrnIndex; 383 struct det_print_parameter *p; 384 385 p = (struct det_print_parameter *) data; 386 scrnIndex = p->m->scrnIndex; 387 xf86DetTimingApplyQuirks(det_mon, p->quirks, 388 p->m->features.hsize, p->m->features.vsize); 389 390 switch (det_mon->type) { 391 case DT: 392 print_detailed_timings(scrnIndex, &det_mon->section.d_timings); 393 break; 394 case DS_SERIAL: 395 xf86DrvMsg(scrnIndex, X_INFO, "Serial No: %s\n", 396 det_mon->section.serial); 397 break; 398 case DS_ASCII_STR: 399 xf86DrvMsg(scrnIndex, X_INFO, " %s\n", det_mon->section.ascii_data); 400 break; 401 case DS_NAME: 402 xf86DrvMsg(scrnIndex, X_INFO, "Monitor name: %s\n", 403 det_mon->section.name); 404 break; 405 case DS_RANGES: 406 { 407 struct monitor_ranges *r = &det_mon->section.ranges; 408 409 xf86DrvMsg(scrnIndex, X_INFO, 410 "Ranges: V min: %i V max: %i Hz, H min: %i H max: %i kHz,", 411 r->min_v, r->max_v, r->min_h, r->max_h); 412 if (r->max_clock_khz != 0) { 413 xf86ErrorF(" PixClock max %i kHz\n", r->max_clock_khz); 414 if (r->maxwidth) 415 xf86DrvMsg(scrnIndex, X_INFO, "Maximum pixel width: %d\n", 416 r->maxwidth); 417 xf86DrvMsg(scrnIndex, X_INFO, "Supported aspect ratios:"); 418 if (r->supported_aspect & SUPPORTED_ASPECT_4_3) 419 xf86ErrorF(" 4:3%s", 420 r->preferred_aspect == 421 PREFERRED_ASPECT_4_3 ? "*" : ""); 422 if (r->supported_aspect & SUPPORTED_ASPECT_16_9) 423 xf86ErrorF(" 16:9%s", 424 r->preferred_aspect == 425 PREFERRED_ASPECT_16_9 ? "*" : ""); 426 if (r->supported_aspect & SUPPORTED_ASPECT_16_10) 427 xf86ErrorF(" 16:10%s", 428 r->preferred_aspect == 429 PREFERRED_ASPECT_16_10 ? "*" : ""); 430 if (r->supported_aspect & SUPPORTED_ASPECT_5_4) 431 xf86ErrorF(" 5:4%s", 432 r->preferred_aspect == 433 PREFERRED_ASPECT_5_4 ? "*" : ""); 434 if (r->supported_aspect & SUPPORTED_ASPECT_15_9) 435 xf86ErrorF(" 15:9%s", 436 r->preferred_aspect == 437 PREFERRED_ASPECT_15_9 ? "*" : ""); 438 xf86ErrorF("\n"); 439 xf86DrvMsg(scrnIndex, X_INFO, "Supported blankings:"); 440 if (r->supported_blanking & CVT_STANDARD) 441 xf86ErrorF(" standard"); 442 if (r->supported_blanking & CVT_REDUCED) 443 xf86ErrorF(" reduced"); 444 xf86ErrorF("\n"); 445 xf86DrvMsg(scrnIndex, X_INFO, "Supported scalings:"); 446 if (r->supported_scaling & SCALING_HSHRINK) 447 xf86ErrorF(" hshrink"); 448 if (r->supported_scaling & SCALING_HSTRETCH) 449 xf86ErrorF(" hstretch"); 450 if (r->supported_scaling & SCALING_VSHRINK) 451 xf86ErrorF(" vshrink"); 452 if (r->supported_scaling & SCALING_VSTRETCH) 453 xf86ErrorF(" vstretch"); 454 xf86ErrorF("\n"); 455 if (r->preferred_refresh) 456 xf86DrvMsg(scrnIndex, X_INFO, "Preferred refresh rate: %d\n", 457 r->preferred_refresh); 458 else 459 xf86DrvMsg(scrnIndex, X_INFO, "Buggy monitor, no preferred " 460 "refresh rate given\n"); 461 } 462 else if (r->max_clock != 0) { 463 xf86ErrorF(" PixClock max %i MHz\n", r->max_clock); 464 } 465 else { 466 xf86ErrorF("\n"); 467 } 468 if (r->gtf_2nd_f > 0) 469 xf86DrvMsg(scrnIndex, X_INFO, " 2nd GTF parameters: f: %i kHz " 470 "c: %i m: %i k %i j %i\n", r->gtf_2nd_f, 471 r->gtf_2nd_c, r->gtf_2nd_m, r->gtf_2nd_k, r->gtf_2nd_j); 472 break; 473 } 474 case DS_STD_TIMINGS: 475 for (j = 0; j < 5; j++) 476 xf86DrvMsg(scrnIndex, X_INFO, 477 "#%i: hsize: %i vsize %i refresh: %i " 478 "vid: %i\n", p->index, det_mon->section.std_t[j].hsize, 479 det_mon->section.std_t[j].vsize, 480 det_mon->section.std_t[j].refresh, 481 det_mon->section.std_t[j].id); 482 break; 483 case DS_WHITE_P: 484 for (j = 0; j < 2; j++) 485 if (det_mon->section.wp[j].index != 0) 486 xf86DrvMsg(scrnIndex, X_INFO, 487 "White point %i: whiteX: %f, whiteY: %f; gamma: %f\n", 488 det_mon->section.wp[j].index, 489 det_mon->section.wp[j].white_x, 490 det_mon->section.wp[j].white_y, 491 det_mon->section.wp[j].white_gamma); 492 break; 493 case DS_CMD: 494 xf86DrvMsg(scrnIndex, X_INFO, "Color management data: (not decoded)\n"); 495 break; 496 case DS_CVT: 497 xf86DrvMsg(scrnIndex, X_INFO, "CVT 3-byte-code modes:\n"); 498 print_cvt_timings(scrnIndex, det_mon->section.cvt); 499 break; 500 case DS_EST_III: 501 xf86DrvMsg(scrnIndex, X_INFO, 502 "Established timings III: (not decoded)\n"); 503 break; 504 case DS_DUMMY: 505 default: 506 break; 507 } 508 if (det_mon->type >= DS_VENDOR && det_mon->type <= DS_VENDOR_MAX) { 509 xf86DrvMsg(scrnIndex, X_INFO, 510 "Unknown vendor-specific block %x\n", 511 det_mon->type - DS_VENDOR); 512 } 513 514 p->index = p->index + 1; 515} 516 517static void 518print_number_sections(int scrnIndex, int num) 519{ 520 if (num) 521 xf86DrvMsg(scrnIndex, X_INFO, "Number of EDID sections to follow: %i\n", 522 num); 523} 524 525xf86MonPtr 526xf86PrintEDID(xf86MonPtr m) 527{ 528 CARD16 i, j, n; 529 char buf[EDID_WIDTH * 2 + 1]; 530 struct det_print_parameter p; 531 532 if (!m) 533 return NULL; 534 535 print_vendor(m->scrnIndex, &m->vendor); 536 print_version(m->scrnIndex, &m->ver); 537 print_display(m->scrnIndex, &m->features, &m->ver); 538 print_established_timings(m->scrnIndex, &m->timings1); 539 print_std_timings(m->scrnIndex, m->timings2); 540 p.m = m; 541 p.index = 0; 542 p.quirks = xf86DDCDetectQuirks(m->scrnIndex, m, FALSE); 543 xf86ForEachDetailedBlock(m, handle_detailed_print, &p); 544 print_number_sections(m->scrnIndex, m->no_sections); 545 546 /* extension block section stuff */ 547 548 xf86DrvMsg(m->scrnIndex, X_INFO, "EDID (in hex):\n"); 549 550 n = 128; 551 if (m->flags & EDID_COMPLETE_RAWDATA) 552 n += m->no_sections * 128; 553 554 for (i = 0; i < n; i += j) { 555 for (j = 0; j < EDID_WIDTH; ++j) { 556 sprintf(&buf[j * 2], "%02x", m->rawData[i + j]); 557 } 558 xf86DrvMsg(m->scrnIndex, X_INFO, "\t%s\n", buf); 559 } 560 561 return m; 562} 563