From 1c406a8dc954865519512d903a19d48862289af9 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sat, 7 Nov 2015 22:15:13 +0100 Subject: [PATCH] Updated to EDID v1.4 Some parts are not fully decoded yet. --- Makefile | 8 ++ edid.c | 253 +++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 208 insertions(+), 53 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f2f96c9 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +CC=gcc +LD=gcc +CFLAGS=-O2 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 + +all: edid + +clean: + rm -f *.o edid diff --git a/edid.c b/edid.c index d6972d1..d781536 100644 --- a/edid.c +++ b/edid.c @@ -1,7 +1,7 @@ /* * Parse VESA Extended Display Identification Data * - * (c) 2011 Martin Mares + * (c) 2011--2015 Martin Mares * * This program can be distributed and used under the terms * of the GNU General Public License version 2 or later. @@ -18,6 +18,8 @@ typedef unsigned char byte; static byte edid[128]; static int version; +static uns block; +static uns num_ext_blocks; static void #ifdef __GNUC__ @@ -62,7 +64,14 @@ show_model(byte *p) ('A' - 1 + (p[1] & 0x1f)), u16_le(p+2), u32_le(p+4)); - printf("Manufactured: %d week %d\n", p[9] + 1990, p[8]); + + uns y = p[9] + 1990; + if (p[8] == 0xff) + printf("Model year: %d\n", y); + else if (p[8]) + printf("Manufactured: %d week %d\n", y, p[8]); + else + printf("Manufactured: %d, week unspecified\n", y); } static void @@ -70,9 +79,26 @@ show_basic(byte *p) { uns input = p[0]; if (input & 0x80) - printf("Input: Digital DFP%c\n", B(input & 1)); + { + printf("Input: Digital,"); + if (version < 0x104) + printf(" DFP%c\n", B(input & 1)); + else + { + uns depth = (input >> 4) & 7; + static const char * const depths[] = { "undefined", "6", "8", "10", "12", "14", "16", "RFU7" }; + + uns iface = input & 0x0f; + static const char * const ifaces[] = { + "undefined", "DVI", "HDMI-a", "HDMI-b", "MDDI", "DisplayPort", "RFU6", "RFU7", + "RFU8", "RFU9", "RFU10", "RFU11", "RFU12", "RFU13", "RFU14", "RFU16" + }; + + printf(" Depth=%s Iface=%s\n", depths[depth], ifaces[iface]); + } + } else - printf("Input: Analog SigLev=%d Setup%c SepSync%c CompSync%c GreenSync%c SerrSync%c\n", + printf("Input: Analog, SigLev=%d Setup%c SepSync%c CompSync%c GreenSync%c SerrSync%c\n", ((input >> 5) & 3), B(input & 0x10), B(input & 0x08), @@ -80,22 +106,39 @@ show_basic(byte *p) B(input & 0x02), B(input & 0x01)); - printf("Image size: %dx%dcm\n", p[1], p[2]); + if (!p[1] && !p[2]) + printf("Image size: undefined\n"); + else if (!p[2]) + printf("Image aspect ratio: %u.%02u:1\n", 1 + p[1]/100, p[1] % 100); + else if (!p[1]) + printf("Image aspect ratio: 1:%u.%02u\n", 1 + p[2]/100, p[2] % 100); + else + printf("Image size: %dx%dcm\n", p[1], p[2]); + if (p[3] == 0xff) printf("Gamma: not given\n"); else - printf("Gamma: %d.%02d\n", (p[3] / 100) + 1, p[3] % 100); + printf("Gamma: %d.%02d\n", 1 + p[3]/100, p[3] % 100); uns flags = p[4]; - static const char * const types[4] = { "Mono/Gray", "RGB", "NonRGB", "Unknown" }; - printf("Flags: Suspend%c Standby%c ActiveOff%c Colors=%s HasSRGB%c PrefTiming%c GTF%c\n", + static const char * const color_types[4] = { "Mono/Gray", "RGB", "NonRGB", "Unknown" }; + static const char * const color_encodings[4] = { + "RGB444", + "RGB444/YCrCb444", + "RGB444/YCrCb422", + "RGB444/YCrCb444/YCrCb422", + }; + printf("Flags: Suspend%c Standby%c ActiveOff%c", B(flags & 0x80), B(flags & 0x40), - B(flags & 0x20), - types[(flags >> 3) & 3], - B(flags & 0x04), - B(flags & 0x02), - B(flags & 0x01)); + B(flags & 0x20)); + if (version >= 0x104 && (input & 0x80)) + printf(" Colors=%s", color_encodings[(flags >> 3) & 3]); + else + printf(" Colors=%s", color_types[(flags >> 3) & 3]); + printf(" HasSRGB%c", B(flags & 0x04)); + printf(" %s%c", (version >= 0x104 ? "PreferredIsNative" : "PrefTiming"), B(flags & 0x02)); + printf(" %s%c\n", (version >= 0x104 ? "ContinuousFreq" : "GTF"), B(flags & 0x01)); } static void @@ -135,7 +178,7 @@ show_timings(byte *p) "1024x768@70", "1024x768@75", "1280x1024@75", - "MFG7", + "1152x870@75", "MFG6", "MFG5", "MFG4", @@ -150,7 +193,7 @@ show_timings(byte *p) printf(" %s", estab_timings[i]); printf("\n"); - printf("Extended timings:"); + printf("Standard timings:"); for (int i=0; i<8; i++) { byte *q = p + 3 + 2*i; @@ -209,20 +252,32 @@ show_detailed_timing(byte *p) uns flags = p[17]; uns stereo = ((flags >> 4) & 6) | (flags & 1); - uns input = (flags >> 3) & 3; + static const char * const stereo_types[8] = { + "none", + "none", + "FieldSeq/RightOn1", + "2-wayIL/RightEven", + "FieldSeq/LeftOn1", + "2-wayIL/LeftEven", + "4-wayIL", + "SideBySide", + }; + printf("\tFlags: Interlace%c Stereo=%s\n", B(flags & 0x80), stereo_types[stereo]); if (stereo >= 2) printf("\tStereo mode: #%d\n", stereo); - static const char * const input_types[4] = { "AnalogComposite", "BipolarAnalogComposite", "DigitalComposite", "DigitalSeparate" }; - printf("\tInput: %s", input_types[input]); - if (input < 3) + uns sync = (flags >> 3) & 3; + + static const char * const sync_types[4] = { "AnalogComposite", "BipolarAnalogComposite", "DigitalComposite", "DigitalSeparate" }; + printf("\tSync: %s", sync_types[sync]); + if (sync < 3) printf(" Serrate%c", B(flags & 4)); else printf(" VPolarity%c", B(flags & 4)); - if (input < 2) + if (sync < 2) printf(" OnRGB%c", B(flags & 2)); - else if (input == 2) - printf(" CompPolarity%c", B(flags & 2)); + else if (sync == 2) + printf(" Polarity%c", B(flags & 2)); else printf(" HPolarity%c", B(flags & 2)); printf("\n"); @@ -250,6 +305,42 @@ show_ascii(char *field, byte *p, uns len) putchar('\n'); } +static void +show_limits(byte *p) +{ + uns flags = p[4]; + uns voffset = flags & 3; + uns hoffset = (flags >> 2) & 3; + + printf("Limits: Vert=%d-%dHz Horiz=%d-%dkHz PixClk=%dMHz", + p[5] + (voffset == 3 ? 255 : 0), + p[6] + (voffset & 2 ? 255 : 0), + p[7] + (hoffset == 3 ? 255 : 0), + p[8] + (hoffset & 2 ? 255 : 0), + p[9]*10); + + switch (p[10]) + { + case 0: // No secondary timing formula + printf(" (no formula)\n"); + break; + case 1: // Range limits only + printf(" (range only)\n"); + break; + case 2: // Secondary GTF + printf(" (2ndary GTF)\n"); + printf("\tSecondary GTF parameters: fStart=%dkHz C=%.1f M=%d K=%d J=%.1f\n", + p[12]*2, p[13]/2., u16_le(p+14), p[16], p[17]/2.); + break; + case 4: + printf(" (CVT v%d.%d)\n", p[11] >> 4, p[11] & 0x0f); + printf("\t[...]\n"); + break; + default: + printf(" (unknown formula #%02x)\n", p[10]); + } +} + static void show_descriptor(byte *p) { @@ -262,22 +353,10 @@ show_descriptor(byte *p) show_ascii("Comment", p+5, 13); break; case 0xfd: // Range limits - printf("Limits: Vert=%d-%dHz Horiz=%d-%dkHz PixClk=%dMHz\n", - p[5], p[6], p[7], p[8], p[9]*10); - switch (p[10]) - { - case 0: // No secondary timing formula - break; - case 2: // Secondary GTF - printf("Secondary GTF parameters: fStart=%dkHz C=%.1f M=%d K=%d J=%.1f\n", - p[12]*2, p[13]/2., u16_le(p+14), p[16], p[17]/2.); - break; - default: - printf("Unidentified secondary timing formula #%02x\n", p[10]); - } + show_limits(p); break; - case 0xfc: // Monitor name - show_ascii("Monitor name", p+5, 13); + case 0xfc: // Product name + show_ascii("Product name", p+5, 13); break; case 0xfb: // Color point printf("Color point: [...]\n"); @@ -285,13 +364,32 @@ show_descriptor(byte *p) case 0xfa: // More standard timings printf("More standard timings: [...]\n"); break; + case 0xf9: + printf("Display color management: [...]\n"); + break; + case 0xf8: + printf("CVT-3: [...]\n"); + break; + case 0xf7: + printf("Established timings 3: [...]\n"); + break; case 0x10: // Dummy descriptor -- entry unused return; default: - printf("Unknown descriptor type %02x\n", p[3]); + printf("%s descriptor type %02x\n", (p[3] <= 0x0f ? "Vendor-specific" : "Unknown"), p[3]); } } +static void +check_sum(void) +{ + byte sum = 0; + for (int i=0; i<128; i++) + sum += edid[i]; + if (sum) + printf("Invalid checksum: off by %02x\n", sum); +} + static void show_edid(void) { @@ -302,11 +400,9 @@ show_edid(void) printf("EDID %d.%d\n", ver, rev); version = (ver << 8) | rev; - byte sum = 0; - for (int i=0; i<128; i++) - sum += edid[i]; - if (sum) - printf("Invalid checksum: off by %02x\n", sum); + if (version >= 0x200) + die("Unsupported version"); + check_sum(); show_model(edid + 8); show_basic(edid + 0x14); @@ -321,8 +417,51 @@ show_edid(void) show_descriptor(p); } - if (edid[0x7e]) - printf("### %d extension blocks follow, but they are not supported yet ###\n", edid[0x7e]); + num_ext_blocks = edid[0x7e]; +} + +static void ext_block_map(void) +{ + for (uns i=1; i<=0xfe; i++) + if (edid[i]) + printf("Block %u: extension %u\n", i, edid[i]); +} + +struct extension { + const char *name; + void (*parser)(void); +}; + +static const struct extension ext_table[0x100] = { + [0x02] = { "CEA 861", NULL }, + [0x10] = { "Video Timing Block", NULL }, + [0x40] = { "Display Information", NULL }, + [0x50] = { "Localized Strings", NULL }, + [0x60] = { "Digital Packet Video Link", NULL }, + [0xf0] = { "Block map", ext_block_map }, + [0xff] = { "Vendor-specific", NULL }, +}; + +static void show_ext(void) +{ + byte ext = edid[0]; + const struct extension *e = &ext_table[ext]; + + printf("\n>> Extension block %u: ", block); + if (e->name) + printf("%s", e->name); + else + printf("Extension #%02x", ext); + if (ext != 0xf0) + printf(" (version %02x)", edid[1]); + printf("\n\n"); + + check_sum(); + + if (e->parser) + e->parser(); + else + printf("!!! Not decoded yet\n"); } static void read_binary(void) @@ -338,7 +477,7 @@ static void read_xorg_log(void) { char line[1024]; - int mode = 0; + static int mode = 0; int cnt = 0; while (fgets(line, sizeof(line), stdin)) @@ -432,14 +571,22 @@ int main(int argc, char **argv) if (optind < argc) usage(); - if (log_mode) - read_xorg_log(); - else - read_binary(); + while (block <= num_ext_blocks) + { + if (log_mode) + read_xorg_log(); + else + read_binary(); + + if (!block) + show_edid(); + else + show_ext(); - show_edid(); + if (hex_mode) + show_hex(); + block++; + } - if (hex_mode) - show_hex(); return 0; } -- 2.39.2