From: Martin Mares Date: Sat, 7 Nov 2015 22:19:06 +0000 (+0100) Subject: Decoding CEA 861 extension including HDMI X-Git-Tag: v1.1~5 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=11be869cff5608a177413871bf657fa1f8190e59;p=edid.git Decoding CEA 861 extension including HDMI --- diff --git a/edid.c b/edid.c index d781536..bf9c04d 100644 --- a/edid.c +++ b/edid.c @@ -55,6 +55,8 @@ B(uns x) return x ? '+' : '-'; } +/*** EDID Basics ***/ + static void show_model(byte *p) { @@ -224,6 +226,8 @@ show_timings(byte *p) printf("\n"); } +/*** EDID Descriptors ***/ + static void show_detailed_timing(byte *p) { @@ -420,6 +424,8 @@ show_edid(void) num_ext_blocks = edid[0x7e]; } +/*** Extensions ***/ + static void ext_block_map(void) { for (uns i=1; i<=0xfe; i++) @@ -427,13 +433,304 @@ static void ext_block_map(void) printf("Block %u: extension %u\n", i, edid[i]); } +static void ext_hdmi(byte *p, uns len) +{ + if (len < 6) + { + printf("\t!!! Truncated\n"); + return; + } + printf("\tPhysical address: %u.%u.%u.%u\n", p[4], p[5], p[6], p[7]); + + if (len < 7) + return; + uns f = p[6]; + printf("\tFlags: AI%c 48bpp%c 36bpp%c 30bpp%c DeepColor-YCbCr444%c DVIDual%c\n", + B(f & 0x80), B(f & 0x40), B(f & 0x20), B(f & 0x10), + B(f & 0x08), B(f & 0x01)); + + if (len < 8) + return; + printf("\tMax TMDS Clock [MHz]: %u\n", 5 * p[8]); + + if (len < 9) + return; + uns f2 = p[8]; + + uns i = 9; + if (f2 & 0x80) // Latency fields present + { + if (i+2 > len) + { + printf("\t!!! Truncated latency fields\n"); + return; + } + printf("\tLatency [ms]:"); + if (p[i] && p[i] != 0xff) + printf(" Video=%u", (p[i]-1)*2); + if (p[i+1] && p[i+1] != 0xff) + printf(" Audio=%u", (p[i+1]-1)*2); + i += 2; + + if (f2 & 0x40) // Interlaced latency present + { + if (i+2 > len) + { + printf("\t!!! Truncated latency fields\n"); + return; + } + printf("\tInterlaced latency [ms]:"); + if (p[i] && p[i] != 0xff) + printf(" Video=%u", (p[i]-1)*2); + if (p[i+1] && p[i+1] != 0xff) + printf(" Audio=%u", (p[i+1]-1)*2); + i += 2; + } + } +} + +// A table of CEA video modes taken from https://en.wikipedia.org/wiki/Extended_Display_Identification_Data +static const char * const cea_modes[] = { + [1] = "640x480p@60 4:3", + [2] = "720x480p@60 4:3", + [3] = "720x480p@60 16:9", + [4] = "1280x720p@60 16:9", + [5] = "1920x1080i@60 16:9", + [6] = "720(1440)x480i@60 4:3", + [7] = "720(1440)x480i@60 16:9", + [8] = "720(1440)x240p@60 4:3", + [9] = "720(1440)x240p@60 16:9", + [10] = "(2880)x480i@60 4:3", + [11] = "(2880)x480i@60 16:9", + [12] = "(2880)x240p@60 4:3", + [13] = "(2880)x240p@60 16:9", + [14] = "1440x480p@60 4:3", + [15] = "1440x480p@60 16:9", + [16] = "1920x1080p@60 16:9", + [17] = "720x576p@50 4:3", + [18] = "720x576p@50 16:9", + [19] = "1280x720p@50 16:9", + [20] = "1920x1080i@50 16:9", + [21] = "720(1440)x576i@50 4:3", + [22] = "720(1440)x576i@50 16:9", + [23] = "720(1440)x288p@50 4:3", + [24] = "720(1440)x288p@50 16:9", + [25] = "(2880)x576i@50 4:3", + [26] = "(2880)x576i@50 16:9", + [27] = "(2880)x288p@50 4:3", + [28] = "(2880)x288p@50 16:9", + [29] = "1440x576p@50 4:3", + [30] = "1440x576p@50 16:9", + [31] = "1920x1080p@50 16:9", + [32] = "1920x1080p@24 16:9", + [33] = "1920x1080p@25 16:9", + [34] = "1920x1080p@30 16:9", + [35] = "(2880)x480p@60 4:3", + [36] = "(2880)x480p@60 16:9", + [37] = "(2880)x576p@50 4:3", + [38] = "(2880)x576p@50 16:9", + [39] = "1920x1080i@50 16:9", + [40] = "1920x1080i@100 16:9", + [41] = "1280x720p@100 16:9", + [42] = "720x576p@100 4:3", + [43] = "720x576p@100 16:9", + [44] = "720(1440)x576i@100 4:3", + [45] = "720(1440)x576i@100 16:9", + [46] = "1920x1080i@120 16:9", + [47] = "1280x720p@120 16:9", + [48] = "720x480p@120 4:3", + [49] = "720x480p@120 16:9", + [50] = "720(1440)x480i@120 4:3", + [51] = "720(1440)x480i@120 16:9", + [52] = "720x576p@200 4:3", + [53] = "720x576p@200 16:9", + [54] = "720(1440)x576i@200 4:3", + [55] = "720(1440)x576i@200 16:9", + [56] = "720x480p@240 4:3", + [57] = "720x480p@240 16:9", + [58] = "720(1440)x480i@240 4:3", + [59] = "720(1440)x480i@240 16:9", + [60] = "1280x720p@24 16:9", + [61] = "1280x720p@25 16:9", + [62] = "1280x720p@30 16:9", + [63] = "1920x1080p@120 16:9", + [64] = "1920x1080p@100 16:9", + [65] = "1280x720p@24 64:27", + [66] = "1280x720p@25 64:27", + [67] = "1280x720p@30 64:27", + [68] = "1280x720p@50 64:27", + [69] = "1280x720p@60 64:27", + [70] = "1280x720p@100 64:27", + [71] = "1280x720p@120 64:27", + [72] = "1920x1080p@24 64:27", + [73] = "1920x1080p@25 64:27", + [74] = "1920x1080p@30 64:27", + [75] = "1920x1080p@50 64:27", + [76] = "1920x1080p@60 64:27", + [77] = "1920x1080p@100 64:27", + [78] = "1920x1080p@120 64:27", + [79] = "1680x720p@24 64:27", + [80] = "1680x720p@25 64:27", + [81] = "1680x720p@30 64:27", + [82] = "1680x720p@50 64:27", + [83] = "1680x720p@60 64:27", + [84] = "1680x720p@100 64:27", + [85] = "1680x720p@120 64:27", + [86] = "2560x1080p@24 64:27", + [87] = "2560x1080p@25 64:27", + [88] = "2560x1080p@30 64:27", + [89] = "2560x1080p@50 64:27", + [90] = "2560x1080p@60 64:27", + [91] = "2560x1080p@100 64:27", + [92] = "2560x1080p@120 64:27", + [93] = "3840x2160p@24 16:9", + [94] = "3840x2160p@25 16:9", + [95] = "3840x2160p@30 16:9", + [96] = "3840x2160p@50 16:9", + [97] = "3840x2160p@60 16:9", + [98] = "4096x2160p@24 256:135", + [99] = "4096x2160p@25 256:135", + [100] = "4096x2160p@30 256:135", + [101] = "4096x2160p@50 256:135", + [102] = "4096x2160p@60 256:135", + [103] = "3840x2160p@24 64:27", + [104] = "3840x2160p@25 64:27", + [105] = "3840x2160p@30 64:27", + [106] = "3840x2160p@50 64:27", + [107] = "3840x2160p@60 64:27", +}; + +static void ext_cea_data(uns pos, uns max_pos) +{ + while (pos < max_pos) + { + byte *p = edid + pos; + uns type = p[0] >> 5; + uns len = (p[0] & 31) + 1; + if (pos + len > max_pos) + { + printf("!!! Truncated data block (pos=%u, type=%02x, len=%u)\n", pos, type, len); + break; + } + + switch (type) + { + case 1: // Audio + printf("Audio formats:\n"); + for (uns j=1; j < len; j += 3) + { + byte *q = p + j; + uns fmt = (q[0] >> 3) & 0x0f; + static const char * const fmt_names[16] = { + "RFU0", "LPCM", "AC-3", "MPEG1", + "MP3", "MPEG2", "AAC", "DTS", + "ATRAC", "SACD", "DD+", "DTS+HD", + "MLP", "DST", "WMApro", "RFU15", + }; + uns chans = (q[0] & 7) + 1; + printf("\t%s, Channels=%u\n", fmt_names[fmt], chans); + + uns rates = q[1]; + printf("\t\tSample rates [kHz]: 192%c 176%c 96%c 88%c 48%c 44%c 32%c\n", + B(rates & 0x40), B(rates & 0x20), B(rates & 0x10), + B(rates & 0x08), B(rates & 0x04), B(rates & 0x02), B(rates & 0x01)); + + uns br = q[2]; + if (fmt == 1) + printf("\t\tBit depths: 24%c 20%c 16%c\n", B(br & 4), B(br & 2), B(br & 1)); + else + printf("\t\tMax bit rate [kHz]: %u\n", br*8); + } + break; + case 2: // Video + printf("Video formats:\n"); + for (uns j=1; j < len; j++) + { + uns vf = p[j] & 0x7f; + if (vf && vf < sizeof(cea_modes) / sizeof(cea_modes[0])) + printf("\t%s", cea_modes[vf]); + else + printf("\t#%02x", vf); + if (p[j] & 0x80) + printf(" (native)"); + printf("\n"); + } + break; + case 3: // Vendor specific + { + printf("Vendor-specific: "); + if (len < 4) + { + printf("\t!!! Truncated\n"); + break; + } + uns oid = p[1] | (p[2] << 8) | (p[3] << 16); + if (oid == 0x000c03) + { + printf("HDMI\n"); + ext_hdmi(p, len); + } + else + printf("Vendor %06x\n", oid); + break; + } + case 4: // Speaker allocation + printf("Speakers present:\n"); + if (len < 4) + { + printf("\t!!! Truncated\n"); + break; + } + static const char * const spk_types[8] = { + "Front Left+Right", "LFE", "Front Center", "Rear Left+Right", + "Rear Center", "Front Left+Right Center", "Rear Left+Right Center", "RFU7", + }; + for (uns j=0; j<8; j++) + if (p[1] & (1 << j)) + printf("\t%s\n", spk_types[j]); + break; + default: + printf("Unknown data block #%02x (pos=%u, len=%u)\n", type, pos, len); + } + + pos += len; + } +} + +static void ext_cea_dtd(uns pos, uns max_pos) +{ + // Detailed Timing Descriptors follow the same syntax as in basic EDID + while (pos + 18 <= max_pos && edid[pos] && edid[pos+1]) + { + show_detailed_timing(edid + pos); + pos += 18; + } +} + +static void ext_cea_861(void) +{ + uns version = edid[1]; + uns dtd_pos = edid[2]; + uns flags = edid[3]; + + if (version >= 2) + printf("Flags: Underscan%c BasicAudio%c YCbCr444%c YCbCr422%c\n", + B(flags & 0x80), + B(flags & 0x40), + B(flags & 0x20), + B(flags & 0x10)); + + ext_cea_data(4, (dtd_pos < 127 ? dtd_pos : 127)); + ext_cea_dtd(dtd_pos, 127); +} + struct extension { const char *name; void (*parser)(void); }; static const struct extension ext_table[0x100] = { - [0x02] = { "CEA 861", NULL }, + [0x02] = { "CEA 861", ext_cea_861 }, [0x10] = { "Video Timing Block", NULL }, [0x40] = { "Display Information", NULL }, [0x50] = { "Localized Strings", NULL }, @@ -464,6 +761,8 @@ static void show_ext(void) printf("!!! Not decoded yet\n"); } +/*** Main loop ***/ + static void read_binary(void) { int c = read(0, edid, 128);