return x ? '+' : '-';
}
+/*** EDID Basics ***/
+
static void
show_model(byte *p)
{
printf("\n");
}
+/*** EDID Descriptors ***/
+
static void
show_detailed_timing(byte *p)
{
num_ext_blocks = edid[0x7e];
}
+/*** Extensions ***/
+
static void ext_block_map(void)
{
for (uns i=1; i<=0xfe; i++)
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 },
printf("!!! Not decoded yet\n");
}
+/*** Main loop ***/
+
static void read_binary(void)
{
int c = read(0, edid, 128);