@@ -55,10 +55,12 @@ SPWG Notebook Panel Specification, Version 3.5
EPI Embedded Panel Interface, Revision 1.0
.RE
.TP
-The following EDID standard is partially supported by edid-decode:
+The following EDID standards are partially supported by edid-decode:
.RS
.TP
DisplayID 1.3: VESA Display Identification Data (DisplayID) Standard, Version 1.3
+.TP
+DisplayID 2.0: VESA DisplayID Standard, Version 2.0
.RE
.SH OPTIONS
@@ -146,5 +146,7 @@ std::string block_name(unsigned char block);
void print_timings(edid_state &state, const char *prefix, const struct timings *t, const char *suffix);
const struct timings *find_dmt_id(unsigned char dmt_id);
+const struct timings *vic_to_mode(unsigned char vic);
+char *extract_string(const unsigned char *x, unsigned len);
#endif
@@ -773,7 +773,7 @@ void edid_state::detailed_cvt_descriptor(const unsigned char *x, bool first)
}
/* extract a string from a detailed subblock, checking for termination */
-static char *extract_string(const unsigned char *x, unsigned len)
+char *extract_string(const unsigned char *x, unsigned len)
{
static char s[EDID_PAGE_SIZE];
int seen_newline = 0;
@@ -191,7 +191,7 @@ static const struct timings edid_cta_modes2[] = {
static const unsigned char edid_hdmi_mode_map[] = { 95, 94, 93, 98 };
-static const struct timings *vic_to_mode(unsigned char vic)
+const struct timings *vic_to_mode(unsigned char vic)
{
if (vic > 0 && vic <= ARRAY_SIZE(edid_cta_modes1))
return edid_cta_modes1 + vic - 1;
@@ -9,6 +9,78 @@
#include "edid-decode.h"
+
+// misc functions
+
+static void check_displayid_datablock_revision(const unsigned char *x)
+{
+ unsigned char revisionflags=x[1];
+ if (revisionflags) {
+ warn("Unexpected revision and flags (0x%02x != 0)\n", revisionflags);
+ }
+}
+
+static bool check_displayid_datablock_length(const unsigned char *x, unsigned expectedlenmin = 0, unsigned expectedlenmax = 128 - 2 - 5 - 3, unsigned payloaddumpstart = 0)
+{
+ unsigned char len=x[2];
+ if ( expectedlenmin == expectedlenmax && len != expectedlenmax ) {
+ fail("DisplayID payload length is different than expected (%d != %d)\n", len, expectedlenmax);
+ } else if (len > expectedlenmax) {
+ fail("DisplayID payload length is greater than expected (%d > %d)\n", len, expectedlenmax);
+ } else if (len < expectedlenmin) {
+ fail("DisplayID payload length is less than expected (%d < %d)\n", len, expectedlenmin);
+ } else {
+ return true;
+ }
+
+ if (len > payloaddumpstart) {
+ hex_block(" ", x + 3 + payloaddumpstart, len - payloaddumpstart);
+ }
+ return false;
+}
+
+// tag 0x01
+
+static const char *feature_support_flags[] = {
+ "De-interlacing",
+ "Support ACP, ISRC1, or ISRC2packets",
+ "Fixed pixel format",
+ "Fixed timing",
+ "Power management (DPM)",
+ "Audio input override",
+ "Separate audio inputs provided",
+ "Audio support on video interface"
+};
+
+static void print_flag_lines(const char *indent, const char *label, unsigned char flag_byte, const char **flags) {
+ if (flag_byte) {
+ printf("%s\n", label);
+ for (int i = 0; i < 8; i++) {
+ if (flag_byte & (1<<i)) {
+ printf("%s%s\n", indent, flags[i]);
+ }
+ }
+ }
+}
+
+static void parse_displayid_parameters(const unsigned char *x) {
+ check_displayid_datablock_revision(x);
+ if (check_displayid_datablock_length(x, 12, 12)) {
+ printf(" Image size: %.1f mm x %.1f mm\n", ((x[4]<<8) + x[3]) / 10.0, ((x[6]<<8) + x[5]) / 10.0);
+ printf(" Pixels: %d x %d\n", (x[8]<<8) + x[7], (x[10]<<8) + x[9]);
+ print_flag_lines(" ", " Feature support flags:", x[11], feature_support_flags);
+
+ if (x[12] != 0xff) {
+ printf(" Gamma: %.2f\n", ((x[12] + 100.0) / 100.0));
+ }
+ printf(" Aspect ratio: %.2f\n", ((x[13] + 100.0) / 100.0));
+ printf(" Dynamic bpc native: %d\n", (x[14] & 0xf) + 1);
+ printf(" Dynamic bpc overall: %d\n", ((x[14] >> 4) & 0xf) + 1);
+ }
+}
+
+// tag 0x03
+
static void parse_displayid_detailed_timing(const unsigned char *x)
{
struct timings t = {};
@@ -112,11 +184,226 @@ static void parse_displayid_detailed_timing(const unsigned char *x)
);
}
+// tag 0x0b
+
+static void parse_displayid_gp_string(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x);
+ if (check_displayid_datablock_length(x)) {
+ printf(" %s\n", extract_string(x + 3, x[2]));
+ }
+}
+
+// tag 0x12
+
+static void parse_displayid_tiled_display_topology(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x);
+ if (check_displayid_datablock_length(x, 22, 22)) {
+ unsigned capabilities = x[3];
+ unsigned num_v_tile = (x[4] & 0xf) | (x[6] & 0x30);
+ unsigned num_h_tile = (x[4] >> 4) | ((x[6] >> 2) & 0x30);
+ unsigned tile_v_location = (x[5] & 0xf) | ((x[6] & 0x3) << 4);
+ unsigned tile_h_location = (x[5] >> 4) | (((x[6] >> 2) & 0x3) << 4);
+ unsigned tile_width = x[7] | (x[8] << 8);
+ unsigned tile_height = x[9] | (x[10] << 8);
+ unsigned pix_mult = x[11];
+
+ printf(" Capabilities: 0x%08x\n", capabilities);
+ printf(" Num horizontal tiles: %u Num vertical tiles: %u\n", num_h_tile + 1, num_v_tile + 1);
+ printf(" Tile location: %u, %u\n", tile_h_location, tile_v_location);
+ printf(" Tile resolution: %ux%u\n", tile_width + 1, tile_height + 1);
+ if (capabilities & 0x40) {
+ if (pix_mult) {
+ printf(" Top bevel size: %u pixels\n",
+ pix_mult * x[12] / 10);
+ printf(" Bottom bevel size: %u pixels\n",
+ pix_mult * x[13] / 10);
+ printf(" Right bevel size: %u pixels\n",
+ pix_mult * x[14] / 10);
+ printf(" Left bevel size: %u pixels\n",
+ pix_mult * x[15] / 10);
+ } else {
+ fail("No bevel information, but the pixel multiplier is non-zero\n");
+ }
+ printf(" Tile resolution: %ux%u\n", tile_width + 1, tile_height + 1);
+ } else if (pix_mult) {
+ fail("No bevel information, but the pixel multiplier is non-zero\n");
+ }
+ }
+}
+
+// tag 0x26
+
+static const char *bpc444[] = {"6", "8", "10", "12", "14", "16", NULL, NULL};
+static const char *bpc4xx[] = {"8", "10", "12", "14", "16", NULL, NULL, NULL};
+static const char *audiorates[] = {"32", "44.1", "48", NULL, NULL, NULL, NULL, NULL};
+
+static const char *colorspace_eotf_combinations[] = {
+ "sRGB",
+ "BT.601",
+ "BT.709/BT.1886",
+ "Adobe RGB",
+ "DCI-P3",
+ "BT.2020",
+ "BT.2020/SMPTE ST 2084"
+};
+
+static const char *colorspace_eotf_reserved[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+static const char *colorspaces[] = {
+ "Undefined",
+ "sRGB",
+ "BT.601",
+ "BT.709",
+ "Adobe RGB",
+ "DCI-P3",
+ "BT.2020",
+ "Custom"
+};
+
+static const char *eotfs[] = {
+ "Undefined",
+ "sRGB",
+ "BT.601",
+ "BT.1886",
+ "Adobe RGB",
+ "DCI-P3",
+ "BT.2020",
+ "Gamma function",
+ "SMPTE ST 2084",
+ "Hybrid Log",
+ "Custom"
+};
+
+static void print_flags(const char *label, unsigned char flag_byte, const char **flags, bool reverse = false)
+{
+ if (flag_byte) {
+ printf("%s: ", label);
+ int countflags = 0;
+ for (int i = 0; i < 8; i++) {
+ if (flag_byte & (1<<(reverse?7-i:i))) {
+ if (countflags)
+ printf(", ");
+ if (flags[i])
+ printf("%s", flags[i]);
+ else
+ printf("Undefined(%d)", i);
+ countflags++;
+ }
+ }
+ printf("\n");
+ }
+}
+
+static void parse_displayid_interface_features(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x);
+ if (!check_displayid_datablock_length(x, 9)) {
+ return;
+ }
+ int len=x[2];
+ if (len > 0) print_flags(" Supported bpc for RGB encoding", x[3], bpc444);
+ if (len > 1) print_flags(" Supported bpc for YCbCr 4:4:4 encoding", x[4], bpc444);
+ if (len > 2) print_flags(" Supported bpc for YCbCr 4:2:2 encoding", x[5], bpc4xx);
+ if (len > 3) print_flags(" Supported bpc for YCbCr 4:2:0 encoding", x[6], bpc4xx);
+ if (len > 4 && x[7])
+ printf(" Minimum pixel rate at which YCbCr 4:2:0 encoding is supported: %.3f MHz\n", 74.25 * x[7]);
+ if (len > 5) print_flags(" Supported audio capability and features (kHz)", x[8], audiorates, true);
+ if (len > 6) print_flags(" Supported color space and EOTF standard combination 1", x[9], colorspace_eotf_combinations);
+ if (len > 7) print_flags(" Supported color space and EOTF standard combination 2", x[10], colorspace_eotf_reserved);
+ int i = 0;
+ if (len > 8 && x[11]) {
+ printf(" Supported color space and EOTF additional combinations:");
+ for (i = 0; i < x[11]; i++) {
+ if (i > 6) {
+ printf("\n Number of additional color space and EOTF combinations (%d) is greater than allowed (7).", x[11]);
+ break;
+ } else if (i + 10 > len) {
+ printf("\n Number of additional color space and EOTF combinations (%d) is too many to fit in block (%d).", x[11], len - 9);
+ break;
+ }
+
+ const char *colorspace = "Out of range";
+ const char *eotf = "Out of range";
+ unsigned colorspace_index = (x[12 + i] >> 4) & 0xf;
+ unsigned eotf_index = x[12 + i] & 0xf;
+ if (colorspace_index < sizeof(colorspaces) / sizeof(colorspaces[0])) {
+ colorspace = colorspaces[colorspace_index];
+ }
+ if (eotf_index < sizeof(eotfs) / sizeof(eotfs[0])) {
+ eotf = eotfs[eotf_index];
+ }
+
+ if (i > 0)
+ printf(", ");
+ if (!strcmp(colorspace, eotf)) {
+ printf("%s", colorspace);
+ } else {
+ printf("%s/%s", colorspace, eotf);
+ }
+ } // for
+ printf("\n");
+ } // x[11]
+ check_displayid_datablock_length(x, 9 + i, 9 + i, 9 + i);
+}
+
+// tag 0x29
+
+static void parse_displayid_ContainerID(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x);
+ if (check_displayid_datablock_length(x, 16, 16)) {
+ x += 3;
+ printf(" %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10],x[11],x[12],x[13],x[14],x[15]);
+ }
+}
+
+// DisplayID main
+
+static std::string product_type(unsigned version, unsigned char x, bool heading)
+{
+ std::string headingstr;
+ if (version < 0x20) {
+ headingstr = "Display Product Type";
+ if (heading) return headingstr;
+ switch (x) {
+ case 0: return "Extension Section";
+ case 1: return "Test Structure; test equipment only";
+ case 2: return "Display panel or other transducer, LCD or PDP module, etc.";
+ case 3: return "Standalone display device";
+ case 4: return "Television receiver";
+ case 5: return "Repeater/translator";
+ case 6: return "DIRECT DRIVE monitor";
+ default: break;
+ }
+ }
+ else
+ {
+ headingstr = "Display Product Primary Use Case";
+ if (heading) return headingstr;
+ switch (x) {
+ case 0: return "Same primary use case as the base section";
+ case 1: return "Test Structure; test equipment only";
+ case 2: return "None of the listed primary use cases; generic display";
+ case 3: return "Television (TV) display";
+ case 4: return "Desktop productivity display";
+ case 5: return "Desktop gaming display";
+ case 6: return "Presentation display";
+ case 7: return "Head-mounted Virtual Reality (VR) display";
+ case 8: return "Head-mounted Augmented Reality (AR) display";
+ default: break;
+ }
+ }
+ fail("Unknown %s 0x%02x\n", headingstr.c_str(), x);
+ return std::string("Unknown " + headingstr + " (") + utohex(x) + ")";
+}
+
void edid_state::parse_displayid_block(const unsigned char *x)
{
- const unsigned char *orig = x;
unsigned version = x[1];
- int length = x[2];
+ unsigned length = x[2];
+ unsigned prod_type = x[3]; // future check: based on type, check for required data blocks
unsigned ext_count = x[4];
unsigned i;
@@ -124,94 +411,114 @@ void edid_state::parse_displayid_block(const unsigned char *x)
block.c_str(), version >> 4, version & 0xf,
length, ext_count);
+ if (ext_count > 0) {
+ warn("Non-0 DisplayID extension count %d\n", ext_count);
+ }
+
+ printf("%s: %s\n", product_type(version, prod_type, true).c_str(), product_type(version, prod_type, false).c_str());
+
+ if (length > 121) {
+ fail("DisplayID length %d is greater than 121\n", length);
+ length = 121;
+ }
+
unsigned offset = 5;
while (length > 0) {
unsigned tag = x[offset];
+ switch (tag) {
+ // DisplayID 1.3:
+ case 0x00: data_block = "Product Identification Data Block (" + utohex(tag) + ")"; break; // not implemented
+ case 0x01: data_block = "Display Parameters Data Block (" + utohex(tag) + ")"; break;
+ case 0x02: data_block = "Color Characteristics Data Block"; break; // not implemented
+ case 0x03: data_block = "Video Timing Modes Type 1 - Detailed Timings Data Block"; break;
+ case 0x04: data_block = "Video Timing Modes Type 2 - Detailed Timings Data Block"; break; // not implemented
+ case 0x05: data_block = "Video Timing Modes Type 3 - Short Timings Data Block"; break; // not implemented
+ case 0x06: data_block = "Video Timing Modes Type 4 - DMT Timings Data Block"; break; // not implemented
+ case 0x07: data_block = "Supported Timing Modes Type 1 - VESA DMT Timings Data Block"; break;
+ case 0x08: data_block = "Supported Timing Modes Type 2 - CTA Timings Data Block"; break;
+ case 0x09: data_block = "Video Timing Range Data Block"; break; // not implemented
+ case 0x0a: data_block = "Product Serial Number Data Block"; break; // not implemented
+ case 0x0b: data_block = "GP ASCII String Data Block"; break;
+ case 0x0c: data_block = "Display Device Data Data Block"; break; // not implemented
+ case 0x0d: data_block = "Interface Power Sequencing Data Block"; break; // not implemented
+ case 0x0e: data_block = "Transfer Characteristics Data Block"; break; // not implemented
+ case 0x0f: data_block = "Display Interface Data Block"; break; // not implemented
+ case 0x10: data_block = "Stereo Display Interface Data Block (" + utohex(tag) + ")"; break; // not implemented
+ case 0x11: data_block = "Video Timing Modes Type 5 - Short Timings Data Block"; break; // not implemented
+ case 0x12: data_block = "Tiled Display Topology Data Block (" + utohex(tag) + ")"; break;
+ case 0x13: data_block = "Video Timing Modes Type 6 - Detailed Timings Data Block"; break; // not implemented
+ // 14h .. 7Eh RESERVED for Additional VESA-defined Data Blocks
+ // DisplayID 2.0
+ case 0x20: data_block = "Product Identification Data Block (" + utohex(tag) + ")"; break; // not implemented
+ case 0x21: data_block = "Display Parameters Data Block (" + utohex(tag) + ")"; break; // not implemented
+ case 0x22: data_block = "Video Timing Modes Type 7 - Detailed Timings Data Block"; break; // not implemented
+ case 0x23: data_block = "Video Timing Modes Type 8 - Enumerated Timing Codes Data Block"; break; // not implemented
+ case 0x24: data_block = "Video Timing Modes Type 9 - Formula-based Timings Data Block"; break; // not implemented
+ case 0x25: data_block = "Dynamic Video Timing Range Limits Data Block"; break; // not implemented
+ case 0x26: data_block = "Display Interface Features Data Block"; break;
+ case 0x27: data_block = "Stereo Display Interface Data Block (" + utohex(tag) + ")"; break; // not implemented
+ case 0x28: data_block = "Tiled Display Topology Data Block (" + utohex(tag) + ")"; break; // not implemented
+ case 0x29: data_block = "ContainerID Data Block"; break;
+ // 2Ah .. 7Dh RESERVED for Additional VESA-defined Data Blocks
+ case 0x7e: // DisplayID 2.0
+ case 0x7f: data_block = "Vendor-specific Data Block (" + utohex(tag) + ")"; break; // DisplayID 1.3 // not implemented
+ // 7Fh .. 80h RESERVED
+ case 0x81: data_block = "CTA DisplayID Data Block (" + utohex(tag) + ")"; break; // not implemented
+ // 82h .. FFh RESERVED
+ default: data_block = "Unknown DisplayID Data Block (" + utohex(tag) + ")"; break;
+ }
+
+ if (length < 3) {
+ // report a problem when the remaining bytes are not 0.
+ if (tag || x[offset + 1]) {
+ fail("Not enough bytes remain (%d) for a DisplayID data block or the DisplayID filler is non-0\n", length);
+ }
+ break;
+ }
+
unsigned len = x[offset + 2];
+
+ if (length < len + 3) {
+ fail("The length of this DisplayID data block (%d) exceeds the number of bytes remaining (%d)\n", len + 3, length);
+ break;
+ }
if (!tag && !len) {
- while (length && !x[offset]) {
- length--;
- offset++;
- }
- if (length)
+ // A Product Identification Data Block with no payload bytes is not valid - assume this is the end.
+ if (!memchk(x + offset, length)) {
fail("Non-0 filler bytes in the DisplayID block\n");
+ }
break;
}
- switch (tag) {
- case 0x00: data_block = "Product ID Data Block"; break;
- case 0x01: data_block = "Display Parameters Data Block"; break;
- case 0x02: data_block = "Color Characteristics Data Block"; break;
- case 0x03: data_block = "Type 1 Detailed Timings Data Block"; break;
- case 0x04: data_block = "Type 2 Detailed Timings Data Block"; break;
- case 0x05: data_block = "Type 3 Short Timings Data Block"; break;
- case 0x06: data_block = "Type 4 DMT Timings Data Block"; break;
- case 0x07: data_block = "Type 1 VESA DMT Timings Data Block"; break;
- case 0x08: data_block = "CTA Timings Data Block"; break;
- case 0x09: data_block = "Video Timing Range Data Block"; break;
- case 0x0a: data_block = "Product Serial Number Data Block"; break;
- case 0x0b: data_block = "GP ASCII String Data Block"; break;
- case 0x0c: data_block = "Display Device Data Data Block"; break;
- case 0x0d: data_block = "Interface Power Sequencing Data Block"; break;
- case 0x0e: data_block = "Transfer Characteristics Data Block"; break;
- case 0x0f: data_block = "Display Interface Data Block"; break;
- case 0x10: data_block = "Stereo Display Interface Data Block"; break;
- case 0x12: data_block = "Tiled Display Topology Data Block"; break;
- default: data_block = "Unknown DisplayID Data Block (" + utohex(tag) + ")"; break;
- }
printf(" %s\n", data_block.c_str());
switch (tag) {
+ case 0x01: parse_displayid_parameters(x + offset); break;
case 0x03:
for (i = 0; i < len / 20; i++) {
parse_displayid_detailed_timing(&x[offset + 3 + (i * 20)]);
}
break;
-
case 0x07:
for (i = 0; i < min(len, 10) * 8; i++)
- if (x[offset + 3 + i / 8] & (1 << (i % 8)))
+ if (x[offset + 3 + i / 8] & (1 << (i % 8))) {
print_timings(" ", find_dmt_id(i + 1), "DMT");
- break;
-
- case 0x12: {
- unsigned capabilities = x[offset + 3];
- unsigned num_v_tile = (x[offset + 4] & 0xf) | (x[offset + 6] & 0x30);
- unsigned num_h_tile = (x[offset + 4] >> 4) | ((x[offset + 6] >> 2) & 0x30);
- unsigned tile_v_location = (x[offset + 5] & 0xf) | ((x[offset + 6] & 0x3) << 4);
- unsigned tile_h_location = (x[offset + 5] >> 4) | (((x[offset + 6] >> 2) & 0x3) << 4);
- unsigned tile_width = x[offset + 7] | (x[offset + 8] << 8);
- unsigned tile_height = x[offset + 9] | (x[offset + 10] << 8);
- unsigned pix_mult = x[offset + 11];
-
- printf(" Capabilities: 0x%08x\n", capabilities);
- printf(" Num horizontal tiles: %u Num vertical tiles: %u\n", num_h_tile + 1, num_v_tile + 1);
- printf(" Tile location: %u, %u\n", tile_h_location, tile_v_location);
- printf(" Tile resolution: %ux%u\n", tile_width + 1, tile_height + 1);
- if (capabilities & 0x40) {
- if (pix_mult) {
- printf(" Top bevel size: %u pixels\n",
- pix_mult * x[offset + 12] / 10);
- printf(" Bottom bevel size: %u pixels\n",
- pix_mult * x[offset + 13] / 10);
- printf(" Right bevel size: %u pixels\n",
- pix_mult * x[offset + 14] / 10);
- printf(" Left bevel size: %u pixels\n",
- pix_mult * x[offset + 15] / 10);
- } else {
- fail("No bevel information, but the pixel multiplier is non-zero\n");
}
- printf(" Tile resolution: %ux%u\n", tile_width + 1, tile_height + 1);
- } else if (pix_mult) {
- fail("No bevel information, but the pixel multiplier is non-zero\n");
- }
break;
- }
-
- default:
- hex_block(" ", x + offset + 3, len);
+ case 0x08:
+ for (i = 0; i < min(len, 8) * 8; i++)
+ if (x[offset + 3 + i / 8] & (1 << (i % 8))) {
+ char suffix[16];
+ sprintf(suffix, "VIC %3u", i + 1);
+ print_timings(" ", vic_to_mode(i + 1), suffix);
+ }
break;
+ case 0x0b: parse_displayid_gp_string(x + offset); break;
+ case 0x12: parse_displayid_tiled_display_topology(x + offset); break;
+ case 0x26: parse_displayid_interface_features(x + offset); break;
+ case 0x29: parse_displayid_ContainerID(x + offset); break;
+ default: hex_block(" ", x + offset + 3, len); break;
}
length -= len + 3;
offset += len + 3;
@@ -223,5 +530,10 @@ void edid_state::parse_displayid_block(const unsigned char *x)
* (excluding DisplayID-in-EDID magic byte)
*/
data_block.clear();
- do_checksum(" ", orig + 1, orig[2] + 5);
+ do_checksum(" ", x + 1, x[2] + 5);
+
+ if (!memchk(x + 1 + x[2] + 5, 0x7f - (1 + x[2] + 5))) {
+ data_block = "Padding";
+ fail("DisplayID padding contains non-zero bytes\n");
+ }
}
- Decode "Display Parameters Block". Example in lg-ultrafine-5k*. - Decode "CTA Timings Block". Similar to "Type 1 VESA DMT Timings Block". - Decode "GP ASCII String Block". Example in dell-up2715k-mdp-switchresx. - Added DisplayID 2.0 tags: - Decode "Display Interface Features Data Block". Example in acer-xv273k* but it appears to be missing the "Minimum pixel rate at which YCbCr 4:2:0 encoding is supported" byte. - Decode "ContainerID Data Block". Example in lg-ultrafine-5k* - Unknown DisplayID blocks are dumped as hex. - Add DisplayID 2.0 spec to man page. - Show DisplayID tag hex byte to make it possible to distinguish between DisplayID 1.3 and 2.0 spec blocks of the same name. - Show DisplayID product type. - Renamed Type* blocks to distinguish between different types of Video Timing Modes and Supported Timing Modes. - Check padding after DisplayID checksum. - Add more checks for DisplayID length and revision. - Move Tiled Topology decode to new function. Signed-off-by: Joe van Tunen <joevt@shaw.ca> --- edid-decode.1 | 4 +- edid-decode.h | 2 + parse-base-block.cpp | 2 +- parse-cta-block.cpp | 2 +- parse-displayid-block.cpp | 446 ++++++++++++++++++++++++++++++++------ 5 files changed, 386 insertions(+), 70 deletions(-)