2 * Parse VESA Extended Display Identification Data
4 * (c) 2011 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];
24 __attribute__((format(printf,1,2)))
25 __attribute__((noreturn))
31 fprintf(stderr, "edid: ");
32 vfprintf(stderr, msg, args);
41 return p[0] | (p[1] << 8);
47 return u16_le(p) | (u16_le(p+2) << 16);
59 printf("Model: %c%c%c %04x #%u\n",
60 ('A' - 1 + ((p[0] >> 2) & 0x1f)),
61 ('A' - 1 + ((p[0] & 3) << 3) | ((p[1] & 0xe0) >> 5)),
62 ('A' - 1 + (p[1] & 0x1f)),
65 printf("Manufactured: %d week %d\n", p[9] + 1990, p[8]);
73 printf("Input: Digital DFP%c\n", B(input & 1));
75 printf("Input: Analog SigLev=%d Setup%c SepSync%c CompSync%c GreenSync%c SerrSync%c\n",
83 printf("Image size: %dx%dcm\n", p[1], p[2]);
85 printf("Gamma: not given\n");
87 printf("Gamma: %d.%02d\n", (p[3] / 100) + 1, p[3] % 100);
90 static const char * const types[4] = { "Mono/Gray", "RGB", "NonRGB", "Unknown" };
91 printf("Flags: Suspend%c Standby%c ActiveOff%c Colors=%s HasSRGB%c PrefTiming%c GTF%c\n",
95 types[(flags >> 3) & 3],
104 uns rx = 4*p[2] | ((p[0] >> 6) & 3);
105 uns ry = 4*p[3] | ((p[0] >> 4) & 3);
106 uns gx = 4*p[4] | ((p[0] >> 2) & 3);
107 uns gy = 4*p[5] | ((p[0] ) & 3);
108 uns bx = 4*p[5] | ((p[1] >> 6) & 3);
109 uns by = 4*p[7] | ((p[1] >> 4) & 3);
110 uns wx = 4*p[8] | ((p[1] >> 2) & 3);
111 uns wy = 4*p[9] | ((p[1] ) & 3);
112 #define C(c) ((double)c / 1024)
113 printf("Chromaticity: R=(%.3f,%.3f) G=(%.3f,%.3f) B=(%.3f,%.3f) W=(%.3f,%.3f)\n",
114 C(rx), C(ry), C(gx), C(gy), C(bx), C(by), C(wx), C(wy));
119 show_timings(byte *p)
121 static const char * const estab_timings[24] = {
147 printf("Basic timings:");
148 for (int i=0; i<24; i++)
149 if (p[i/8] & (0x80 >> (i%8)))
150 printf(" %s", estab_timings[i]);
153 printf("Extended timings:");
154 for (int i=0; i<8; i++)
156 byte *q = p + 3 + 2*i;
157 if (q[0] == 1 && q[1] == 1)
159 uns h = (31 + q[0]) * 8;
164 if (version < 0x0103)
179 printf(" %dx%d@%d", h, v, 60 + (q[1] & 0x3f));
185 show_detailed_timing(byte *p)
187 uns hactive = p[2] | ((p[4] << 4) & 0xf00);
188 uns hblank = p[3] | ((p[4] << 8) & 0xf00);
189 uns vactive = p[5] | ((p[7] << 4) & 0xf00);
190 uns vblank = p[6] | ((p[7] << 8) & 0xf00);
191 uns hs_offset = p[8] | ((p[11] << 2) & 0x300);
192 uns hs_width = p[9] | ((p[11] << 4) & 0x300);
193 uns vs_offset = (p[10] >> 4) | ((p[11] << 2) & 0x30);
194 uns vs_width = (p[10] & 0xf) | ((p[11] << 4) & 0x30);
195 uns hsize = p[12] | ((p[14] << 4) & 0xf00);
196 uns vsize = p[13] | ((p[14] << 8) & 0xf00);
200 printf("Detailed timing: %dx%d (%.1f Hz)\n",
202 u16_le(p) * 10000. / (hactive+hblank) / (vactive+vblank));
203 printf("\tPixClk: %.2fMHz\n", u16_le(p) / 100.);
204 printf("\tH: Active=%d Blank=%d SyncOffset=%d SyncWidth=%d Border=%d\n",
205 hactive, hblank, hs_offset, hs_width, hborder);
206 printf("\tV: Active=%d Blank=%d SyncOffset=%d SyncWidth=%d Border=%d\n",
207 vactive, vblank, vs_offset, vs_width, vborder);
208 printf("\tSize: %dx%dmm\n", hsize, vsize);
211 uns stereo = ((flags >> 4) & 6) | (flags & 1);
212 uns input = (flags >> 3) & 3;
214 printf("\tStereo mode: #%d\n", stereo);
216 static const char * const input_types[4] = { "AnalogComposite", "BipolarAnalogComposite", "DigitalComposite", "DigitalSeparate" };
217 printf("\tInput: %s", input_types[input]);
219 printf(" Serrate%c", B(flags & 4));
221 printf(" VPolarity%c", B(flags & 4));
223 printf(" OnRGB%c", B(flags & 2));
225 printf(" CompPolarity%c", B(flags & 2));
227 printf(" HPolarity%c", B(flags & 2));
231 printf("\tCalculated DPI: %dx%d\n",
232 (int)(hactive / (hsize / 25.4)),
233 (int)(vactive / (vsize / 25.4)));
237 show_ascii(char *field, byte *p, uns len)
239 printf("%s: ", field);
244 else if (*p >= 0x20 && *p <= 0x7e)
247 printf("<%02x>", *p);
254 show_descriptor(byte *p)
258 case 0xff: // Serial number
259 show_ascii("Serial", p+5, 13);
261 case 0xfe: // ASCII data
262 show_ascii("Comment", p+5, 13);
264 case 0xfd: // Range limits
265 printf("Limits: Vert=%d-%dHz Horiz=%d-%dkHz PixClk=%dMHz\n",
266 p[5], p[6], p[7], p[8], p[9]*10);
269 case 0: // No secondary timing formula
271 case 2: // Secondary GTF
272 printf("Secondary GTF parameters: fStart=%dkHz C=%.1f M=%d K=%d J=%.1f\n",
273 p[12]*2, p[13]/2., u16_le(p+14), p[16], p[17]/2.);
276 printf("Unidentified secondary timing formula #%02x\n", p[10]);
279 case 0xfc: // Monitor name
280 show_ascii("Monitor name", p+5, 13);
282 case 0xfb: // Color point
283 printf("Color point: [...]\n");
285 case 0xfa: // More standard timings
286 printf("More standard timings: [...]\n");
288 case 0x10: // Dummy descriptor -- entry unused
291 printf("Unknown descriptor type %02x\n", p[3]);
298 if (memcmp(edid, "\000\377\377\377\377\377\377\000", 8))
299 die("Invalid EDID signature, giving up");
300 int ver = edid[0x12];
301 int rev = edid[0x13];
302 printf("EDID %d.%d\n", ver, rev);
303 version = (ver << 8) | rev;
306 for (int i=0; i<128; i++)
309 printf("Invalid checksum: off by %02x\n", sum);
311 show_model(edid + 8);
312 show_basic(edid + 0x14);
313 show_color(edid + 0x19);
314 show_timings(edid + 0x23);
315 for (int i=0; i<4; i++)
317 byte *p = edid + 0x36 + 18*i;
319 show_detailed_timing(p);
325 printf("### %d extension blocks follow, but they are not supported yet ###\n", edid[0x7e]);
328 static void read_binary(void)
330 int c = read(0, edid, 128);
332 die("Read error: %m");
334 die("Read only %d bytes, need 128", c);
344 while (fgets(line, sizeof(line), stdin))
348 if (strstr(line, "EDID (in hex):"))
353 char *c = strchr(line, '\n');
357 for (int i=0; i<32; i++)
362 if (x >= '0' && x <= '9')
364 else if (x >= 'A' && x <= 'F')
366 else if (x >= 'a' && x <= 'f')
373 edid[cnt-1-i/2] |= x << 4;
375 if (c > line && c[-1] != ' ' && c[-1] != '\t')
382 die("No EDID log found");
384 die("EDID log found, but it is too short (only %d bytes)", cnt);
387 die("EDID log parse error at: %s", line);
394 for (int i=0; i<128; i+=16)
397 for (int j=0; j<16; j++)
398 printf(" %02x", edid[i+j]);
406 fprintf(stderr, "Usage: edid <options>\n\n\
408 -l\t\tParse input as Xorg log file and try to find EDID dump there\n\
409 -x\t\tShow hexdump of the whole EDID block\n\
414 int main(int argc, char **argv)
420 while ((opt = getopt(argc, argv, "xl")) >= 0)