]> mj.ucw.cz Git - misc.git/blob - edid.c
155ffa829a85dd8f3a15a3681ac1e3acb317ed4a
[misc.git] / edid.c
1 /*
2  *      Parse VESA Extended Display Identification Data
3  *
4  *      (c) 2011 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include <unistd.h>
12
13 typedef unsigned int uns;
14 typedef unsigned char byte;
15
16 static byte edid[128];
17 static int version;
18
19 static void
20 #ifdef __GNUC__
21         __attribute__((format(printf,1,2)))
22         __attribute__((noreturn))
23 #endif
24 die(char *msg, ...)
25 {
26   va_list args;
27   va_start(args, msg);
28   fprintf(stderr, "edid: ");
29   vfprintf(stderr, msg, args);
30   fputc('\n', stderr);
31   va_end(args);
32   exit(1);
33 }
34
35 static uns
36 u16_le(byte *p)
37 {
38   return p[0] | (p[1] << 8);
39 }
40
41 static uns
42 u32_le(byte *p)
43 {
44   return u16_le(p) | (u16_le(p+2) << 16);
45 }
46
47 static uns
48 B(uns x)
49 {
50   return x ? '+' : '-';
51 }
52
53 static void
54 show_model(byte *p)
55 {
56   printf("Model: %c%c%c %04x #%u\n",
57         ('A' - 1 + ((p[0] >> 2) & 0x1f)),
58         ('A' - 1 + ((p[0] & 3) << 3) | ((p[1] & 0xe0) >> 5)),
59         ('A' - 1 + (p[1] & 0x1f)),
60         u16_le(p+2),
61         u32_le(p+4));
62   printf("Manufactured: %d week %d\n", p[9] + 1990, p[8]);
63 }
64
65 static void
66 show_basic(byte *p)
67 {
68   uns input = p[0];
69   if (input & 0x80)
70     printf("Input: Digital DFP%c\n", B(input & 1));
71   else
72     printf("Input: Analog SigLev=%d Setup%c SepSync%c CompSync%c GreenSync%c SerrSync%c\n",
73         ((input >> 5) & 3),
74         B(input & 0x10),
75         B(input & 0x08),
76         B(input & 0x04),
77         B(input & 0x02),
78         B(input & 0x01));
79
80   printf("Image size: %dx%dcm\n", p[1], p[2]);
81   if (p[3] == 0xff)
82     printf("Gamma: not given\n");
83   else
84     printf("Gamma: %d.%02d\n", (p[3] / 100) + 1, p[3] % 100);
85
86   uns flags = p[4];
87   static const char * const types[4] = { "Mono/Gray", "RGB", "NonRGB", "Unknown" };
88   printf("Flags: Suspend%c Standby%c ActiveOff%c Colors=%s HasSRGB%c PrefTiming%c GTF%c\n",
89         B(flags & 0x80),
90         B(flags & 0x40),
91         B(flags & 0x20),
92         types[(flags >> 3) & 3],
93         B(flags & 0x04),
94         B(flags & 0x02),
95         B(flags & 0x01));
96 }
97
98 static void
99 show_color(byte *p)
100 {
101   uns rx = 4*p[2] | ((p[0] >> 6) & 3);
102   uns ry = 4*p[3] | ((p[0] >> 4) & 3);
103   uns gx = 4*p[4] | ((p[0] >> 2) & 3);
104   uns gy = 4*p[5] | ((p[0]     ) & 3);
105   uns bx = 4*p[5] | ((p[1] >> 6) & 3);
106   uns by = 4*p[7] | ((p[1] >> 4) & 3);
107   uns wx = 4*p[8] | ((p[1] >> 2) & 3);
108   uns wy = 4*p[9] | ((p[1]     ) & 3);
109 #define C(c) ((double)c / 1024)
110   printf("Chromaticity: R=(%.3f,%.3f) G=(%.3f,%.3f) B=(%.3f,%.3f) W=(%.3f,%.3f)\n",
111         C(rx), C(ry), C(gx), C(gy), C(bx), C(by), C(wx), C(wy));
112 #undef C
113 }
114
115 static void
116 show_timings(byte *p)
117 {
118   static const char * const estab_timings[24] = {
119         "720x400@70",
120         "720x400@88",
121         "640x480@60",
122         "640x480@67",
123         "640x480@72",
124         "640x480@75",
125         "800x600@56",
126         "800x600@60",
127         "800x600@72",
128         "800x600@75",
129         "832x624@75",
130         "1024x768@87i",
131         "1024x768@60",
132         "1024x768@70",
133         "1024x768@75",
134         "1280x1024@75",
135         "MFG7",
136         "MFG6",
137         "MFG5",
138         "MFG4",
139         "MFG3",
140         "MFG2",
141         "MFG1",
142         "MFG0",
143   };
144   printf("Basic timings:");
145   for (int i=0; i<24; i++)
146     if (p[i/8] & (0x80 >> (i%8)))
147       printf(" %s", estab_timings[i]);
148   printf("\n");
149
150   printf("Extended timings:");
151   for (int i=0; i<8; i++)
152     {
153       byte *q = p + 3 + 2*i;
154       if (q[0] == 1 && q[1] == 1)
155         continue;
156       uns h = (31 + q[0]) * 8;
157       uns v;
158       switch (q[1] >> 6)
159         {
160         case 0:
161           if (version < 0x0103)
162             v = h;
163           else
164             v = 10*h/16;
165           break;
166         case 1:
167           v = 3*h/4;
168           break;
169         case 2:
170           v = 4*h/5;
171           break;
172         default:
173           v = 9*h/16;
174           break;
175         }
176       printf(" %dx%d@%d", h, v, 60 + (q[1] & 0x3f));
177     }
178   printf("\n");
179 }
180
181 static void
182 show_detailed_timing(byte *p)
183 {
184   uns hactive = p[2] | ((p[4] << 4) & 0xf00);
185   uns hblank  = p[3] | ((p[4] << 8) & 0xf00);
186   uns vactive = p[5] | ((p[7] << 4) & 0xf00);
187   uns vblank  = p[6] | ((p[7] << 8) & 0xf00);
188   uns hs_offset = p[8] | ((p[11] << 2) & 0x300);
189   uns hs_width  = p[9] | ((p[11] << 4) & 0x300);
190   uns vs_offset = (p[10] >> 4) | ((p[11] << 2) & 0x30);
191   uns vs_width  = (p[10] & 0xf) | ((p[11] << 4) & 0x30);
192   uns hsize   = p[12] | ((p[14] << 4) & 0xf00);
193   uns vsize   = p[13] | ((p[14] << 8) & 0xf00);
194   uns hborder = p[15];
195   uns vborder = p[16];
196
197   printf("Detailed timing: %dx%d (%.1f Hz)\n",
198         hactive, vactive,
199         u16_le(p) * 10000. / (hactive+hblank) / (vactive+vblank));
200   printf("\tPixClk: %.2fMHz\n", u16_le(p) / 100.);
201   printf("\tH: Active=%d Blank=%d SyncOffset=%d SyncWidth=%d Border=%d\n",
202         hactive, hblank, hs_offset, hs_width, hborder);
203   printf("\tV: Active=%d Blank=%d SyncOffset=%d SyncWidth=%d Border=%d\n",
204         vactive, vblank, vs_offset, vs_width, vborder);
205   printf("\tSize: %dx%dmm\n", hsize, vsize);
206
207   uns flags = p[17];
208   uns stereo = ((flags >> 4) & 6) | (flags & 1);
209   uns input = (flags >> 3) & 3;
210   if (stereo >= 2)
211     printf("\tStereo mode: #%d\n", stereo);
212
213   static const char * const input_types[4] = { "AnalogComposite", "BipolarAnalogComposite", "DigitalComposite", "DigitalSeparate" };
214   printf("\tInput: %s", input_types[input]);
215   if (input < 3)
216     printf(" Serrate%c", B(flags & 4));
217   else
218     printf(" VPolarity%c", B(flags & 4));
219   if (input < 2)
220     printf(" OnRGB%c", B(flags & 2));
221   else if (input == 2)
222     printf(" CompPolarity%c", B(flags & 2));
223   else
224     printf(" HPolarity%c", B(flags & 2));
225   printf("\n");
226
227   if (hsize && vsize)
228     printf("\tCalculated DPI: %dx%d\n",
229         (int)(hactive / (hsize / 25.4)),
230         (int)(vactive / (vsize / 25.4)));
231 }
232
233 static void
234 show_ascii(char *field, byte *p, uns len)
235 {
236   printf("%s: ", field);
237   while (len--)
238     {
239       if (*p == '\n')
240         break;
241       else if (*p >= 0x20 && *p <= 0x7e)
242         putchar(*p);
243       else
244         printf("<%02x>", *p);
245       p++;
246     }
247   putchar('\n');
248 }
249
250 static void
251 show_descriptor(byte *p)
252 {
253   switch (p[3])
254     {
255     case 0xff:          // Serial number
256       show_ascii("Serial", p+5, 13);
257       break;
258     case 0xfe:          // ASCII data
259       show_ascii("Comment", p+5, 13);
260       break;
261     case 0xfd:          // Range limits
262       printf("Limits: Vert=%d-%dHz Horiz=%d-%dkHz PixClk=%dMHz\n",
263         p[5], p[6], p[7], p[8], p[9]*10);
264       switch (p[10])
265         {
266         case 0:         // No secondary timing formula
267           break;
268         case 2:         // Secondary GTF
269           printf("Secondary GTF parameters: fStart=%dkHz C=%.1f M=%d K=%d J=%.1f\n",
270             p[12]*2, p[13]/2., u16_le(p+14), p[16], p[17]/2.);
271           break;
272         default:
273           printf("Unidentified secondary timing formula #%02x\n", p[10]);
274         }
275       break;
276     case 0xfc:          // Monitor name
277       show_ascii("Monitor name", p+5, 13);
278       break;
279     case 0xfb:          // Color point
280       printf("Color point: [...]\n");
281       break;
282     case 0xfa:          // More standard timings
283       printf("More standard timings: [...]\n");
284       break;
285     case 0x10:          // Dummy descriptor -- entry unused
286       return;
287     default:
288       printf("Unknown descriptor type %02x\n", p[3]);
289     }
290 }
291
292 static void
293 show_edid(void)
294 {
295   if (memcmp(edid, "\000\377\377\377\377\377\377\000", 8))
296     die("Invalid EDID signature, giving up");
297   int ver = edid[0x12];
298   int rev = edid[0x13];
299   printf("EDID %d.%d\n", ver, rev);
300   version = (ver << 8) | rev;
301
302   byte sum = 0;
303   for (int i=0; i<128; i++)
304     sum += edid[i];
305   if (sum)
306     printf("Invalid checksum: off by %02x\n", sum);
307
308   show_model(edid + 8);
309   show_basic(edid + 0x14);
310   show_color(edid + 0x19);
311   show_timings(edid + 0x23);
312   for (int i=0; i<4; i++)
313     {
314       byte *p = edid + 0x36 + 18*i;
315       if (p[0] || p[1])
316         show_detailed_timing(p);
317       else
318         show_descriptor(p);
319     }
320
321   if (edid[0x7e])
322     printf("### %d extension blocks follow, but they are not supported yet ###\n", edid[0x7e]);
323 }
324
325 static void read_binary(void)
326 {
327   int c = read(0, edid, 128);
328   if (c < 0)
329     die("Read error: %m");
330   else if (c < 128)
331     die("Read only %d bytes, need 128", c);
332 }
333
334 static void
335 read_xorg_log(void)
336 {
337   char line[1024];
338   int mode = 0;
339   int cnt = 0;
340
341   while (fgets(line, sizeof(line), stdin))
342     {
343       if (!mode)
344         {
345           if (strstr(line, "EDID (in hex):"))
346             mode = 1;
347         }
348       else
349         {
350           char *c = strchr(line, '\n');
351           if (!c)
352             goto parseerr;
353           cnt += 16;
354           for (int i=0; i<32; i++)
355             {
356               if (c == line)
357                 goto parseerr;
358               int x = *--c;
359               if (x >= '0' && x <= '9')
360                 x -= '0';
361               else if (x >= 'A' && x <= 'F')
362                 x -= 'A' - 10;
363               else if (x >= 'a' && x <= 'f')
364                 x -= 'a' - 10;
365               else
366                 goto parseerr;
367               if (!(i % 2))
368                 edid[cnt-1-i/2] = x;
369               else
370                 edid[cnt-1-i/2] |= x << 4;
371             }
372           if (c > line && c[-1] != ' ' && c[-1] != '\t')
373             goto parseerr;
374           if (cnt == 128)
375             return;
376         }
377     }
378   if (!cnt)
379     die("No EDID log found");
380   else
381     die("EDID log found, but it is too short (only %d bytes)", cnt);
382
383 parseerr:
384   die("EDID log parse error at: %s", line);
385 }
386
387 static void
388 show_hex(void)
389 {
390   printf("\n");
391   for (int i=0; i<128; i+=16)
392     {
393       printf("@%02x:", i);
394       for (int j=0; j<16; j++)
395         printf(" %02x", edid[i+j]);
396       printf("\n");
397     }
398 }
399
400 static void
401 usage(void)
402 {
403   fprintf(stderr, "Usage: edid <options>\n\n\
404 Options:\n\
405 -l\t\tParse input as Xorg log file and try to find EDID dump there\n\
406 -x\t\tShow hexdump of the whole EDID block\n\
407 ");
408   exit(1);
409 }
410
411 int main(int argc, char **argv)
412 {
413   int opt;
414   int hex_mode = 0;
415   int log_mode = 0;
416
417   while ((opt = getopt(argc, argv, "xl")) >= 0)
418     switch (opt)
419       {
420       case 'l':
421         log_mode = 1;
422         break;
423       case 'x':
424         hex_mode = 1;
425         break;
426       default:
427         usage();
428       }
429   if (optind < argc)
430     usage();
431
432   if (log_mode)
433     read_xorg_log();
434   else
435     read_binary();
436
437   show_edid();
438
439   if (hex_mode)
440     show_hex();
441   return 0;
442 }