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