]> mj.ucw.cz Git - edid.git/commitdiff
Decoding CEA 861 extension including HDMI
authorMartin Mares <mj@ucw.cz>
Sat, 7 Nov 2015 22:19:06 +0000 (23:19 +0100)
committerMartin Mares <mj@ucw.cz>
Sat, 7 Nov 2015 22:19:06 +0000 (23:19 +0100)
edid.c

diff --git a/edid.c b/edid.c
index d781536dadce8121442752195b772d3366d01149..bf9c04de9d0ccc480a8c58ac313519a1ceb0dc65 100644 (file)
--- 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);