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);
61 printf("Model: %c%c%c %04x #%u\n",
62 ('A' - 1 + ((p[0] >> 2) & 0x1f)),
63 ('A' - 1 + ((p[0] & 3) << 3) | ((p[1] & 0xe0) >> 5)),
64 ('A' - 1 + (p[1] & 0x1f)),
70 printf("Model year: %d\n", y);
72 printf("Manufactured: %d week %d\n", y, p[8]);
74 printf("Manufactured: %d, week unspecified\n", y);
83 printf("Input: Digital,");
85 printf(" DFP%c\n", B(input & 1));
88 uns depth = (input >> 4) & 7;
89 static const char * const depths[] = { "undefined", "6", "8", "10", "12", "14", "16", "RFU7" };
91 uns iface = input & 0x0f;
92 static const char * const ifaces[] = {
93 "undefined", "DVI", "HDMI-a", "HDMI-b", "MDDI", "DisplayPort", "RFU6", "RFU7",
94 "RFU8", "RFU9", "RFU10", "RFU11", "RFU12", "RFU13", "RFU14", "RFU16"
97 printf(" Depth=%s Iface=%s\n", depths[depth], ifaces[iface]);
101 printf("Input: Analog, SigLev=%d Setup%c SepSync%c CompSync%c GreenSync%c SerrSync%c\n",
110 printf("Image size: undefined\n");
112 printf("Image aspect ratio: %u.%02u:1\n", 1 + p[1]/100, p[1] % 100);
114 printf("Image aspect ratio: 1:%u.%02u\n", 1 + p[2]/100, p[2] % 100);
116 printf("Image size: %dx%dcm\n", p[1], p[2]);
119 printf("Gamma: not given\n");
121 printf("Gamma: %d.%02d\n", 1 + p[3]/100, p[3] % 100);
124 static const char * const color_types[4] = { "Mono/Gray", "RGB", "NonRGB", "Unknown" };
125 static const char * const color_encodings[4] = {
129 "RGB444/YCrCb444/YCrCb422",
131 printf("Flags: Suspend%c Standby%c ActiveOff%c",
135 if (version >= 0x104 && (input & 0x80))
136 printf(" Colors=%s", color_encodings[(flags >> 3) & 3]);
138 printf(" Colors=%s", color_types[(flags >> 3) & 3]);
139 printf(" HasSRGB%c", B(flags & 0x04));
140 printf(" %s%c", (version >= 0x104 ? "PreferredIsNative" : "PrefTiming"), B(flags & 0x02));
141 printf(" %s%c\n", (version >= 0x104 ? "ContinuousFreq" : "GTF"), B(flags & 0x01));
147 uns rx = 4*p[2] | ((p[0] >> 6) & 3);
148 uns ry = 4*p[3] | ((p[0] >> 4) & 3);
149 uns gx = 4*p[4] | ((p[0] >> 2) & 3);
150 uns gy = 4*p[5] | ((p[0] ) & 3);
151 uns bx = 4*p[5] | ((p[1] >> 6) & 3);
152 uns by = 4*p[7] | ((p[1] >> 4) & 3);
153 uns wx = 4*p[8] | ((p[1] >> 2) & 3);
154 uns wy = 4*p[9] | ((p[1] ) & 3);
155 #define C(c) ((double)c / 1024)
156 printf("Chromaticity: R=(%.3f,%.3f) G=(%.3f,%.3f) B=(%.3f,%.3f) W=(%.3f,%.3f)\n",
157 C(rx), C(ry), C(gx), C(gy), C(bx), C(by), C(wx), C(wy));
162 show_timings(byte *p)
164 static const char * const estab_timings[24] = {
190 printf("Basic timings:");
191 for (int i=0; i<24; i++)
192 if (p[i/8] & (0x80 >> (i%8)))
193 printf(" %s", estab_timings[i]);
196 printf("Standard timings:");
197 for (int i=0; i<8; i++)
199 byte *q = p + 3 + 2*i;
200 if (q[0] == 1 && q[1] == 1)
202 uns h = (31 + q[0]) * 8;
207 if (version < 0x0103)
222 printf(" %dx%d@%d", h, v, 60 + (q[1] & 0x3f));
228 show_detailed_timing(byte *p)
230 uns hactive = p[2] | ((p[4] << 4) & 0xf00);
231 uns hblank = p[3] | ((p[4] << 8) & 0xf00);
232 uns vactive = p[5] | ((p[7] << 4) & 0xf00);
233 uns vblank = p[6] | ((p[7] << 8) & 0xf00);
234 uns hs_offset = p[8] | ((p[11] << 2) & 0x300);
235 uns hs_width = p[9] | ((p[11] << 4) & 0x300);
236 uns vs_offset = (p[10] >> 4) | ((p[11] << 2) & 0x30);
237 uns vs_width = (p[10] & 0xf) | ((p[11] << 4) & 0x30);
238 uns hsize = p[12] | ((p[14] << 4) & 0xf00);
239 uns vsize = p[13] | ((p[14] << 8) & 0xf00);
243 printf("Detailed timing: %dx%d (%.1f Hz)\n",
245 u16_le(p) * 10000. / (hactive+hblank) / (vactive+vblank));
246 printf("\tPixClk: %.2fMHz\n", u16_le(p) / 100.);
247 printf("\tH: Active=%d Blank=%d SyncOffset=%d SyncWidth=%d Border=%d\n",
248 hactive, hblank, hs_offset, hs_width, hborder);
249 printf("\tV: Active=%d Blank=%d SyncOffset=%d SyncWidth=%d Border=%d\n",
250 vactive, vblank, vs_offset, vs_width, vborder);
251 printf("\tSize: %dx%dmm\n", hsize, vsize);
254 uns stereo = ((flags >> 4) & 6) | (flags & 1);
255 static const char * const stereo_types[8] = {
265 printf("\tFlags: Interlace%c Stereo=%s\n", B(flags & 0x80), stereo_types[stereo]);
267 printf("\tStereo mode: #%d\n", stereo);
269 uns sync = (flags >> 3) & 3;
271 static const char * const sync_types[4] = { "AnalogComposite", "BipolarAnalogComposite", "DigitalComposite", "DigitalSeparate" };
272 printf("\tSync: %s", sync_types[sync]);
274 printf(" Serrate%c", B(flags & 4));
276 printf(" VPolarity%c", B(flags & 4));
278 printf(" OnRGB%c", B(flags & 2));
280 printf(" Polarity%c", B(flags & 2));
282 printf(" HPolarity%c", B(flags & 2));
286 printf("\tCalculated DPI: %dx%d\n",
287 (int)(hactive / (hsize / 25.4)),
288 (int)(vactive / (vsize / 25.4)));
292 show_ascii(char *field, byte *p, uns len)
294 printf("%s: ", field);
299 else if (*p >= 0x20 && *p <= 0x7e)
302 printf("<%02x>", *p);
312 uns voffset = flags & 3;
313 uns hoffset = (flags >> 2) & 3;
315 printf("Limits: Vert=%d-%dHz Horiz=%d-%dkHz PixClk=%dMHz",
316 p[5] + (voffset == 3 ? 255 : 0),
317 p[6] + (voffset & 2 ? 255 : 0),
318 p[7] + (hoffset == 3 ? 255 : 0),
319 p[8] + (hoffset & 2 ? 255 : 0),
324 case 0: // No secondary timing formula
325 printf(" (no formula)\n");
327 case 1: // Range limits only
328 printf(" (range only)\n");
330 case 2: // Secondary GTF
331 printf(" (2ndary GTF)\n");
332 printf("\tSecondary GTF parameters: fStart=%dkHz C=%.1f M=%d K=%d J=%.1f\n",
333 p[12]*2, p[13]/2., u16_le(p+14), p[16], p[17]/2.);
336 printf(" (CVT v%d.%d)\n", p[11] >> 4, p[11] & 0x0f);
340 printf(" (unknown formula #%02x)\n", p[10]);
345 show_descriptor(byte *p)
349 case 0xff: // Serial number
350 show_ascii("Serial", p+5, 13);
352 case 0xfe: // ASCII data
353 show_ascii("Comment", p+5, 13);
355 case 0xfd: // Range limits
358 case 0xfc: // Product name
359 show_ascii("Product name", p+5, 13);
361 case 0xfb: // Color point
362 printf("Color point: [...]\n");
364 case 0xfa: // More standard timings
365 printf("More standard timings: [...]\n");
368 printf("Display color management: [...]\n");
371 printf("CVT-3: [...]\n");
374 printf("Established timings 3: [...]\n");
376 case 0x10: // Dummy descriptor -- entry unused
379 printf("%s descriptor type %02x\n", (p[3] <= 0x0f ? "Vendor-specific" : "Unknown"), p[3]);
387 for (int i=0; i<128; i++)
390 printf("Invalid checksum: off by %02x\n", sum);
396 if (memcmp(edid, "\000\377\377\377\377\377\377\000", 8))
397 die("Invalid EDID signature, giving up");
398 int ver = edid[0x12];
399 int rev = edid[0x13];
400 printf("EDID %d.%d\n", ver, rev);
401 version = (ver << 8) | rev;
403 if (version >= 0x200)
404 die("Unsupported version");
407 show_model(edid + 8);
408 show_basic(edid + 0x14);
409 show_color(edid + 0x19);
410 show_timings(edid + 0x23);
411 for (int i=0; i<4; i++)
413 byte *p = edid + 0x36 + 18*i;
415 show_detailed_timing(p);
420 num_ext_blocks = edid[0x7e];
423 static void ext_block_map(void)
425 for (uns i=1; i<=0xfe; i++)
427 printf("Block %u: extension %u\n", i, edid[i]);
432 void (*parser)(void);
435 static const struct extension ext_table[0x100] = {
436 [0x02] = { "CEA 861", NULL },
437 [0x10] = { "Video Timing Block", NULL },
438 [0x40] = { "Display Information", NULL },
439 [0x50] = { "Localized Strings", NULL },
440 [0x60] = { "Digital Packet Video Link", NULL },
441 [0xf0] = { "Block map", ext_block_map },
442 [0xff] = { "Vendor-specific", NULL },
445 static void show_ext(void)
448 const struct extension *e = &ext_table[ext];
450 printf("\n>> Extension block %u: ", block);
452 printf("%s", e->name);
454 printf("Extension #%02x", ext);
456 printf(" (version %02x)", edid[1]);
464 printf("!!! Not decoded yet\n");
467 static void read_binary(void)
469 int c = read(0, edid, 128);
471 die("Read error: %m");
473 die("Read only %d bytes, need 128", c);
483 while (fgets(line, sizeof(line), stdin))
487 if (strstr(line, "EDID (in hex):"))
492 char *c = strchr(line, '\n');
496 for (int i=0; i<32; i++)
501 if (x >= '0' && x <= '9')
503 else if (x >= 'A' && x <= 'F')
505 else if (x >= 'a' && x <= 'f')
512 edid[cnt-1-i/2] |= x << 4;
514 if (c > line && c[-1] != ' ' && c[-1] != '\t')
521 die("No EDID log found");
523 die("EDID log found, but it is too short (only %d bytes)", cnt);
526 die("EDID log parse error at: %s", line);
533 for (int i=0; i<128; i+=16)
536 for (int j=0; j<16; j++)
537 printf(" %02x", edid[i+j]);
545 fprintf(stderr, "Usage: edid <options>\n\n\
547 -l\t\tParse input as Xorg log file and try to find EDID dump there\n\
548 -x\t\tShow hexdump of the whole EDID block\n\
553 int main(int argc, char **argv)
559 while ((opt = getopt(argc, argv, "xl")) >= 0)
574 while (block <= num_ext_blocks)