/*
* Parse VESA Extended Display Identification Data
*
- * (c) 2011 Martin Mares <mj@ucw.cz>
+ * (c) 2011--2015 Martin Mares <mj@ucw.cz>
*
* This program can be distributed and used under the terms
* of the GNU General Public License version 2 or later.
static byte edid[128];
static int version;
+static uns block;
+static uns num_ext_blocks;
static void
#ifdef __GNUC__
('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
{
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),
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
"1024x768@70",
"1024x768@75",
"1280x1024@75",
- "MFG7",
+ "1152x870@75",
"MFG6",
"MFG5",
"MFG4",
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;
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");
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)
{
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");
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)
{
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);
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)
read_xorg_log(void)
{
char line[1024];
- int mode = 0;
+ static int mode = 0;
int cnt = 0;
while (fgets(line, sizeof(line), stdin))
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;
}