2 * Parse VESA Extended Display Identification Data
4 * (c) 2011--2015 Martin Mares <mj@ucw.cz>
6 * This program can be distributed and used under the terms
7 * of the GNU General Public License version 2 or later.
16 typedef unsigned int uns;
17 typedef unsigned char byte;
19 static byte edid[128];
22 static uns num_ext_blocks;
26 __attribute__((format(printf,1,2)))
27 __attribute__((noreturn))
33 fprintf(stderr, "edid: ");
34 vfprintf(stderr, msg, args);
43 return p[0] | (p[1] << 8);
49 return u16_le(p) | (u16_le(p+2) << 16);
63 printf("Model: %c%c%c %04x #%u\n",
64 ('A' - 1 + ((p[0] >> 2) & 0x1f)),
65 ('A' - 1 + ((p[0] & 3) << 3) | ((p[1] & 0xe0) >> 5)),
66 ('A' - 1 + (p[1] & 0x1f)),
72 printf("Model year: %d\n", y);
74 printf("Manufactured: %d week %d\n", y, p[8]);
76 printf("Manufactured: %d, week unspecified\n", y);
85 printf("Input: Digital,");
87 printf(" DFP%c\n", B(input & 1));
90 uns depth = (input >> 4) & 7;
91 static const char * const depths[] = { "undefined", "6", "8", "10", "12", "14", "16", "RFU7" };
93 uns iface = input & 0x0f;
94 static const char * const ifaces[] = {
95 "undefined", "DVI", "HDMI-a", "HDMI-b", "MDDI", "DisplayPort", "RFU6", "RFU7",
96 "RFU8", "RFU9", "RFU10", "RFU11", "RFU12", "RFU13", "RFU14", "RFU16"
99 printf(" Depth=%s Iface=%s\n", depths[depth], ifaces[iface]);
103 printf("Input: Analog, SigLev=%d Setup%c SepSync%c CompSync%c GreenSync%c SerrSync%c\n",
112 printf("Image size: undefined\n");
114 printf("Image aspect ratio: %u.%02u:1\n", 1 + p[1]/100, p[1] % 100);
116 printf("Image aspect ratio: 1:%u.%02u\n", 1 + p[2]/100, p[2] % 100);
118 printf("Image size: %dx%dcm\n", p[1], p[2]);
121 printf("Gamma: not given\n");
123 printf("Gamma: %d.%02d\n", 1 + p[3]/100, p[3] % 100);
126 static const char * const color_types[4] = { "Mono/Gray", "RGB", "NonRGB", "Unknown" };
127 static const char * const color_encodings[4] = {
131 "RGB444/YCrCb444/YCrCb422",
133 printf("Flags: Suspend%c Standby%c ActiveOff%c",
137 if (version >= 0x104 && (input & 0x80))
138 printf(" Colors=%s", color_encodings[(flags >> 3) & 3]);
140 printf(" Colors=%s", color_types[(flags >> 3) & 3]);
141 printf(" HasSRGB%c", B(flags & 0x04));
142 printf(" %s%c", (version >= 0x104 ? "PreferredIsNative" : "PrefTiming"), B(flags & 0x02));
143 printf(" %s%c\n", (version >= 0x104 ? "ContinuousFreq" : "GTF"), B(flags & 0x01));
149 uns rx = 4*p[2] | ((p[0] >> 6) & 3);
150 uns ry = 4*p[3] | ((p[0] >> 4) & 3);
151 uns gx = 4*p[4] | ((p[0] >> 2) & 3);
152 uns gy = 4*p[5] | ((p[0] ) & 3);
153 uns bx = 4*p[5] | ((p[1] >> 6) & 3);
154 uns by = 4*p[7] | ((p[1] >> 4) & 3);
155 uns wx = 4*p[8] | ((p[1] >> 2) & 3);
156 uns wy = 4*p[9] | ((p[1] ) & 3);
157 #define C(c) ((double)c / 1024)
158 printf("Chromaticity: R=(%.3f,%.3f) G=(%.3f,%.3f) B=(%.3f,%.3f) W=(%.3f,%.3f)\n",
159 C(rx), C(ry), C(gx), C(gy), C(bx), C(by), C(wx), C(wy));
164 show_timings(byte *p)
166 static const char * const estab_timings[24] = {
192 printf("Basic timings:");
193 for (int i=0; i<24; i++)
194 if (p[i/8] & (0x80 >> (i%8)))
195 printf(" %s", estab_timings[i]);
198 printf("Standard timings:");
199 for (int i=0; i<8; i++)
201 byte *q = p + 3 + 2*i;
202 if (q[0] == 1 && q[1] == 1)
204 uns h = (31 + q[0]) * 8;
209 if (version < 0x0103)
224 printf(" %dx%d@%d", h, v, 60 + (q[1] & 0x3f));
229 /*** EDID Descriptors ***/
232 show_detailed_timing(byte *p)
234 uns hactive = p[2] | ((p[4] << 4) & 0xf00);
235 uns hblank = p[3] | ((p[4] << 8) & 0xf00);
236 uns vactive = p[5] | ((p[7] << 4) & 0xf00);
237 uns vblank = p[6] | ((p[7] << 8) & 0xf00);
238 uns hs_offset = p[8] | ((p[11] << 2) & 0x300);
239 uns hs_width = p[9] | ((p[11] << 4) & 0x300);
240 uns vs_offset = (p[10] >> 4) | ((p[11] << 2) & 0x30);
241 uns vs_width = (p[10] & 0xf) | ((p[11] << 4) & 0x30);
242 uns hsize = p[12] | ((p[14] << 4) & 0xf00);
243 uns vsize = p[13] | ((p[14] << 8) & 0xf00);
247 printf("Detailed timing: %dx%d (%.1f Hz)\n",
249 u16_le(p) * 10000. / (hactive+hblank) / (vactive+vblank));
250 printf("\tPixClk: %.2fMHz\n", u16_le(p) / 100.);
251 printf("\tH: Active=%d Blank=%d SyncOffset=%d SyncWidth=%d Border=%d\n",
252 hactive, hblank, hs_offset, hs_width, hborder);
253 printf("\tV: Active=%d Blank=%d SyncOffset=%d SyncWidth=%d Border=%d\n",
254 vactive, vblank, vs_offset, vs_width, vborder);
255 printf("\tSize: %dx%dmm\n", hsize, vsize);
258 uns stereo = ((flags >> 4) & 6) | (flags & 1);
259 static const char * const stereo_types[8] = {
269 printf("\tFlags: Interlace%c Stereo=%s\n", B(flags & 0x80), stereo_types[stereo]);
271 printf("\tStereo mode: #%d\n", stereo);
273 uns sync = (flags >> 3) & 3;
275 static const char * const sync_types[4] = { "AnalogComposite", "BipolarAnalogComposite", "DigitalComposite", "DigitalSeparate" };
276 printf("\tSync: %s", sync_types[sync]);
278 printf(" Serrate%c", B(flags & 4));
280 printf(" VPolarity%c", B(flags & 4));
282 printf(" OnRGB%c", B(flags & 2));
284 printf(" Polarity%c", B(flags & 2));
286 printf(" HPolarity%c", B(flags & 2));
290 printf("\tCalculated DPI: %dx%d\n",
291 (int)(hactive / (hsize / 25.4)),
292 (int)(vactive / (vsize / 25.4)));
296 show_ascii(char *field, byte *p, uns len)
298 printf("%s: ", field);
303 else if (*p >= 0x20 && *p <= 0x7e)
306 printf("<%02x>", *p);
316 uns voffset = flags & 3;
317 uns hoffset = (flags >> 2) & 3;
319 printf("Limits: Vert=%d-%dHz Horiz=%d-%dkHz PixClk=%dMHz",
320 p[5] + (voffset == 3 ? 255 : 0),
321 p[6] + (voffset & 2 ? 255 : 0),
322 p[7] + (hoffset == 3 ? 255 : 0),
323 p[8] + (hoffset & 2 ? 255 : 0),
328 case 0: // No secondary timing formula
329 printf(" (no formula)\n");
331 case 1: // Range limits only
332 printf(" (range only)\n");
334 case 2: // Secondary GTF
335 printf(" (2ndary GTF)\n");
336 printf("\tSecondary GTF parameters: fStart=%dkHz C=%.1f M=%d K=%d J=%.1f\n",
337 p[12]*2, p[13]/2., u16_le(p+14), p[16], p[17]/2.);
340 printf(" (CVT v%d.%d)\n", p[11] >> 4, p[11] & 0x0f);
344 printf(" (unknown formula #%02x)\n", p[10]);
349 show_descriptor(byte *p)
353 case 0xff: // Serial number
354 show_ascii("Serial", p+5, 13);
356 case 0xfe: // ASCII data
357 show_ascii("Comment", p+5, 13);
359 case 0xfd: // Range limits
362 case 0xfc: // Product name
363 show_ascii("Product name", p+5, 13);
365 case 0xfb: // Color point
366 printf("Color point: [...]\n");
368 case 0xfa: // More standard timings
369 printf("More standard timings: [...]\n");
372 printf("Display color management: [...]\n");
375 printf("CVT-3: [...]\n");
378 printf("Established timings 3: [...]\n");
380 case 0x10: // Dummy descriptor -- entry unused
383 printf("%s descriptor type %02x\n", (p[3] <= 0x0f ? "Vendor-specific" : "Unknown"), p[3]);
391 for (int i=0; i<128; i++)
394 printf("Invalid checksum: off by %02x\n", sum);
400 if (memcmp(edid, "\000\377\377\377\377\377\377\000", 8))
401 die("Invalid EDID signature, giving up");
402 int ver = edid[0x12];
403 int rev = edid[0x13];
404 printf("EDID %d.%d\n", ver, rev);
405 version = (ver << 8) | rev;
407 if (version >= 0x200)
408 die("Unsupported version");
411 show_model(edid + 8);
412 show_basic(edid + 0x14);
413 show_color(edid + 0x19);
414 show_timings(edid + 0x23);
415 for (int i=0; i<4; i++)
417 byte *p = edid + 0x36 + 18*i;
419 show_detailed_timing(p);
424 num_ext_blocks = edid[0x7e];
429 static void ext_block_map(void)
431 for (uns i=1; i<=0xfe; i++)
433 printf("Block %u: extension %u\n", i, edid[i]);
436 static void ext_hdmi(byte *p, uns len)
440 printf("\t!!! Truncated\n");
443 printf("\tPhysical address: %u.%u.%u.%u\n", p[4], p[5], p[6], p[7]);
448 printf("\tFlags: AI%c 48bpp%c 36bpp%c 30bpp%c DeepColor-YCbCr444%c DVIDual%c\n",
449 B(f & 0x80), B(f & 0x40), B(f & 0x20), B(f & 0x10),
450 B(f & 0x08), B(f & 0x01));
454 printf("\tMax TMDS Clock [MHz]: %u\n", 5 * p[7]);
461 if (f2 & 0x80) // Latency fields present
465 printf("\t!!! Truncated latency fields\n");
468 printf("\tLatency [ms]:");
469 if (p[i] && p[i] != 0xff)
470 printf(" Video=%u", (p[i]-1)*2);
471 if (p[i+1] && p[i+1] != 0xff)
472 printf(" Audio=%u", (p[i+1]-1)*2);
475 if (f2 & 0x40) // Interlaced latency present
479 printf("\t!!! Truncated latency fields\n");
482 printf("\tInterlaced latency [ms]:");
483 if (p[i] && p[i] != 0xff)
484 printf(" Video=%u", (p[i]-1)*2);
485 if (p[i+1] && p[i+1] != 0xff)
486 printf(" Audio=%u", (p[i+1]-1)*2);
492 // A table of CEA video modes taken from https://en.wikipedia.org/wiki/Extended_Display_Identification_Data
493 static const char * const cea_modes[] = {
494 [1] = "640x480p@60 4:3",
495 [2] = "720x480p@60 4:3",
496 [3] = "720x480p@60 16:9",
497 [4] = "1280x720p@60 16:9",
498 [5] = "1920x1080i@60 16:9",
499 [6] = "720(1440)x480i@60 4:3",
500 [7] = "720(1440)x480i@60 16:9",
501 [8] = "720(1440)x240p@60 4:3",
502 [9] = "720(1440)x240p@60 16:9",
503 [10] = "(2880)x480i@60 4:3",
504 [11] = "(2880)x480i@60 16:9",
505 [12] = "(2880)x240p@60 4:3",
506 [13] = "(2880)x240p@60 16:9",
507 [14] = "1440x480p@60 4:3",
508 [15] = "1440x480p@60 16:9",
509 [16] = "1920x1080p@60 16:9",
510 [17] = "720x576p@50 4:3",
511 [18] = "720x576p@50 16:9",
512 [19] = "1280x720p@50 16:9",
513 [20] = "1920x1080i@50 16:9",
514 [21] = "720(1440)x576i@50 4:3",
515 [22] = "720(1440)x576i@50 16:9",
516 [23] = "720(1440)x288p@50 4:3",
517 [24] = "720(1440)x288p@50 16:9",
518 [25] = "(2880)x576i@50 4:3",
519 [26] = "(2880)x576i@50 16:9",
520 [27] = "(2880)x288p@50 4:3",
521 [28] = "(2880)x288p@50 16:9",
522 [29] = "1440x576p@50 4:3",
523 [30] = "1440x576p@50 16:9",
524 [31] = "1920x1080p@50 16:9",
525 [32] = "1920x1080p@24 16:9",
526 [33] = "1920x1080p@25 16:9",
527 [34] = "1920x1080p@30 16:9",
528 [35] = "(2880)x480p@60 4:3",
529 [36] = "(2880)x480p@60 16:9",
530 [37] = "(2880)x576p@50 4:3",
531 [38] = "(2880)x576p@50 16:9",
532 [39] = "1920x1080i@50 16:9",
533 [40] = "1920x1080i@100 16:9",
534 [41] = "1280x720p@100 16:9",
535 [42] = "720x576p@100 4:3",
536 [43] = "720x576p@100 16:9",
537 [44] = "720(1440)x576i@100 4:3",
538 [45] = "720(1440)x576i@100 16:9",
539 [46] = "1920x1080i@120 16:9",
540 [47] = "1280x720p@120 16:9",
541 [48] = "720x480p@120 4:3",
542 [49] = "720x480p@120 16:9",
543 [50] = "720(1440)x480i@120 4:3",
544 [51] = "720(1440)x480i@120 16:9",
545 [52] = "720x576p@200 4:3",
546 [53] = "720x576p@200 16:9",
547 [54] = "720(1440)x576i@200 4:3",
548 [55] = "720(1440)x576i@200 16:9",
549 [56] = "720x480p@240 4:3",
550 [57] = "720x480p@240 16:9",
551 [58] = "720(1440)x480i@240 4:3",
552 [59] = "720(1440)x480i@240 16:9",
553 [60] = "1280x720p@24 16:9",
554 [61] = "1280x720p@25 16:9",
555 [62] = "1280x720p@30 16:9",
556 [63] = "1920x1080p@120 16:9",
557 [64] = "1920x1080p@100 16:9",
558 [65] = "1280x720p@24 64:27",
559 [66] = "1280x720p@25 64:27",
560 [67] = "1280x720p@30 64:27",
561 [68] = "1280x720p@50 64:27",
562 [69] = "1280x720p@60 64:27",
563 [70] = "1280x720p@100 64:27",
564 [71] = "1280x720p@120 64:27",
565 [72] = "1920x1080p@24 64:27",
566 [73] = "1920x1080p@25 64:27",
567 [74] = "1920x1080p@30 64:27",
568 [75] = "1920x1080p@50 64:27",
569 [76] = "1920x1080p@60 64:27",
570 [77] = "1920x1080p@100 64:27",
571 [78] = "1920x1080p@120 64:27",
572 [79] = "1680x720p@24 64:27",
573 [80] = "1680x720p@25 64:27",
574 [81] = "1680x720p@30 64:27",
575 [82] = "1680x720p@50 64:27",
576 [83] = "1680x720p@60 64:27",
577 [84] = "1680x720p@100 64:27",
578 [85] = "1680x720p@120 64:27",
579 [86] = "2560x1080p@24 64:27",
580 [87] = "2560x1080p@25 64:27",
581 [88] = "2560x1080p@30 64:27",
582 [89] = "2560x1080p@50 64:27",
583 [90] = "2560x1080p@60 64:27",
584 [91] = "2560x1080p@100 64:27",
585 [92] = "2560x1080p@120 64:27",
586 [93] = "3840x2160p@24 16:9",
587 [94] = "3840x2160p@25 16:9",
588 [95] = "3840x2160p@30 16:9",
589 [96] = "3840x2160p@50 16:9",
590 [97] = "3840x2160p@60 16:9",
591 [98] = "4096x2160p@24 256:135",
592 [99] = "4096x2160p@25 256:135",
593 [100] = "4096x2160p@30 256:135",
594 [101] = "4096x2160p@50 256:135",
595 [102] = "4096x2160p@60 256:135",
596 [103] = "3840x2160p@24 64:27",
597 [104] = "3840x2160p@25 64:27",
598 [105] = "3840x2160p@30 64:27",
599 [106] = "3840x2160p@50 64:27",
600 [107] = "3840x2160p@60 64:27",
603 static void ext_cea_data(uns pos, uns max_pos)
605 while (pos < max_pos)
607 byte *p = edid + pos;
608 uns type = p[0] >> 5;
609 uns len = (p[0] & 31) + 1;
610 if (pos + len > max_pos)
612 printf("!!! Truncated data block (pos=%u, type=%02x, len=%u)\n", pos, type, len);
619 printf("Audio formats:\n");
620 for (uns j=1; j < len; j += 3)
623 uns fmt = (q[0] >> 3) & 0x0f;
624 static const char * const fmt_names[16] = {
625 "RFU0", "LPCM", "AC-3", "MPEG1",
626 "MP3", "MPEG2", "AAC", "DTS",
627 "ATRAC", "SACD", "DD+", "DTS+HD",
628 "MLP", "DST", "WMApro", "RFU15",
630 uns chans = (q[0] & 7) + 1;
631 printf("\t%s, Channels=%u\n", fmt_names[fmt], chans);
634 printf("\t\tSample rates [kHz]: 192%c 176%c 96%c 88%c 48%c 44%c 32%c\n",
635 B(rates & 0x40), B(rates & 0x20), B(rates & 0x10),
636 B(rates & 0x08), B(rates & 0x04), B(rates & 0x02), B(rates & 0x01));
640 printf("\t\tBit depths: 24%c 20%c 16%c\n", B(br & 4), B(br & 2), B(br & 1));
642 printf("\t\tMax bit rate [kHz]: %u\n", br*8);
646 printf("Video formats:\n");
647 for (uns j=1; j < len; j++)
649 uns vf = p[j] & 0x7f;
650 if (vf && vf < sizeof(cea_modes) / sizeof(cea_modes[0]))
651 printf("\t%s", cea_modes[vf]);
653 printf("\t#%02x", vf);
659 case 3: // Vendor specific
661 printf("Vendor-specific: ");
664 printf("\t!!! Truncated\n");
667 uns oid = p[1] | (p[2] << 8) | (p[3] << 16);
674 printf("Vendor %06x\n", oid);
677 case 4: // Speaker allocation
678 printf("Speakers present:\n");
681 printf("\t!!! Truncated\n");
684 static const char * const spk_types[8] = {
685 "Front Left+Right", "LFE", "Front Center", "Rear Left+Right",
686 "Rear Center", "Front Left+Right Center", "Rear Left+Right Center", "RFU7",
688 for (uns j=0; j<8; j++)
690 printf("\t%s\n", spk_types[j]);
693 printf("Unknown data block #%02x (pos=%u, len=%u)\n", type, pos, len);
700 static void ext_cea_dtd(uns pos, uns max_pos)
702 // Detailed Timing Descriptors follow the same syntax as in basic EDID
703 while (pos + 18 <= max_pos && edid[pos] && edid[pos+1])
705 show_detailed_timing(edid + pos);
710 static void ext_cea_861(void)
712 uns version = edid[1];
713 uns dtd_pos = edid[2];
717 printf("Flags: Underscan%c BasicAudio%c YCbCr444%c YCbCr422%c\n",
723 ext_cea_data(4, (dtd_pos < 127 ? dtd_pos : 127));
724 ext_cea_dtd(dtd_pos, 127);
729 void (*parser)(void);
732 static const struct extension ext_table[0x100] = {
733 [0x02] = { "CEA 861", ext_cea_861 },
734 [0x10] = { "Video Timing Block", NULL },
735 [0x40] = { "Display Information", NULL },
736 [0x50] = { "Localized Strings", NULL },
737 [0x60] = { "Digital Packet Video Link", NULL },
738 [0xf0] = { "Block map", ext_block_map },
739 [0xff] = { "Vendor-specific", NULL },
742 static void show_ext(void)
745 const struct extension *e = &ext_table[ext];
747 printf("\n>> Extension block %u: ", block);
749 printf("%s", e->name);
751 printf("Extension #%02x", ext);
753 printf(" (version %02x)", edid[1]);
761 printf("!!! Not decoded yet\n");
766 static void read_binary(void)
768 int c = read(0, edid, 128);
770 die("Read error: %m");
772 die("Read only %d bytes, need 128", c);
782 while (fgets(line, sizeof(line), stdin))
786 if (strstr(line, "EDID (in hex):"))
791 char *c = strchr(line, '\n');
795 for (int i=0; i<32; i++)
800 if (x >= '0' && x <= '9')
802 else if (x >= 'A' && x <= 'F')
804 else if (x >= 'a' && x <= 'f')
811 edid[cnt-1-i/2] |= x << 4;
813 if (c > line && c[-1] != ' ' && c[-1] != '\t')
820 die("No EDID log found");
822 die("EDID log found, but it is too short (only %d bytes)", cnt);
825 die("EDID log parse error at: %s", line);
832 for (int i=0; i<128; i+=16)
835 for (int j=0; j<16; j++)
836 printf(" %02x", edid[i+j]);
844 fprintf(stderr, "Usage: edid <options>\n\n\
846 -l\t\tParse input as Xorg log file and try to find EDID dump there\n\
847 -x\t\tShow hexdump of the whole EDID block\n\
852 int main(int argc, char **argv)
858 while ((opt = getopt(argc, argv, "xl")) >= 0)
873 while (block <= num_ext_blocks)