]> mj.ucw.cz Git - edid.git/blob - edid.c
Updated to EDID v1.4
[edid.git] / edid.c
1 /*
2  *      Parse VESA Extended Display Identification Data
3  *
4  *      (c) 2011--2015 Martin Mares <mj@ucw.cz>
5  *
6  *      This program can be distributed and used under the terms
7  *      of the GNU General Public License version 2 or later.
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 typedef unsigned int uns;
17 typedef unsigned char byte;
18
19 static byte edid[128];
20 static int version;
21 static uns block;
22 static uns num_ext_blocks;
23
24 static void
25 #ifdef __GNUC__
26         __attribute__((format(printf,1,2)))
27         __attribute__((noreturn))
28 #endif
29 die(char *msg, ...)
30 {
31   va_list args;
32   va_start(args, msg);
33   fprintf(stderr, "edid: ");
34   vfprintf(stderr, msg, args);
35   fputc('\n', stderr);
36   va_end(args);
37   exit(1);
38 }
39
40 static uns
41 u16_le(byte *p)
42 {
43   return p[0] | (p[1] << 8);
44 }
45
46 static uns
47 u32_le(byte *p)
48 {
49   return u16_le(p) | (u16_le(p+2) << 16);
50 }
51
52 static uns
53 B(uns x)
54 {
55   return x ? '+' : '-';
56 }
57
58 static void
59 show_model(byte *p)
60 {
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)),
65         u16_le(p+2),
66         u32_le(p+4));
67
68   uns y = p[9] + 1990;
69   if (p[8] == 0xff)
70     printf("Model year: %d\n", y);
71   else if (p[8])
72     printf("Manufactured: %d week %d\n", y, p[8]);
73   else
74     printf("Manufactured: %d, week unspecified\n", y);
75 }
76
77 static void
78 show_basic(byte *p)
79 {
80   uns input = p[0];
81   if (input & 0x80)
82     {
83       printf("Input: Digital,");
84       if (version < 0x104)
85         printf(" DFP%c\n", B(input & 1));
86       else
87         {
88           uns depth = (input >> 4) & 7;
89           static const char * const depths[] = { "undefined", "6", "8", "10", "12", "14", "16", "RFU7" };
90
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"
95           };
96
97           printf(" Depth=%s Iface=%s\n", depths[depth], ifaces[iface]);
98         }
99     }
100   else
101     printf("Input: Analog, SigLev=%d Setup%c SepSync%c CompSync%c GreenSync%c SerrSync%c\n",
102         ((input >> 5) & 3),
103         B(input & 0x10),
104         B(input & 0x08),
105         B(input & 0x04),
106         B(input & 0x02),
107         B(input & 0x01));
108
109   if (!p[1] && !p[2])
110     printf("Image size: undefined\n");
111   else if (!p[2])
112     printf("Image aspect ratio: %u.%02u:1\n", 1 + p[1]/100, p[1] % 100);
113   else if (!p[1])
114     printf("Image aspect ratio: 1:%u.%02u\n", 1 + p[2]/100, p[2] % 100);
115   else
116     printf("Image size: %dx%dcm\n", p[1], p[2]);
117
118   if (p[3] == 0xff)
119     printf("Gamma: not given\n");
120   else
121     printf("Gamma: %d.%02d\n", 1 + p[3]/100, p[3] % 100);
122
123   uns flags = p[4];
124   static const char * const color_types[4] = { "Mono/Gray", "RGB", "NonRGB", "Unknown" };
125   static const char * const color_encodings[4] = {
126     "RGB444",
127     "RGB444/YCrCb444",
128     "RGB444/YCrCb422",
129     "RGB444/YCrCb444/YCrCb422",
130   };
131   printf("Flags: Suspend%c Standby%c ActiveOff%c",
132         B(flags & 0x80),
133         B(flags & 0x40),
134         B(flags & 0x20));
135   if (version >= 0x104 && (input & 0x80))
136     printf(" Colors=%s", color_encodings[(flags >> 3) & 3]);
137   else
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));
142 }
143
144 static void
145 show_color(byte *p)
146 {
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));
158 #undef C
159 }
160
161 static void
162 show_timings(byte *p)
163 {
164   static const char * const estab_timings[24] = {
165         "720x400@70",
166         "720x400@88",
167         "640x480@60",
168         "640x480@67",
169         "640x480@72",
170         "640x480@75",
171         "800x600@56",
172         "800x600@60",
173         "800x600@72",
174         "800x600@75",
175         "832x624@75",
176         "1024x768@87i",
177         "1024x768@60",
178         "1024x768@70",
179         "1024x768@75",
180         "1280x1024@75",
181         "1152x870@75",
182         "MFG6",
183         "MFG5",
184         "MFG4",
185         "MFG3",
186         "MFG2",
187         "MFG1",
188         "MFG0",
189   };
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]);
194   printf("\n");
195
196   printf("Standard timings:");
197   for (int i=0; i<8; i++)
198     {
199       byte *q = p + 3 + 2*i;
200       if (q[0] == 1 && q[1] == 1)
201         continue;
202       uns h = (31 + q[0]) * 8;
203       uns v;
204       switch (q[1] >> 6)
205         {
206         case 0:
207           if (version < 0x0103)
208             v = h;
209           else
210             v = 10*h/16;
211           break;
212         case 1:
213           v = 3*h/4;
214           break;
215         case 2:
216           v = 4*h/5;
217           break;
218         default:
219           v = 9*h/16;
220           break;
221         }
222       printf(" %dx%d@%d", h, v, 60 + (q[1] & 0x3f));
223     }
224   printf("\n");
225 }
226
227 static void
228 show_detailed_timing(byte *p)
229 {
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);
240   uns hborder = p[15];
241   uns vborder = p[16];
242
243   printf("Detailed timing: %dx%d (%.1f Hz)\n",
244         hactive, vactive,
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);
252
253   uns flags = p[17];
254   uns stereo = ((flags >> 4) & 6) | (flags & 1);
255   static const char * const stereo_types[8] = {
256     "none",
257     "none",
258     "FieldSeq/RightOn1",
259     "2-wayIL/RightEven",
260     "FieldSeq/LeftOn1",
261     "2-wayIL/LeftEven",
262     "4-wayIL",
263     "SideBySide",
264   };
265   printf("\tFlags: Interlace%c Stereo=%s\n", B(flags & 0x80), stereo_types[stereo]);
266   if (stereo >= 2)
267     printf("\tStereo mode: #%d\n", stereo);
268
269   uns sync = (flags >> 3) & 3;
270
271   static const char * const sync_types[4] = { "AnalogComposite", "BipolarAnalogComposite", "DigitalComposite", "DigitalSeparate" };
272   printf("\tSync: %s", sync_types[sync]);
273   if (sync < 3)
274     printf(" Serrate%c", B(flags & 4));
275   else
276     printf(" VPolarity%c", B(flags & 4));
277   if (sync < 2)
278     printf(" OnRGB%c", B(flags & 2));
279   else if (sync == 2)
280     printf(" Polarity%c", B(flags & 2));
281   else
282     printf(" HPolarity%c", B(flags & 2));
283   printf("\n");
284
285   if (hsize && vsize)
286     printf("\tCalculated DPI: %dx%d\n",
287         (int)(hactive / (hsize / 25.4)),
288         (int)(vactive / (vsize / 25.4)));
289 }
290
291 static void
292 show_ascii(char *field, byte *p, uns len)
293 {
294   printf("%s: ", field);
295   while (len--)
296     {
297       if (*p == '\n')
298         break;
299       else if (*p >= 0x20 && *p <= 0x7e)
300         putchar(*p);
301       else
302         printf("<%02x>", *p);
303       p++;
304     }
305   putchar('\n');
306 }
307
308 static void
309 show_limits(byte *p)
310 {
311   uns flags = p[4];
312   uns voffset = flags & 3;
313   uns hoffset = (flags >> 2) & 3;
314
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),
320     p[9]*10);
321
322   switch (p[10])
323     {
324     case 0:             // No secondary timing formula
325       printf(" (no formula)\n");
326       break;
327     case 1:             // Range limits only
328       printf(" (range only)\n");
329       break;
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.);
334       break;
335     case 4:
336       printf(" (CVT v%d.%d)\n", p[11] >> 4, p[11] & 0x0f);
337       printf("\t[...]\n");
338       break;
339     default:
340       printf(" (unknown formula #%02x)\n", p[10]);
341     }
342 }
343
344 static void
345 show_descriptor(byte *p)
346 {
347   switch (p[3])
348     {
349     case 0xff:          // Serial number
350       show_ascii("Serial", p+5, 13);
351       break;
352     case 0xfe:          // ASCII data
353       show_ascii("Comment", p+5, 13);
354       break;
355     case 0xfd:          // Range limits
356       show_limits(p);
357       break;
358     case 0xfc:          // Product name
359       show_ascii("Product name", p+5, 13);
360       break;
361     case 0xfb:          // Color point
362       printf("Color point: [...]\n");
363       break;
364     case 0xfa:          // More standard timings
365       printf("More standard timings: [...]\n");
366       break;
367     case 0xf9:
368       printf("Display color management: [...]\n");
369       break;
370     case 0xf8:
371       printf("CVT-3: [...]\n");
372       break;
373     case 0xf7:
374       printf("Established timings 3: [...]\n");
375       break;
376     case 0x10:          // Dummy descriptor -- entry unused
377       return;
378     default:
379       printf("%s descriptor type %02x\n", (p[3] <= 0x0f ? "Vendor-specific" : "Unknown"), p[3]);
380     }
381 }
382
383 static void
384 check_sum(void)
385 {
386   byte sum = 0;
387   for (int i=0; i<128; i++)
388     sum += edid[i];
389   if (sum)
390     printf("Invalid checksum: off by %02x\n", sum);
391 }
392
393 static void
394 show_edid(void)
395 {
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;
402
403   if (version >= 0x200)
404     die("Unsupported version");
405   check_sum();
406
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++)
412     {
413       byte *p = edid + 0x36 + 18*i;
414       if (p[0] || p[1])
415         show_detailed_timing(p);
416       else
417         show_descriptor(p);
418     }
419
420   num_ext_blocks = edid[0x7e];
421 }
422
423 static void ext_block_map(void)
424 {
425   for (uns i=1; i<=0xfe; i++)
426     if (edid[i])
427       printf("Block %u: extension %u\n", i, edid[i]);
428 }
429
430 struct extension {
431   const char *name;
432   void (*parser)(void);
433 };
434
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 },
443 };
444
445 static void show_ext(void)
446 {
447   byte ext = edid[0];
448   const struct extension *e = &ext_table[ext];
449
450   printf("\n>> Extension block %u: ", block);
451   if (e->name)
452     printf("%s", e->name);
453   else
454     printf("Extension #%02x", ext);
455   if (ext != 0xf0)
456     printf(" (version %02x)", edid[1]);
457   printf("\n\n");
458
459   check_sum();
460
461   if (e->parser)
462     e->parser();
463   else
464     printf("!!! Not decoded yet\n");
465 }
466
467 static void read_binary(void)
468 {
469   int c = read(0, edid, 128);
470   if (c < 0)
471     die("Read error: %m");
472   else if (c < 128)
473     die("Read only %d bytes, need 128", c);
474 }
475
476 static void
477 read_xorg_log(void)
478 {
479   char line[1024];
480   static int mode = 0;
481   int cnt = 0;
482
483   while (fgets(line, sizeof(line), stdin))
484     {
485       if (!mode)
486         {
487           if (strstr(line, "EDID (in hex):"))
488             mode = 1;
489         }
490       else
491         {
492           char *c = strchr(line, '\n');
493           if (!c)
494             goto parseerr;
495           cnt += 16;
496           for (int i=0; i<32; i++)
497             {
498               if (c == line)
499                 goto parseerr;
500               int x = *--c;
501               if (x >= '0' && x <= '9')
502                 x -= '0';
503               else if (x >= 'A' && x <= 'F')
504                 x -= 'A' - 10;
505               else if (x >= 'a' && x <= 'f')
506                 x -= 'a' - 10;
507               else
508                 goto parseerr;
509               if (!(i % 2))
510                 edid[cnt-1-i/2] = x;
511               else
512                 edid[cnt-1-i/2] |= x << 4;
513             }
514           if (c > line && c[-1] != ' ' && c[-1] != '\t')
515             goto parseerr;
516           if (cnt == 128)
517             return;
518         }
519     }
520   if (!cnt)
521     die("No EDID log found");
522   else
523     die("EDID log found, but it is too short (only %d bytes)", cnt);
524
525 parseerr:
526   die("EDID log parse error at: %s", line);
527 }
528
529 static void
530 show_hex(void)
531 {
532   printf("\n");
533   for (int i=0; i<128; i+=16)
534     {
535       printf("@%02x:", i);
536       for (int j=0; j<16; j++)
537         printf(" %02x", edid[i+j]);
538       printf("\n");
539     }
540 }
541
542 static void
543 usage(void)
544 {
545   fprintf(stderr, "Usage: edid <options>\n\n\
546 Options:\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\
549 ");
550   exit(1);
551 }
552
553 int main(int argc, char **argv)
554 {
555   int opt;
556   int hex_mode = 0;
557   int log_mode = 0;
558
559   while ((opt = getopt(argc, argv, "xl")) >= 0)
560     switch (opt)
561       {
562       case 'l':
563         log_mode = 1;
564         break;
565       case 'x':
566         hex_mode = 1;
567         break;
568       default:
569         usage();
570       }
571   if (optind < argc)
572     usage();
573
574   while (block <= num_ext_blocks)
575     {
576       if (log_mode)
577         read_xorg_log();
578       else
579         read_binary();
580
581       if (!block)
582         show_edid();
583       else
584         show_ext();
585
586       if (hex_mode)
587         show_hex();
588       block++;
589     }
590
591   return 0;
592 }