| // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| #include "edid_utils.h" |
| |
| /* Dump out an EDID block in a simple format */ |
| void show_edid_data(FILE* outfile, unsigned char* edid_data, |
| int items, int base) |
| { |
| int item = 0; |
| |
| while (item < items) { |
| int i; |
| fprintf(outfile, " 0x%04x: ", item + base); |
| for(i=0; i < 16; i++) { |
| fprintf(outfile, "%02x ", edid_data[item++]); |
| if (item >= items) break; |
| } |
| fprintf(outfile, "\n"); |
| } |
| } |
| |
| |
| unsigned char test_edid1[256] = { |
| 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00, |
| 0x06,0xaf,0x5c,0x20,0x00,0x00,0x00,0x00, |
| 0x01,0x12,0x01,0x03,0x80,0x1a,0x0e,0x78, |
| 0x0a,0x99,0x85,0x95,0x55,0x56,0x92,0x28, |
| 0x22,0x50,0x54,0x00,0x00,0x00,0x01,0x01, |
| 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, |
| 0x01,0x01,0x01,0x01,0x01,0x01,0x96,0x19, |
| 0x56,0x28,0x50,0x00,0x08,0x30,0x18,0x10, |
| 0x24,0x00,0x00,0x90,0x10,0x00,0x00,0x18, |
| 0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x20,0x00,0x00,0x00,0xfe,0x00,0x41, |
| 0x55,0x4f,0x0a,0x20,0x20,0x20,0x20,0x20, |
| 0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xfe, |
| 0x00,0x42,0x31,0x31,0x36,0x58,0x57,0x30, |
| 0x32,0x20,0x56,0x30,0x20,0x0a,0x00,0xf8 |
| }; |
| |
| unsigned char test_edid2[256] = { |
| 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00, |
| 0x30,0xe4,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x14,0x01,0x03,0x80,0x1a,0x0e,0x78, |
| 0x0a,0xbf,0x45,0x95,0x58,0x52,0x8a,0x28, |
| 0x25,0x50,0x54,0x00,0x00,0x00,0x01,0x01, |
| 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, |
| 0x01,0x01,0x01,0x01,0x01,0x01,0x84,0x1c, |
| 0x56,0xa8,0x50,0x00,0x19,0x30,0x30,0x20, |
| 0x35,0x00,0x00,0x90,0x10,0x00,0x00,0x1b, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x4c, |
| 0x47,0x20,0x44,0x69,0x73,0x70,0x6c,0x61, |
| 0x79,0x0a,0x20,0x20,0x00,0x00,0x00,0xfc, |
| 0x00,0x4c,0x50,0x31,0x31,0x36,0x57,0x48, |
| 0x31,0x2d,0x54,0x4c,0x4e,0x31,0x00,0x4e |
| }; |
| |
| unsigned char test_edid3[256] = { |
| 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00, |
| 0x4d,0xd9,0x02,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x11,0x01,0x03,0x80,0x00,0x00,0x78, |
| 0x0a,0x0d,0xc9,0xa0,0x57,0x47,0x98,0x27, |
| 0x12,0x48,0x4c,0x00,0x00,0x00,0x01,0x01, |
| 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, |
| 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1d, |
| 0x80,0xd0,0x72,0x1c,0x16,0x20,0x10,0x2c, |
| 0x25,0x80,0xc4,0x8e,0x21,0x00,0x00,0x9e, |
| 0x01,0x1d,0x80,0x18,0x71,0x1c,0x16,0x20, |
| 0x58,0x2c,0x25,0x00,0xc4,0x8e,0x21,0x00, |
| 0x00,0x9e,0x00,0x00,0x00,0xfc,0x00,0x48, |
| 0x44,0x4d,0x49,0x20,0x4c,0x4c,0x43,0x0a, |
| 0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xfd, |
| 0x00,0x3b,0x3d,0x0f,0x2d,0x08,0x00,0x0a, |
| 0x20,0x20,0x20,0x20,0x20,0x20,0x01,0xc0, |
| 0x02,0x03,0x1e,0x47,0x4f,0x94,0x13,0x05, |
| 0x03,0x04,0x02,0x01,0x16,0x15,0x07,0x06, |
| 0x11,0x10,0x12,0x1f,0x23,0x09,0x07,0x01, |
| 0x65,0x03,0x0c,0x00,0x10,0x00,0x8c,0x0a, |
| 0xd0,0x90,0x20,0x40,0x31,0x20,0x0c,0x40, |
| 0x55,0x00,0x13,0x8e,0x21,0x00,0x00,0x18, |
| 0x01,0x1d,0x00,0xbc,0x52,0xd0,0x1e,0x20, |
| 0xb8,0x28,0x55,0x40,0xc4,0x8e,0x21,0x00, |
| 0x00,0x1e,0x8c,0x0a,0xd0,0x8a,0x20,0xe0, |
| 0x2d,0x10,0x10,0x3e,0x96,0x00,0xc4,0x8e, |
| 0x21,0x00,0x00,0x18,0x01,0x1d,0x00,0x72, |
| 0x51,0xd0,0x1e,0x20,0x6e,0x28,0x55,0x00, |
| 0xc4,0x8e,0x21,0x00,0x00,0x1e,0x8c,0x0a, |
| 0xd0,0x8a,0x20,0xe0,0x2d,0x10,0x10,0x3e, |
| 0x96,0x00,0x13,0x8e,0x21,0x00,0x00,0x18, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfb |
| }; |
| |
| unsigned char test_edid4[256] = { |
| 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00, |
| 0x4c,0x2d,0x10,0x02,0x00,0x00,0x00,0x00, |
| 0x31,0x0f,0x01,0x03,0x80,0x10,0x09,0x8c, |
| 0x0a,0xe2,0xbd,0xa1,0x5b,0x4a,0x98,0x24, |
| 0x15,0x47,0x4a,0x20,0x00,0x00,0x01,0x01, |
| 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, |
| 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1d, |
| 0x00,0x72,0x51,0xd0,0x1e,0x20,0x6e,0x28, |
| 0x55,0x00,0xa0,0x5a,0x00,0x00,0x00,0x1e, |
| 0x01,0x1d,0x80,0x18,0x71,0x1c,0x16,0x20, |
| 0x58,0x2c,0x25,0x00,0xa0,0x5a,0x00,0x00, |
| 0x00,0x9e,0x00,0x00,0x00,0xfd,0x00,0x3b, |
| 0x3d,0x1e,0x2e,0x08,0x00,0x0a,0x20,0x20, |
| 0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xfc, |
| 0x00,0x53,0x41,0x4d,0x53,0x55,0x4e,0x47, |
| 0x0a,0x20,0x20,0x20,0x20,0x20,0x01,0x8d, |
| 0x02,0x03,0x16,0x71,0x43,0x84,0x05,0x03, |
| 0x23,0x09,0x07,0x07,0x83,0x01,0x00,0x00, |
| 0x65,0x03,0x0c,0x00,0x20,0x00,0x8c,0x0a, |
| 0xd0,0x8a,0x20,0xe0,0x2d,0x10,0x10,0x3e, |
| 0x96,0x00,0xa0,0x5a,0x00,0x00,0x00,0x18, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30 |
| }; |
| |
| unsigned char test_edid5[256] = { |
| 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00, |
| 0x3d,0xcb,0x61,0x07,0x00,0x00,0x00,0x00, |
| 0x00,0x11,0x01,0x03,0x80,0x00,0x00,0x78, |
| 0x0a,0x0d,0xc9,0xa0,0x57,0x47,0x98,0x27, |
| 0x12,0x48,0x4c,0x00,0x00,0x00,0x01,0x01, |
| 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, |
| 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1d, |
| 0x80,0x18,0x71,0x1c,0x16,0x20,0x58,0x2c, |
| 0x25,0x00,0xc4,0x8e,0x21,0x00,0x00,0x9e, |
| 0x01,0x1d,0x80,0xd0,0x72,0x1c,0x16,0x20, |
| 0x10,0x2c,0x25,0x80,0xc4,0x8e,0x21,0x00, |
| 0x00,0x9e,0x00,0x00,0x00,0xfc,0x00,0x54, |
| 0x58,0x2d,0x53,0x52,0x36,0x30,0x35,0x0a, |
| 0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xfd, |
| 0x00,0x17,0xf0,0x0f,0x7e,0x11,0x00,0x0a, |
| 0x20,0x20,0x20,0x20,0x20,0x20,0x01,0x93, |
| 0x02,0x03,0x3b,0x72,0x55,0x85,0x04,0x03, |
| 0x02,0x0e,0x0f,0x07,0x23,0x24,0x10,0x94, |
| 0x13,0x12,0x11,0x1d,0x1e,0x16,0x25,0x26, |
| 0x01,0x1f,0x35,0x09,0x7f,0x07,0x0f,0x7f, |
| 0x07,0x17,0x07,0x50,0x3f,0x06,0xc0,0x57, |
| 0x06,0x00,0x5f,0x7e,0x01,0x67,0x5e,0x00, |
| 0x83,0x4f,0x00,0x00,0x66,0x03,0x0c,0x00, |
| 0x20,0x00,0x80,0x8c,0x0a,0xd0,0x8a,0x20, |
| 0xe0,0x2d,0x10,0x10,0x3e,0x96,0x00,0xc4, |
| 0x8e,0x21,0x00,0x00,0x18,0x8c,0x0a,0xd0, |
| 0x90,0x20,0x40,0x31,0x20,0x0c,0x40,0x55, |
| 0x00,0xc4,0x8e,0x21,0x00,0x00,0x18,0x01, |
| 0x1d,0x00,0x72,0x51,0xd0,0x1e,0x20,0x6e, |
| 0x28,0x55,0x00,0xc4,0x8e,0x21,0x00,0x00, |
| 0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd |
| }; |
| |
| /* Has DTD that is too wide */ |
| unsigned char test_edid6[256] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, |
| 0x10, 0xac, 0x63, 0x40, 0x4c, 0x35, 0x31, 0x33, |
| 0x0c, 0x15, 0x01, 0x03, 0x80, 0x40, 0x28, 0x78, |
| 0xea, 0x8d, 0x85, 0xad, 0x4f, 0x35, 0xb1, 0x25, |
| 0x0e, 0x50, 0x54, 0xa5, 0x4b, 0x00, 0x71, 0x4f, |
| 0x81, 0x00, 0x81, 0x80, 0xa9, 0x40, 0xd1, 0x00, |
| 0xd1, 0x40, 0x01, 0x01, 0x01, 0x01, 0xe2, 0x68, |
| 0x00, 0xa0, 0xa0, 0x40, 0x2e, 0x60, 0x30, 0x20, |
| 0x36, 0x00, 0x81, 0x91, 0x21, 0x00, 0x00, 0x1a, |
| 0x00, 0x00, 0x00, 0xff, 0x00, 0x50, 0x48, 0x35, |
| 0x4e, 0x59, 0x31, 0x33, 0x4d, 0x33, 0x31, 0x35, |
| 0x4c, 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44, |
| 0x45, 0x4c, 0x4c, 0x20, 0x55, 0x33, 0x30, 0x31, |
| 0x31, 0x0a, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, |
| 0x00, 0x31, 0x56, 0x1d, 0x71, 0x1c, 0x00, 0x0a, |
| 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0xb0 |
| }; |
| |
| static unsigned char *test_edids[N_TEST_EDIDS] = { |
| test_edid1, test_edid2, test_edid3, test_edid4, test_edid5, |
| test_edid6 |
| }; |
| |
| int get_test_edid(int n, unsigned char *dst) |
| { |
| if ((n < 1) || (n > N_TEST_EDIDS)) return -1; |
| memcpy(dst, test_edids[n-1], 256); |
| return 0; |
| } |
| |
| int show_test_edid(FILE *outfile, int n) |
| { |
| if ((n < 1) || (n > N_TEST_EDIDS)) return -1; |
| fprintf(outfile, "Test EDID %d\n", n); |
| show_edid(outfile, test_edids[n-1], 1); |
| return 0; |
| } |
| |
| /* Print an edid descriptor block (standard case is at 54 + 18*i) */ |
| void show_edid_dtd(FILE* outfile, unsigned char* base) |
| { |
| int pelclk = base[DTD_PCLK_LO] + (base[DTD_PCLK_HI]<<8); |
| |
| if (pelclk != 0) { |
| int hres = base[DTD_HA_LO] + ((base[DTD_HABL_HI] & 0xf0)<<4); |
| int hbl = base[DTD_HBL_LO] + ((base[DTD_HABL_HI] & 0x0f)<<8); |
| int vres = base[DTD_VA_LO] + ((base[DTD_VABL_HI] & 0xf0)<<4); |
| int vbl = base[DTD_VBL_LO] + ((base[DTD_VABL_HI] & 0x0f)<<8); |
| int hso = base[DTD_HSO_LO] + ((base[DTD_HVSX_HI] & 0xc0)<<2); |
| int hsw = base[DTD_HSW_LO] + ((base[DTD_HVSX_HI] & 0x30)<<4); |
| int vso = (base[DTD_VSX_LO] >> 4) + ((base[DTD_HVSX_HI] & 0x0c)<<2); |
| int vsw = (base[DTD_VSX_LO] & 0xf) + ((base[DTD_HVSX_HI] & 0x03)<<4); |
| int hsiz = base[DTD_HSIZE_LO] + ((base[DTD_HVSIZE_HI] & 0xf0)<<4); |
| int vsiz = base[DTD_VSIZE_LO] + ((base[DTD_HVSIZE_HI] & 0x0f)<<8); |
| int hbdr = base[DTD_HBORDER]; |
| int vbdr = base[DTD_VBORDER]; |
| int mdflg = base[DTD_FLAGS]; |
| |
| int refr = (pelclk * 10000)/((hres+hbl)*(vres+vbl)); |
| int refm = (pelclk * 10000)%((hres+hbl)*(vres+vbl)); |
| int refd = (refm*100)/((hres+hbl)*(vres+vbl)); |
| |
| fprintf(outfile, "%dx%d%c@%d.%02d, dot clock %d %cHsync %cVsync\n", |
| hres, vres, (mdflg & 0x80) ? 'i':'p', |
| refr, refd, |
| pelclk * 10000, |
| (mdflg & 0x2) ? '+':'-', |
| (mdflg & 0x4) ? '+':'-'); |
| fprintf(outfile, "H: start %d, end %d, total %d\n", |
| hres+hso, hres+hso+hsw, hres+hbl); |
| fprintf(outfile, "V: start %d, end %d, total %d\n", |
| vres+vso, vres+vso+vsw, vres+vbl); |
| fprintf(outfile, "Size %dx%dmm, Border %dx%d pixels\n", |
| hsiz, vsiz, hbdr, vbdr); |
| } else { |
| char monstr[DTD_SIZE]; |
| int stp; |
| switch (base[DTD_TYPETAG]) { |
| case DTDTYPE_SERIAL: |
| case DTDTYPE_STRING: |
| case DTDTYPE_NAME: |
| strncpy(monstr, (char *)base+DTD_STRING, 13); |
| for(stp = 0; stp < 13; stp++) if (monstr[stp] == 0x0a) monstr[stp] = 0; |
| monstr[stp] = 0; |
| if (base[3] != DTDTYPE_STRING) |
| fprintf(outfile, "%s: %s\n", |
| (base[3] == DTDTYPE_NAME) ? "Name" : "Serial", monstr); |
| else |
| fprintf(outfile, "%s\n", monstr); |
| break; |
| |
| case DTDTYPE_LIMITS: |
| fprintf(outfile, "Range V %d - %d Hz, H %d - %d kHz, Pel <= %d MHz\n", |
| base[DTD_MINV_HZ], base[DTD_MAXV_HZ], |
| base[DTD_MINH_kHZ], base[DTD_MAXH_kHZ], |
| base[DTD_MAXCLK_100kHZ]*10); |
| break; |
| |
| default: |
| fprintf(outfile, "Undecoded descriptor block type 0x%x\n", |
| base[DTD_TYPETAG]); |
| break; |
| } |
| } |
| } |
| |
| |
| char *sad_audio_type[16] = { |
| "Reserved", "LPCM", "AC-3", "MPEG1 (Layer 1 and 2)", |
| "MP3", "MPEG2", "AAC", "DTS", |
| "ATRAC", "SACD", "DD+", "DTS-HD", |
| "MLP/Dolby TrueHD", "DST Audio", "WMA Pro", "Reserved"}; |
| |
| char *uscanstr[4] = { |
| "not supported", |
| "always overscan", |
| "always underscan", |
| "supports both over- and underscan"}; |
| |
| void show_cea_timing(FILE* outfile, unsigned char* edid_ext) |
| { |
| int i, dbc; |
| int off_dtd = edid_ext[CEA_DTD_OFFSET]; |
| int n_dtd; |
| fprintf(outfile, "Found CEA EDID Timing Extension rev 3\n"); |
| |
| if (off_dtd < CEA_DBC_START) { |
| fprintf(outfile, "Block is empty (off_dtd = %d)\n", off_dtd); |
| return; |
| } |
| /* Ends with 0 and a checksum, have at least one pad byte */ |
| n_dtd = (CEA_LAST_PAD - off_dtd)/DTD_SIZE; |
| fprintf(outfile, |
| "Block has DTDs starting at offset %d (thus %d bytes of DBCs)\n", |
| off_dtd, off_dtd - CEA_DBC_START); |
| fprintf(outfile, "There is space for max %d DTDs in the extension\n", n_dtd); |
| fprintf(outfile, |
| "There are %d native DTDs (between regular and extensions)\n", |
| edid_ext[CEA_NATIVE_DTDS] & 0xf); |
| fprintf(outfile, "IT formats %sdefault to underscan\n", |
| (edid_ext[CEA_SUPPORT] & 0x80) ? "" : "do not "); |
| fprintf(outfile, |
| "Support: %sbasic audio, %sYCrCb 4:4:4, %sYCrCb 4:2:2\n", |
| (edid_ext[CEA_SUPPORT] & 0x40) ? "" : "no ", |
| (edid_ext[CEA_SUPPORT] & 0x20) ? "" : "no ", |
| (edid_ext[CEA_SUPPORT] & 0x10) ? "" : "no "); |
| |
| /* Between offset 4 and off_dtd is the Data Block Collection */ |
| /* There may be none, in which case off_dtd == 4 */ |
| dbc = CEA_DBC_START; |
| while (dbc < off_dtd) { |
| int db_len = edid_ext[dbc+DBC_TAG_LENGTH] & DBC_LEN_MASK; |
| int dbp = dbc + 1; |
| |
| switch (edid_ext[dbc+DBC_TAG_LENGTH] >> DBC_TAG_SHIFT) { |
| |
| case DBC_TAG_AUDIO: |
| /* Audio Data Block */ |
| while (dbp < (dbc + db_len + 1)) { |
| int atype =(edid_ext[dbp + DBCA_FORMAT]>>3) & 0xf; |
| fprintf(outfile, "Audio: %d channel %s: ", |
| edid_ext[dbp + DBCA_FORMAT] & 0x7, |
| sad_audio_type[atype]); |
| if (edid_ext[dbp + DBCA_RATE] & 0x40) fprintf(outfile, "192k "); |
| if (edid_ext[dbp + DBCA_RATE] & 0x20) fprintf(outfile, "176k "); |
| if (edid_ext[dbp + DBCA_RATE] & 0x10) fprintf(outfile, "96k "); |
| if (edid_ext[dbp + DBCA_RATE] & 0x08) fprintf(outfile, "88k "); |
| if (edid_ext[dbp + DBCA_RATE] & 0x04) fprintf(outfile, "48k "); |
| if (edid_ext[dbp + DBCA_RATE] & 0x02) fprintf(outfile, "44k "); |
| if (edid_ext[dbp + DBCA_RATE] & 0x01) fprintf(outfile, "32k "); |
| |
| if (atype == 1) |
| fprintf(outfile, "%s%s%s\n", |
| (edid_ext[dbp + DBCA_INFO] & 0x4) ? "24-bit ":"", |
| (edid_ext[dbp + DBCA_INFO] & 0x2) ? "20-bit ":"", |
| (edid_ext[dbp + DBCA_INFO] & 0x1) ? "16-bit":""); |
| else if ((atype >= 2) && (atype <= 8)) |
| fprintf(outfile, "Max %dkHz\n", edid_ext[dbp + DBCA_INFO] * 8); |
| else |
| fprintf(outfile, "Codec vendor flags 0x%02x\n", |
| edid_ext[dbp + DBCA_INFO]); |
| dbp += DBCA_SIZE; |
| } |
| break; |
| |
| case DBC_TAG_VIDEO: |
| /* Vidio Data Block */ |
| while (dbp < (dbc + db_len + 1)) { |
| int vtype = edid_ext[dbp + DBCV_CODE] & 0x7f; |
| fprintf(outfile, "Video: Code %d %s\n", vtype, |
| (edid_ext[dbp + DBCV_CODE] & 0x80) ? "(native)":""); |
| dbp += DBCV_SIZE; |
| } |
| break; |
| |
| case DBC_TAG_VENDOR: |
| /* Vendor Data Block */ |
| if ((edid_ext[dbp+DBCVND_IEEE_LO] == 0x03) && |
| (edid_ext[dbp+DBCVND_IEEE_MID] == 0x0C) && |
| (edid_ext[dbp+DBCVND_IEEE_HI] == 0x00)) { |
| fprintf(outfile, |
| "HDMI Vendor block (CEC @0x%04x):\nSupport: %s%s%s%s%s%s\n", |
| edid_ext[dbp+DBCVHDMI_CEC_LO] + |
| (edid_ext[dbp+DBCVHDMI_CEC_HI]<<8), |
| (edid_ext[dbp+DBCVHDMI_SUPPORT] & 0x80) ? "AI ":"", |
| (edid_ext[dbp+DBCVHDMI_SUPPORT] & 0x40) ? "DC_48bit ":"", |
| (edid_ext[dbp+DBCVHDMI_SUPPORT] & 0x20) ? "DC_36bit ":"", |
| (edid_ext[dbp+DBCVHDMI_SUPPORT] & 0x10) ? "DC_30bit ":"", |
| (edid_ext[dbp+DBCVHDMI_SUPPORT] & 0x08) ? "DC_Y444 ":"", |
| (edid_ext[dbp+DBCVHDMI_SUPPORT] & 0x01) ? "DVI_Dual":""); |
| if (edid_ext[dbp+DBCVHDMI_MAXTMDS_5MHz] > 0) |
| fprintf(outfile, "Max TMDS Frequency %dMHz\n", |
| edid_ext[dbp+DBCVHDMI_MAXTMDS_5MHz]*5); |
| if (edid_ext[dbp+DBCVHDMI_LATFLAGS] & 0x80) |
| fprintf(outfile, "Video latency %dms, audio latency %dms\n", |
| 2 * (edid_ext[dbp+DBCVHDMI_VLAT] - 1), |
| 2 * (edid_ext[dbp+DBCVHDMI_ALAT] - 1)); |
| if (edid_ext[dbp+7] & 0x40) |
| fprintf(outfile, |
| "Interlaced Video latency %dms, audio latency %dms\n", |
| 2 * (edid_ext[dbp+DBCVHDMI_IVLAT] - 1), |
| 2 * (edid_ext[dbp+DBCVHDMI_IALAT] - 1)); |
| } |
| else |
| fprintf(outfile, "Vendor block for %02x-%02x-%02x", |
| edid_ext[dbp+DBCVND_IEEE_LO], edid_ext[dbp+DBCVND_IEEE_MID], |
| edid_ext[dbp+DBCVND_IEEE_HI]); |
| break; |
| |
| case DBC_TAG_SPEAKER: |
| /* Speaker allocation Block */ |
| fprintf(outfile, "Speakers: %s%s%s%s%s%s%s\n", |
| (edid_ext[dbp+DBCSP_ALLOC] & 0x40) ? "RearCenter L/R ":"", |
| (edid_ext[dbp+DBCSP_ALLOC] & 0x20) ? "FrontCenter L/R ":"", |
| (edid_ext[dbp+DBCSP_ALLOC] & 0x10) ? "Rear Center":"", |
| (edid_ext[dbp+DBCSP_ALLOC] & 0x08) ? "Rear L/R ":"", |
| (edid_ext[dbp+DBCSP_ALLOC] & 0x04) ? "Front Center ":"", |
| (edid_ext[dbp+DBCSP_ALLOC] & 0x02) ? "LFE ":"", |
| (edid_ext[dbp+DBCSP_ALLOC] & 0x01) ? "Front L/R ":""); |
| break; |
| |
| case DBC_TAG_EXTENDED: |
| switch (edid_ext[dbp + DBC_ETAG]) { |
| case DBC_ETAG_VCDB: |
| fprintf(outfile, "Video Capabilities:\n"); |
| fprintf(outfile, " Quantization range selectable: %s\n", |
| (edid_ext[dbp + VCDB_FLAGS] & 0x40) ? "unknown":"via AVI Q"); |
| /* PT field zero implies no data, just use IT and CE fields */ |
| if (VCDB_S_PT(edid_ext[dbp + VCDB_FLAGS])) |
| fprintf(outfile, " Preferred mode %s\n", |
| uscanstr[VCDB_S_PT(edid_ext[dbp + VCDB_FLAGS])]); |
| fprintf(outfile, " IT modes %s\n", |
| uscanstr[VCDB_S_IT(edid_ext[dbp + VCDB_FLAGS])]); |
| fprintf(outfile, " CE modes %s\n", |
| uscanstr[VCDB_S_CE(edid_ext[dbp + VCDB_FLAGS])]); |
| break; |
| |
| case DBC_ETAG_COL: |
| fprintf(outfile, "Colorimetry supports %s%s metadata 0x%x\n", |
| (edid_ext[dbp+COL_FLAGS] & 0x02) ? "HD (YCC709) ":"", |
| (edid_ext[dbp+COL_FLAGS] & 0x01) ? "SD (YCC601) ":"", |
| (edid_ext[dbp+COL_META] & 0x07)); |
| break; |
| |
| default: |
| fprintf(outfile, |
| "Unknown Data block with extended tag 0x%x, length 0x%x\n", |
| edid_ext[dbc + DBC_ETAG], db_len); |
| } |
| |
| default: |
| fprintf(outfile, "Unknown Data Block with type tag 0x%x, length 0x%x\n", |
| edid_ext[dbc+DBC_TAG_LENGTH] >> DBC_TAG_SHIFT, db_len); |
| break; |
| } |
| dbc += db_len + 1; |
| } |
| for(i=0; i < n_dtd; i++) { |
| /* Find 0,0 when we hit padding */ |
| if ((edid_ext[off_dtd + DTD_SIZE*i + DTD_PCLK_LO] == 0) && |
| (edid_ext[off_dtd + DTD_SIZE*i + DTD_PCLK_HI] == 0)) { |
| fprintf(outfile, "End of DTD padding reached after %d DTDs\n", i); |
| break; |
| } |
| show_edid_dtd(outfile, edid_ext + (off_dtd + DTD_SIZE*i)); |
| } |
| } |
| |
| |
| int edid_valid(unsigned char *edid_data) |
| { |
| return ((edid_data[EDID_HDR+0]==0x00) && (edid_data[EDID_HDR+1]==0xff) && |
| (edid_data[EDID_HDR+2]==0xff) && (edid_data[EDID_HDR+3]==0xff) && |
| (edid_data[EDID_HDR+4]==0xff) && (edid_data[EDID_HDR+5]==0xff) && |
| (edid_data[EDID_HDR+6]==0xff) && (edid_data[EDID_HDR+7]==0x00)); |
| } |
| |
| int edid_lpcm_support(unsigned char *edid_data, int ext) |
| { |
| unsigned char* edid_ext = edid_data + EDID_SIZE; |
| int dbc; |
| int off_dtd = edid_ext[CEA_DTD_OFFSET]; |
| |
| /* No if no extension, which can happen for two reasons */ |
| /* a) ext < 1 indicates no data was read into the extension area */ |
| /* b) edid_data[126] < 1 indicates EDID does not use extension area */ |
| if ((ext < 1) || (edid_data[EDID_EXT_FLAG] < 1)) |
| return 0; |
| |
| /* No if extension is not CEA rev 3 */ |
| if (!((edid_ext[EEXT_TAG] == 0x02) && (edid_ext[EEXT_REV] == 0x03))) |
| return 0; |
| |
| /* If DBC block is not empty look for audio info */ |
| if (off_dtd > CEA_DBC_START) { |
| |
| /* Between offset 4 and off_dtd is the Data Block Collection */ |
| /* There may be none, in which case off_dtd == 4 */ |
| dbc = CEA_DBC_START; |
| while (dbc < off_dtd) { |
| int db_len = edid_ext[dbc + DBC_TAG_LENGTH] & DBC_LEN_MASK; |
| int dbp = dbc + 1; |
| |
| if (((edid_ext[dbc+DBC_TAG_LENGTH] >> DBC_TAG_SHIFT) == DBC_TAG_AUDIO) && |
| (((edid_ext[dbp + DBCA_FORMAT]>>3) & 0xF) == DBCA_FMT_LPCM)) { |
| /* Audio Data Block, type LPCM, return bitmap of frequencies */ |
| return edid_ext[dbp+DBCA_RATE]; |
| } |
| dbc += db_len + 1; |
| } |
| /* Get here if failed to find LPCM info in DBC block */ |
| } |
| /* Last chance is to look for Basic Audio return bitmap for 32, 44.1, 48 */ |
| if (edid_ext[CEA_SUPPORT] & 0x40) |
| return 0x7; |
| |
| return 0; |
| } |
| |
| |
| int edid_has_hdmi_info(unsigned char *edid_data, int ext) |
| { |
| unsigned char* edid_ext = edid_data + EDID_SIZE; |
| int dbc; |
| int off_dtd = edid_ext[CEA_DTD_OFFSET]; |
| |
| /* No if no extension, which can happen for two reasons */ |
| /* a) ext < 1 indicates no data was read into the extension area */ |
| /* b) edid_data[126] < 1 indicates EDID does not use extension area */ |
| if ((ext < 1) || (edid_data[EDID_EXT_FLAG] < 1)) |
| return 0; |
| |
| /* No if extension is not CEA rev 3 */ |
| if (!((edid_ext[EEXT_TAG] == 0x02) && (edid_ext[EEXT_REV] == 0x03))) |
| return 0; |
| |
| /* No if block is empty */ |
| if (off_dtd < CEA_DBC_START) |
| return 0; |
| |
| /* Between offset 4 and off_dtd is the Data Block Collection */ |
| /* There may be none, in which case off_dtd == 4 */ |
| dbc = CEA_DBC_START; |
| while (dbc < off_dtd) { |
| int db_len = edid_ext[dbc + DBC_TAG_LENGTH] & DBC_LEN_MASK; |
| int dbp = dbc + 1; |
| |
| if ((edid_ext[dbc + DBC_TAG_LENGTH] >> DBC_TAG_SHIFT) == DBC_TAG_VENDOR) { |
| /* Vendor Data Block */ |
| if ((edid_ext[dbp+DBCVND_IEEE_LO] == 0x03) && |
| (edid_ext[dbp+DBCVND_IEEE_MID] == 0x0C) && |
| (edid_ext[dbp+DBCVND_IEEE_HI] == 0x00)) |
| return 1; |
| } |
| dbc += db_len + 1; |
| } |
| return 0; |
| } |
| |
| /* Print out an EDID */ |
| void show_edid(FILE *outfile, unsigned char *edid_data, int ext) |
| { |
| int i; |
| int edidver = edid_data[EDID_VERSION]; |
| int edidrev = edid_data[EDID_REVISION]; |
| unsigned char* edid_ext; |
| |
| if (!edid_valid(edid_data)) { |
| fprintf(outfile, "Block does not contain EDID header\n"); |
| return; |
| } |
| /* unsigned edid_data so the right shifts pull in zeros */ |
| fprintf(outfile, "Manufacturer ID %c%c%c, product ID 0x%x\n", |
| '@' + (edid_data[EDID_MFG_EID]>>2), |
| '@' + (((edid_data[EDID_MFG_EID] & 3)<<3) + |
| (edid_data[EDID_MFG_EID+1]>>5)), |
| '@' + (edid_data[EDID_MFG_EID+1] & 0x1f), |
| edid_data[EDID_MFG_PROD_LO] + (edid_data[EDID_MFG_PROD_HI]<<8)); |
| fprintf(outfile, "Manufactured wk %d of %d. Edid version %d.%d\n", |
| edid_data[EDID_MFG_WEEK], 1990+edid_data[EDID_MFG_YEAR], |
| edidver, edidrev); |
| fprintf(outfile, |
| "Input: %s, vid level %d, %s, %s %s %s %s sync, %dx%dcm, Gamma %f\n", |
| (edid_data[EDID_VIDEO_IN] & 0x80) ? "digital" : "analog", |
| (edid_data[EDID_VIDEO_IN]>>5) & 3, |
| (edid_data[EDID_VIDEO_IN] * 0x10) ? "Blank to black" : "", |
| (edid_data[EDID_VIDEO_IN] * 0x08) ? "Separate" : "", |
| (edid_data[EDID_VIDEO_IN] * 0x04) ? "Composite" : "", |
| (edid_data[EDID_VIDEO_IN] * 0x02) ? "On-green" : "", |
| (edid_data[EDID_VIDEO_IN] * 0x01) ? "Serration V" : "", |
| edid_data[EDID_MAX_HSIZE], edid_data[EDID_MAX_VSIZE], |
| 1.0+((float)edid_data[EDID_GAMMA]/100.0)); |
| fprintf(outfile, "Features: %s %s %s %s %s %s %s\n", |
| (edid_data[EDID_FEATURES] & 0x80) ? "standby" : "", |
| (edid_data[EDID_FEATURES] & 0x40) ? "suspend" : "", |
| (edid_data[EDID_FEATURES] & 0x20) ? "active-off" : "", |
| (edid_data[EDID_FEATURES] & 0x18) ? "colour" : "monochrome", |
| (edid_data[EDID_FEATURES] & 0x04) ? "std-cspace" : "non-std-cspace", |
| (edid_data[EDID_FEATURES] & 0x02) ? "preferred-timing" : "", |
| (edid_data[EDID_FEATURES] & 0x01) ? "default-GTF" : ""); |
| |
| fprintf(outfile, "Established Timing:\n"); |
| if (edid_data[EDID_ESTTIME1] & 0x80) fprintf(outfile, "720x400@70\n"); |
| if (edid_data[EDID_ESTTIME1] & 0x40) fprintf(outfile, "720x400@88\n"); |
| if (edid_data[EDID_ESTTIME1] & 0x20) fprintf(outfile, "640x480@60\n"); |
| if (edid_data[EDID_ESTTIME1] & 0x10) fprintf(outfile, "640x480@67\n"); |
| if (edid_data[EDID_ESTTIME1] & 0x08) fprintf(outfile, "640x480@72\n"); |
| if (edid_data[EDID_ESTTIME1] & 0x04) fprintf(outfile, "640x480@75\n"); |
| if (edid_data[EDID_ESTTIME1] & 0x02) fprintf(outfile, "800x600@56\n"); |
| if (edid_data[EDID_ESTTIME1] & 0x01) fprintf(outfile, "800x600@60\n"); |
| if (edid_data[EDID_ESTTIME2] & 0x80) fprintf(outfile, "800x600@72\n"); |
| if (edid_data[EDID_ESTTIME2] & 0x40) fprintf(outfile, "800x600@75\n"); |
| if (edid_data[EDID_ESTTIME2] & 0x20) fprintf(outfile, "832x624@75\n"); |
| if (edid_data[EDID_ESTTIME2] & 0x10) fprintf(outfile, "1024x768i@87\n"); |
| if (edid_data[EDID_ESTTIME2] & 0x08) fprintf(outfile, "1024x768@60\n"); |
| if (edid_data[EDID_ESTTIME2] & 0x04) fprintf(outfile, "1024x768@70\n"); |
| if (edid_data[EDID_ESTTIME2] & 0x02) fprintf(outfile, "1024x768@75\n"); |
| if (edid_data[EDID_ESTTIME2] & 0x01) fprintf(outfile, "1280x1024@75\n"); |
| if (edid_data[EDID_MFGTIME] & 0x80) fprintf(outfile, "1152x870@75\n"); |
| |
| fprintf(outfile, "Standard timing:\n"); |
| for(i=0; i < EDID_N_STDTIME; i++) { |
| int hinfo = edid_data[EDID_STDTIMEH+2*i]; |
| int vinfo = edid_data[EDID_STDTIMEV+2*i]; |
| int hres, vres; |
| |
| /* 01 01 is pad by spec, but 00 00 and 20 20 are see in wild */ |
| if (((hinfo == 0x01) && (vinfo == 0x01)) || |
| ((hinfo == 0x00) && (vinfo == 0x00)) || |
| ((hinfo == 0x20) && (vinfo == 0x20))) |
| continue; |
| hres = (hinfo * 8) + 248; |
| switch (vinfo >> 6) { |
| case ASPECT_16_10: |
| vres = (hres * 10)/16; |
| break; |
| case ASPECT_4_3: |
| vres = (hres * 3)/4; |
| break; |
| case ASPECT_5_4: |
| vres = (hres * 4)/5; |
| break; |
| case ASPECT_16_9: |
| vres = (hres * 9)/16; |
| break; |
| /* Default will only be hit if compiler broken */ |
| default: |
| vres = 0; |
| } |
| fprintf(outfile, "%d: %dx%d@%d\n", i, hres, vres, 60 + (vinfo & 0x3f)); |
| } |
| |
| fprintf(outfile, "Descriptor blocks:\n"); |
| for(i = 0; i < EDID_N_DTDS; i++) { |
| show_edid_dtd(outfile, edid_data + (EDID_DTD_BASE + i * DTD_SIZE)); |
| } |
| fprintf(outfile, "EDID contains %d extensions\n", edid_data[EDID_EXT_FLAG]); |
| |
| edid_ext = edid_data + EDID_SIZE; |
| |
| if ((ext >= 1) && (edid_data[EDID_EXT_FLAG] >= 1)) { |
| if ((edid_ext[EEXT_TAG] == 0x02) && (edid_ext[EEXT_REV] == 0x03)) { |
| show_cea_timing(outfile, edid_ext); |
| } else { |
| char *tagtype; |
| switch(edid_ext[EEXT_TAG]) { |
| case 0x01: |
| tagtype = "LCD Timings"; |
| break; |
| case 0x02: |
| tagtype = "CEA"; |
| break; |
| case 0x20: |
| tagtype = "EDID 2.0"; |
| break; |
| case 0x30: |
| tagtype = "Color Information"; |
| break; |
| case 0x40: |
| tagtype = "DVI Feature"; |
| break; |
| case 0x50: |
| tagtype = "Touch Screen Map"; |
| break; |
| case 0xF0: |
| tagtype = "Block Map"; |
| break; |
| case 0xFF: |
| tagtype = "Manufacturer"; |
| break; |
| default: |
| tagtype = "Unknown"; |
| } |
| fprintf(outfile, "EDID %s extension tag 0x%02x rev 0x%02x skipped\n", |
| tagtype, edid_ext[EEXT_TAG], edid_ext[EEXT_REV]); |
| } |
| } |
| } |
| |
| |
| /* Pixel counts normally round to 8 */ |
| #define CLOSE_ENOUGH(a, b) (abs((a)-(b)) < 16) |
| |
| /* These match order of defines ASPECT_x_y in edid_utils.h */ |
| char *aspect_to_str[]={"16:10","4:3","5:4","16:9"}; |
| |
| int find_aspect(int h, int v) |
| { |
| if (CLOSE_ENOUGH((h * 3), (v * 4))) |
| return ASPECT_4_3; |
| if (CLOSE_ENOUGH((h * 4), (v * 5))) |
| return ASPECT_5_4; |
| if (CLOSE_ENOUGH((h * 9), (v * 16))) |
| return ASPECT_16_9; |
| if (CLOSE_ENOUGH((h * 10), (v * 16))) |
| return ASPECT_16_10; |
| |
| return -1; |
| } |
| |
| int find_aspect_fromisize(unsigned char *edid_data) |
| { |
| int hsiz = edid_data[EDID_MAX_HSIZE]; |
| int vsiz = edid_data[EDID_MAX_VSIZE]; |
| int res; |
| |
| /* Zero size for projector */ |
| /* Only use this code if there was no preferred resolution */ |
| /* So assume it is an older 4:3 projector not a video one */ |
| if ((hsiz == 0) && (vsiz == 0)) |
| return ASPECT_4_3; |
| |
| res = find_aspect(hsiz, vsiz); |
| |
| /* If things didn't work out, assume the old 4:3 case */ |
| if (res < 0) |
| return ASPECT_4_3; |
| else |
| return res; |
| } |