Home | History | Annotate | Download | only in common
      1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <stdlib.h>
      8 
      9 #include "edid_utils.h"
     10 
     11 /* Dump out an EDID block in a simple format */
     12 void show_edid_data(FILE *outfile, unsigned char *edid_data,
     13 		    int items, int base)
     14 {
     15 	int item = 0;
     16 
     17 	while (item < items) {
     18 		int i;
     19 		fprintf(outfile, " 0x%04x:  ", item + base);
     20 		for (i = 0; i < 16; i++) {
     21 			fprintf(outfile, "%02x ", edid_data[item++]);
     22 			if (item >= items)
     23 				break;
     24 		}
     25 		fprintf(outfile, "\n");
     26 	}
     27 }
     28 
     29 
     30 unsigned char test_edid1[256] = {
     31 	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
     32 	0x06, 0xaf, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00,
     33 	0x01, 0x12, 0x01, 0x03, 0x80, 0x1a, 0x0e, 0x78,
     34 	0x0a, 0x99, 0x85, 0x95, 0x55, 0x56, 0x92, 0x28,
     35 	0x22, 0x50, 0x54, 0x00, 0x00, 0x00, 0x01, 0x01,
     36 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
     37 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x96, 0x19,
     38 	0x56, 0x28, 0x50, 0x00, 0x08, 0x30, 0x18, 0x10,
     39 	0x24, 0x00, 0x00, 0x90, 0x10, 0x00, 0x00, 0x18,
     40 	0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
     41 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     42 	0x00, 0x20, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x41,
     43 	0x55, 0x4f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
     44 	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfe,
     45 	0x00, 0x42, 0x31, 0x31, 0x36, 0x58, 0x57, 0x30,
     46 	0x32, 0x20, 0x56, 0x30, 0x20, 0x0a, 0x00, 0xf8
     47 };
     48 
     49 unsigned char test_edid2[256] = {
     50 	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
     51 	0x30, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     52 	0x00, 0x14, 0x01, 0x03, 0x80, 0x1a, 0x0e, 0x78,
     53 	0x0a, 0xbf, 0x45, 0x95, 0x58, 0x52, 0x8a, 0x28,
     54 	0x25, 0x50, 0x54, 0x00, 0x00, 0x00, 0x01, 0x01,
     55 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
     56 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x84, 0x1c,
     57 	0x56, 0xa8, 0x50, 0x00, 0x19, 0x30, 0x30, 0x20,
     58 	0x35, 0x00, 0x00, 0x90, 0x10, 0x00, 0x00, 0x1b,
     59 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     60 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     61 	0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x4c,
     62 	0x47, 0x20, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61,
     63 	0x79, 0x0a, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
     64 	0x00, 0x4c, 0x50, 0x31, 0x31, 0x36, 0x57, 0x48,
     65 	0x31, 0x2d, 0x54, 0x4c, 0x4e, 0x31, 0x00, 0x4e
     66 };
     67 
     68 unsigned char test_edid3[256] = {
     69 	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
     70 	0x4d, 0xd9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
     71 	0x00, 0x11, 0x01, 0x03, 0x80, 0x00, 0x00, 0x78,
     72 	0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27,
     73 	0x12, 0x48, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x01,
     74 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
     75 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1d,
     76 	0x80, 0xd0, 0x72, 0x1c, 0x16, 0x20, 0x10, 0x2c,
     77 	0x25, 0x80, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x9e,
     78 	0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20,
     79 	0x58, 0x2c, 0x25, 0x00, 0xc4, 0x8e, 0x21, 0x00,
     80 	0x00, 0x9e, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x48,
     81 	0x44, 0x4d, 0x49, 0x20, 0x4c, 0x4c, 0x43, 0x0a,
     82 	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd,
     83 	0x00, 0x3b, 0x3d, 0x0f, 0x2d, 0x08, 0x00, 0x0a,
     84 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xc0,
     85 	0x02, 0x03, 0x1e, 0x47, 0x4f, 0x94, 0x13, 0x05,
     86 	0x03, 0x04, 0x02, 0x01, 0x16, 0x15, 0x07, 0x06,
     87 	0x11, 0x10, 0x12, 0x1f, 0x23, 0x09, 0x07, 0x01,
     88 	0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x8c, 0x0a,
     89 	0xd0, 0x90, 0x20, 0x40, 0x31, 0x20, 0x0c, 0x40,
     90 	0x55, 0x00, 0x13, 0x8e, 0x21, 0x00, 0x00, 0x18,
     91 	0x01, 0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20,
     92 	0xb8, 0x28, 0x55, 0x40, 0xc4, 0x8e, 0x21, 0x00,
     93 	0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0,
     94 	0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xc4, 0x8e,
     95 	0x21, 0x00, 0x00, 0x18, 0x01, 0x1d, 0x00, 0x72,
     96 	0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00,
     97 	0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, 0x8c, 0x0a,
     98 	0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e,
     99 	0x96, 0x00, 0x13, 0x8e, 0x21, 0x00, 0x00, 0x18,
    100 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb
    101 };
    102 
    103 unsigned char test_edid4[256] = {
    104 	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
    105 	0x4c, 0x2d, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,
    106 	0x31, 0x0f, 0x01, 0x03, 0x80, 0x10, 0x09, 0x8c,
    107 	0x0a, 0xe2, 0xbd, 0xa1, 0x5b, 0x4a, 0x98, 0x24,
    108 	0x15, 0x47, 0x4a, 0x20, 0x00, 0x00, 0x01, 0x01,
    109 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    110 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1d,
    111 	0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28,
    112 	0x55, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e,
    113 	0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20,
    114 	0x58, 0x2c, 0x25, 0x00, 0xa0, 0x5a, 0x00, 0x00,
    115 	0x00, 0x9e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
    116 	0x3d, 0x1e, 0x2e, 0x08, 0x00, 0x0a, 0x20, 0x20,
    117 	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
    118 	0x00, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47,
    119 	0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x8d,
    120 	0x02, 0x03, 0x16, 0x71, 0x43, 0x84, 0x05, 0x03,
    121 	0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00,
    122 	0x65, 0x03, 0x0c, 0x00, 0x20, 0x00, 0x8c, 0x0a,
    123 	0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e,
    124 	0x96, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x18,
    125 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    126 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    127 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    128 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    129 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    130 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    131 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    132 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    133 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    134 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    135 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30
    136 };
    137 
    138 unsigned char test_edid5[256] = {
    139 	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
    140 	0x3d, 0xcb, 0x61, 0x07, 0x00, 0x00, 0x00, 0x00,
    141 	0x00, 0x11, 0x01, 0x03, 0x80, 0x00, 0x00, 0x78,
    142 	0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27,
    143 	0x12, 0x48, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x01,
    144 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    145 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1d,
    146 	0x80, 0x18, 0x71, 0x1c, 0x16, 0x20, 0x58, 0x2c,
    147 	0x25, 0x00, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x9e,
    148 	0x01, 0x1d, 0x80, 0xd0, 0x72, 0x1c, 0x16, 0x20,
    149 	0x10, 0x2c, 0x25, 0x80, 0xc4, 0x8e, 0x21, 0x00,
    150 	0x00, 0x9e, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54,
    151 	0x58, 0x2d, 0x53, 0x52, 0x36, 0x30, 0x35, 0x0a,
    152 	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd,
    153 	0x00, 0x17, 0xf0, 0x0f, 0x7e, 0x11, 0x00, 0x0a,
    154 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x93,
    155 	0x02, 0x03, 0x3b, 0x72, 0x55, 0x85, 0x04, 0x03,
    156 	0x02, 0x0e, 0x0f, 0x07, 0x23, 0x24, 0x10, 0x94,
    157 	0x13, 0x12, 0x11, 0x1d, 0x1e, 0x16, 0x25, 0x26,
    158 	0x01, 0x1f, 0x35, 0x09, 0x7f, 0x07, 0x0f, 0x7f,
    159 	0x07, 0x17, 0x07, 0x50, 0x3f, 0x06, 0xc0, 0x57,
    160 	0x06, 0x00, 0x5f, 0x7e, 0x01, 0x67, 0x5e, 0x00,
    161 	0x83, 0x4f, 0x00, 0x00, 0x66, 0x03, 0x0c, 0x00,
    162 	0x20, 0x00, 0x80, 0x8c, 0x0a, 0xd0, 0x8a, 0x20,
    163 	0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xc4,
    164 	0x8e, 0x21, 0x00, 0x00, 0x18, 0x8c, 0x0a, 0xd0,
    165 	0x90, 0x20, 0x40, 0x31, 0x20, 0x0c, 0x40, 0x55,
    166 	0x00, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x18, 0x01,
    167 	0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e,
    168 	0x28, 0x55, 0x00, 0xc4, 0x8e, 0x21, 0x00, 0x00,
    169 	0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    170 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd
    171 };
    172 
    173 /* Has DTD that is too wide */
    174 unsigned char test_edid6[256] = {
    175 	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
    176 	0x10, 0xac, 0x63, 0x40, 0x4c, 0x35, 0x31, 0x33,
    177 	0x0c, 0x15, 0x01, 0x03, 0x80, 0x40, 0x28, 0x78,
    178 	0xea, 0x8d, 0x85, 0xad, 0x4f, 0x35, 0xb1, 0x25,
    179 	0x0e, 0x50, 0x54, 0xa5, 0x4b, 0x00, 0x71, 0x4f,
    180 	0x81, 0x00, 0x81, 0x80, 0xa9, 0x40, 0xd1, 0x00,
    181 	0xd1, 0x40, 0x01, 0x01, 0x01, 0x01, 0xe2, 0x68,
    182 	0x00, 0xa0, 0xa0, 0x40, 0x2e, 0x60, 0x30, 0x20,
    183 	0x36, 0x00, 0x81, 0x91, 0x21, 0x00, 0x00, 0x1a,
    184 	0x00, 0x00, 0x00, 0xff, 0x00, 0x50, 0x48, 0x35,
    185 	0x4e, 0x59, 0x31, 0x33, 0x4d, 0x33, 0x31, 0x35,
    186 	0x4c, 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44,
    187 	0x45, 0x4c, 0x4c, 0x20, 0x55, 0x33, 0x30, 0x31,
    188 	0x31, 0x0a, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd,
    189 	0x00, 0x31, 0x56, 0x1d, 0x71, 0x1c, 0x00, 0x0a,
    190 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0xb0
    191 };
    192 
    193 static unsigned char *test_edids[N_TEST_EDIDS] = {
    194 	test_edid1, test_edid2, test_edid3, test_edid4, test_edid5,
    195 	test_edid6
    196 };
    197 
    198 int get_test_edid(int n, unsigned char *dst)
    199 {
    200 	if ((n < 1) || (n > N_TEST_EDIDS))
    201 		return -1;
    202 	memcpy(dst, test_edids[n-1], 256);
    203 	return 0;
    204 }
    205 
    206 int show_test_edid(FILE *outfile, int n)
    207 {
    208 	if ((n < 1) || (n > N_TEST_EDIDS))
    209 		return -1;
    210 	fprintf(outfile, "Test EDID %d\n", n);
    211 	show_edid(outfile, test_edids[n-1], 1);
    212 	return 0;
    213 }
    214 
    215 static void get_dtd_string(const char *str, char *buf, int buf_size)
    216 {
    217 	int stp;
    218 	int len = buf_size < 14 ? buf_size : 14;
    219 
    220 	strncpy(buf, str, len - 1);
    221 	for (stp = 0; stp < len - 1; stp++)
    222 		if (buf[stp] == 0x0a)
    223 			buf[stp] = 0;
    224 	buf[stp] = 0;
    225 }
    226 
    227 /* Print an edid descriptor block (standard case is at 54 + 18 * i) */
    228 void show_edid_dtd(FILE *outfile, unsigned char *base)
    229 {
    230 	int pelclk = base[DTD_PCLK_LO] + (base[DTD_PCLK_HI]<<8);
    231 	char monstr[DTD_SIZE];
    232 
    233 	if (pelclk != 0) {
    234 		int hres = base[DTD_HA_LO] + ((base[DTD_HABL_HI] & 0xf0)<<4);
    235 		int hbl = base[DTD_HBL_LO] + ((base[DTD_HABL_HI] & 0x0f)<<8);
    236 		int vres = base[DTD_VA_LO] + ((base[DTD_VABL_HI] & 0xf0)<<4);
    237 		int vbl = base[DTD_VBL_LO] + ((base[DTD_VABL_HI] & 0x0f)<<8);
    238 		int hso = base[DTD_HSO_LO] + ((base[DTD_HVSX_HI] & 0xc0)<<2);
    239 		int hsw = base[DTD_HSW_LO] + ((base[DTD_HVSX_HI] & 0x30)<<4);
    240 		int vso = (base[DTD_VSX_LO] >> 4) +
    241 			   ((base[DTD_HVSX_HI] & 0x0c) << 2);
    242 		int vsw = (base[DTD_VSX_LO] & 0xf) +
    243 			   ((base[DTD_HVSX_HI] & 0x03) << 4);
    244 		int hsiz = base[DTD_HSIZE_LO] +
    245 			   ((base[DTD_HVSIZE_HI] & 0xf0) << 4);
    246 		int vsiz = base[DTD_VSIZE_LO] +
    247 			   ((base[DTD_HVSIZE_HI] & 0x0f) << 8);
    248 		int hbdr = base[DTD_HBORDER];
    249 		int vbdr = base[DTD_VBORDER];
    250 		int mdflg = base[DTD_FLAGS];
    251 
    252 		int refr = (pelclk * 10000)/((hres+hbl)*(vres+vbl));
    253 		int refm = (pelclk * 10000)%((hres+hbl)*(vres+vbl));
    254 		int refd = (refm*100)/((hres+hbl)*(vres+vbl));
    255 
    256 		fprintf(outfile,
    257 			"%dx%d%c@%d.%02d, dot clock %d  %cHsync %cVsync\n",
    258 			hres, vres, (mdflg & 0x80) ? 'i' : 'p',
    259 			refr, refd,
    260 			pelclk * 10000,
    261 			(mdflg & 0x2) ? '+' : '-',
    262 			(mdflg & 0x4) ? '+' : '-');
    263 		fprintf(outfile, "H: start %d, end %d, total %d\n",
    264 			hres+hso, hres+hso+hsw, hres+hbl);
    265 		fprintf(outfile, "V: start %d, end %d, total %d\n",
    266 			vres+vso, vres+vso+vsw, vres+vbl);
    267 		fprintf(outfile, "Size %dx%dmm, Border %dx%d pixels\n",
    268 			hsiz, vsiz, hbdr, vbdr);
    269 		return;
    270 	}
    271 
    272 	switch (base[DTD_TYPETAG]) {
    273 	case DTDTYPE_SERIAL:
    274 	case DTDTYPE_STRING:
    275 	case DTDTYPE_NAME:
    276 		get_dtd_string((const char *)base + DTD_STRING,
    277 			       monstr, DTD_SIZE);
    278 
    279 		if (base[3] != DTDTYPE_STRING)
    280 			fprintf(outfile, "%s: %s\n",
    281 				(base[3] == DTDTYPE_NAME) ?
    282 				"Name" : "Serial",
    283 				monstr);
    284 		else
    285 			fprintf(outfile, "%s\n", monstr);
    286 		break;
    287 
    288 	case DTDTYPE_LIMITS:
    289 		fprintf(outfile,
    290 			"V %d - %d Hz, H %d - %d kHz, Pel <= %d MHz\n",
    291 			base[DTD_MINV_HZ], base[DTD_MAXV_HZ],
    292 			base[DTD_MINH_kHZ], base[DTD_MAXH_kHZ],
    293 			base[DTD_MAXCLK_100kHZ]*10);
    294 		break;
    295 
    296 	default:
    297 		fprintf(outfile,
    298 			"Undecoded descriptor block type 0x%x\n",
    299 			base[DTD_TYPETAG]);
    300 		break;
    301 	}
    302 }
    303 
    304 
    305 char *sad_audio_type[16] = {
    306 	"Reserved", "LPCM", "AC-3", "MPEG1 (Layer 1 and 2)",
    307 	"MP3", "MPEG2", "AAC", "DTS",
    308 	"ATRAC", "SACD", "DD+", "DTS-HD",
    309 	"MLP/Dolby TrueHD", "DST Audio", "WMA Pro", "Reserved",
    310 };
    311 
    312 char *uscanstr[4] = {
    313 	"not supported",
    314 	"always overscan",
    315 	"always underscan",
    316 	"supports both over- and underscan",
    317 };
    318 
    319 static inline void show_audio_dbc(FILE *outfile,
    320 				  const unsigned char *edid_ext,
    321 				  int dbc)
    322 {
    323 	int dbp = dbc + 1;
    324 	int db_len = edid_ext[dbc+DBC_TAG_LENGTH] & DBC_LEN_MASK;
    325 
    326 	while (dbp < (dbc + db_len + 1)) {
    327 		int atype =
    328 			(edid_ext[dbp + DBCA_FORMAT]>>3) & 0xf;
    329 		unsigned char dbca_rate = edid_ext[dbp + DBCA_RATE];
    330 
    331 		fprintf(outfile, "Audio: %d channels %s: ",
    332 			(edid_ext[dbp + DBCA_FORMAT] & 0x7) + 1,
    333 			sad_audio_type[atype]);
    334 
    335 		if (dbca_rate & 0x40)
    336 			fprintf(outfile, "192k ");
    337 		if (dbca_rate & 0x20)
    338 			fprintf(outfile, "176k ");
    339 		if (dbca_rate & 0x10)
    340 			fprintf(outfile, "96k ");
    341 		if (dbca_rate & 0x08)
    342 			fprintf(outfile, "88k ");
    343 		if (dbca_rate & 0x04)
    344 			fprintf(outfile, "48k ");
    345 		if (dbca_rate & 0x02)
    346 			fprintf(outfile, "44k ");
    347 		if (dbca_rate & 0x01)
    348 			fprintf(outfile, "32k ");
    349 
    350 		if (atype == 1) {
    351 			unsigned char dbca_info = edid_ext[dbp + DBCA_INFO];
    352 			fprintf(outfile, "%s%s%s\n",
    353 				(dbca_info & 0x4) ? "24-bit " : "",
    354 				(dbca_info & 0x2) ? "20-bit " : "",
    355 				(dbca_info & 0x1) ? "16-bit" : "");
    356 		} else if ((atype >= 2) && (atype <= 8)) {
    357 			fprintf(outfile, "Max %dkHz\n",
    358 				edid_ext[dbp + DBCA_INFO] * 8);
    359 		} else {
    360 			fprintf(outfile, "Codec vendor flags 0x%02x\n",
    361 				edid_ext[dbp + DBCA_INFO]);
    362 		}
    363 
    364 		dbp += DBCA_SIZE;
    365 	}
    366 }
    367 
    368 static inline void show_vendor_dbc(FILE *outfile,
    369 				   const unsigned char *edid_ext,
    370 				   int dbp)
    371 {
    372 	if ((edid_ext[dbp + DBCVND_IEEE_LO] != 0x03) ||
    373 	    (edid_ext[dbp + DBCVND_IEEE_MID] != 0x0C) ||
    374 	    (edid_ext[dbp + DBCVND_IEEE_HI] != 0x00)) {
    375 		fprintf(outfile, "Vendor block for %02x-%02x-%02x",
    376 			edid_ext[dbp + DBCVND_IEEE_LO],
    377 			edid_ext[dbp + DBCVND_IEEE_MID],
    378 			edid_ext[dbp + DBCVND_IEEE_HI]);
    379 		return;
    380 	}
    381 
    382 	fprintf(outfile,
    383 		"HDMI Vendor block (CEC @0x%04x):\n"
    384 		"Support: %s%s%s%s%s%s\n",
    385 		edid_ext[dbp + DBCVHDMI_CEC_LO] +
    386 		(edid_ext[dbp + DBCVHDMI_CEC_HI] << 8),
    387 		(edid_ext[dbp + DBCVHDMI_SUPPORT] & 0x80) ? "AI " : "",
    388 		(edid_ext[dbp + DBCVHDMI_SUPPORT] & 0x40) ? "DC_48bit " : "",
    389 		(edid_ext[dbp + DBCVHDMI_SUPPORT] & 0x20) ? "DC_36bit " : "",
    390 		(edid_ext[dbp + DBCVHDMI_SUPPORT] & 0x10) ? "DC_30bit " : "",
    391 		(edid_ext[dbp + DBCVHDMI_SUPPORT] & 0x08) ? "DC_Y444 " : "",
    392 		(edid_ext[dbp + DBCVHDMI_SUPPORT] & 0x01) ? "DVI_Dual" : "");
    393 
    394 	if (edid_ext[dbp + DBCVHDMI_MAXTMDS_5MHz] > 0)
    395 		fprintf(outfile, "Max TMDS Frequency %dMHz\n",
    396 			edid_ext[dbp + DBCVHDMI_MAXTMDS_5MHz]*5);
    397 
    398 	if (edid_ext[dbp + DBCVHDMI_LATFLAGS] & 0x80)
    399 		fprintf(outfile, "Video latency %dms, audio latency %dms\n",
    400 			2 * (edid_ext[dbp + DBCVHDMI_VLAT] - 1),
    401 			2 * (edid_ext[dbp + DBCVHDMI_ALAT] - 1));
    402 
    403 	if (edid_ext[dbp + 7] & 0x40)
    404 		fprintf(outfile,
    405 			"Interlaced Video latency %dms, audio latency %dms\n",
    406 			2 * (edid_ext[dbp + DBCVHDMI_IVLAT] - 1),
    407 			2 * (edid_ext[dbp + DBCVHDMI_IALAT] - 1));
    408 }
    409 
    410 static void show_extended_dbc(FILE *outfile,
    411 			      const unsigned char *edid_ext,
    412 			      int dbc)
    413 {
    414 	int dbp = dbc + 1;
    415 	int db_len = edid_ext[dbc + DBC_TAG_LENGTH] & DBC_LEN_MASK;
    416 
    417 	switch (edid_ext[dbp + DBC_ETAG]) {
    418 	case DBC_ETAG_VCDB:
    419 	{
    420 		unsigned char vcdb_flags;
    421 
    422 		fprintf(outfile, "Video Capabilities:\n");
    423 		fprintf(outfile,
    424 			"  Quantization range selectable: %s\n",
    425 			(edid_ext[dbp + VCDB_FLAGS] & 0x40) ?
    426 				"unknown" : "via AVI Q");
    427 
    428 		/* PT field zero implies no data, just use IT
    429 		 * and CE fields
    430 		 */
    431 		vcdb_flags = edid_ext[dbp + VCDB_FLAGS];
    432 		if (VCDB_S_PT(vcdb_flags))
    433 			fprintf(outfile,
    434 				"  Preferred mode %s\n",
    435 				uscanstr[VCDB_S_PT(vcdb_flags)]);
    436 		fprintf(outfile, "  IT modes %s\n",
    437 			uscanstr[VCDB_S_IT(vcdb_flags)]);
    438 		fprintf(outfile, "  CE modes %s\n",
    439 			uscanstr[VCDB_S_CE(vcdb_flags)]);
    440 		break;
    441 	}
    442 
    443 	case DBC_ETAG_COL:
    444 		fprintf(outfile,
    445 			"Colorimetry supports %s%s metadata 0x%x\n",
    446 			(edid_ext[dbp + COL_FLAGS] & 0x02) ? "HD(YCC709) " : "",
    447 			(edid_ext[dbp + COL_FLAGS] & 0x01) ? "SD(YCC601) " : "",
    448 			(edid_ext[dbp + COL_META] & 0x07));
    449 		break;
    450 
    451 	default:
    452 		fprintf(outfile,
    453 			"Unknown extended tag data block 0x%x,  length 0x%x\n",
    454 			edid_ext[dbc + DBC_ETAG], db_len);
    455 	}
    456 }
    457 
    458 void show_cea_timing(FILE *outfile, unsigned char *edid_ext)
    459 {
    460 	int i, dbc;
    461 	int off_dtd = edid_ext[CEA_DTD_OFFSET];
    462 	int n_dtd;
    463 	fprintf(outfile, "Found CEA EDID Timing Extension rev 3\n");
    464 
    465 	if (off_dtd < CEA_DBC_START) {
    466 		fprintf(outfile, "Block is empty (off_dtd = %d)\n", off_dtd);
    467 		return;
    468 	}
    469 	/* Ends with 0 and a checksum, have at least one pad byte */
    470 	n_dtd = (CEA_LAST_PAD - off_dtd)/DTD_SIZE;
    471 	fprintf(outfile,
    472 		"Block has DTDs starting at offset %d (%d bytes of DBCs)\n",
    473 		off_dtd, off_dtd - CEA_DBC_START);
    474 	fprintf(outfile, "There is space for %d DTDs in extension\n", n_dtd);
    475 	fprintf(outfile,
    476 		"There are %d native DTDs (between regular and extensions)\n",
    477 		edid_ext[CEA_NATIVE_DTDS] & 0xf);
    478 	fprintf(outfile, "IT formats %sdefault to underscan\n",
    479 		(edid_ext[CEA_SUPPORT] & 0x80) ? "" : "do not ");
    480 	fprintf(outfile,
    481 		"Support: %sbasic audio, %sYCrCb 4:4:4, %sYCrCb 4:2:2\n",
    482 		(edid_ext[CEA_SUPPORT] & 0x40) ? "" : "no ",
    483 		(edid_ext[CEA_SUPPORT] & 0x20) ? "" : "no ",
    484 		(edid_ext[CEA_SUPPORT] & 0x10) ? "" : "no ");
    485 
    486 	/* Between offset 4 and off_dtd is the Data Block Collection */
    487 	/* There may be none, in which case off_dtd == 4             */
    488 	dbc = CEA_DBC_START;
    489 	while (dbc < off_dtd) {
    490 		int db_len = edid_ext[dbc + DBC_TAG_LENGTH] & DBC_LEN_MASK;
    491 		int dbp = dbc + 1;
    492 
    493 		switch (edid_ext[dbc+DBC_TAG_LENGTH] >> DBC_TAG_SHIFT) {
    494 		case DBC_TAG_AUDIO:
    495 			/* Audio Data Block */
    496 			show_audio_dbc(outfile, edid_ext, dbc);
    497 			break;
    498 
    499 		case DBC_TAG_VIDEO:
    500 			/* Vidio Data Block */
    501 			while (dbp < (dbc + db_len + 1)) {
    502 				int vtype = edid_ext[dbp + DBCV_CODE] & 0x7f;
    503 				fprintf(outfile, "Video: Code %d %s\n", vtype,
    504 					(edid_ext[dbp + DBCV_CODE] & 0x80) ?
    505 						"(native)" : "");
    506 				dbp += DBCV_SIZE;
    507 			}
    508 			break;
    509 
    510 		case DBC_TAG_VENDOR:
    511 			/* Vendor Data Block */
    512 			show_vendor_dbc(outfile, edid_ext, dbc + 1);
    513 			break;
    514 
    515 		case DBC_TAG_SPEAKER:
    516 		{
    517 			/* Speaker allocation Block */
    518 			unsigned char dbcsp_alloc = edid_ext[dbp + DBCSP_ALLOC];
    519 
    520 			fprintf(outfile, "Speakers: %s%s%s%s%s%s%s\n",
    521 				(dbcsp_alloc & 0x40) ? "RearCenter L/R " : "",
    522 				(dbcsp_alloc & 0x20) ? "FrontCenter L/R " : "",
    523 				(dbcsp_alloc & 0x10) ? "Rear Center" : "",
    524 				(dbcsp_alloc & 0x08) ? "Rear L/R " : "",
    525 				(dbcsp_alloc & 0x04) ? "Front Center " : "",
    526 				(dbcsp_alloc & 0x02) ? "LFE " : "",
    527 				(dbcsp_alloc & 0x01) ? "Front L/R " : "");
    528 			break;
    529 		}
    530 
    531 		case DBC_TAG_EXTENDED:
    532 			show_extended_dbc(outfile, edid_ext, dbc);
    533 			break;
    534 
    535 		default:
    536 			fprintf(outfile,
    537 				"Unknown Data Block type tag 0x%x, len 0x%x\n",
    538 				edid_ext[dbc+DBC_TAG_LENGTH] >> DBC_TAG_SHIFT,
    539 				db_len);
    540 			break;
    541 		}
    542 
    543 		dbc += db_len + 1;
    544 	}
    545 	for (i = 0; i < n_dtd; i++) {
    546 		/* Find 0,0 when we hit padding */
    547 		if ((edid_ext[off_dtd + DTD_SIZE * i + DTD_PCLK_LO] == 0) &&
    548 		    (edid_ext[off_dtd + DTD_SIZE * i + DTD_PCLK_HI] == 0)) {
    549 			fprintf(outfile,
    550 				"End of DTD padding after %d DTDs\n", i);
    551 			break;
    552 		}
    553 		show_edid_dtd(outfile, edid_ext + (off_dtd + DTD_SIZE * i));
    554 	}
    555 }
    556 
    557 
    558 int edid_valid(const unsigned char *edid_data)
    559 {
    560 	return ((edid_data[EDID_HDR + 0] == 0x00) &&
    561 		(edid_data[EDID_HDR + 1] == 0xff) &&
    562 		(edid_data[EDID_HDR + 2] == 0xff) &&
    563 		(edid_data[EDID_HDR + 3] == 0xff) &&
    564 		(edid_data[EDID_HDR + 4] == 0xff) &&
    565 		(edid_data[EDID_HDR + 5] == 0xff) &&
    566 		(edid_data[EDID_HDR + 6] == 0xff) &&
    567 		(edid_data[EDID_HDR + 7] == 0x00));
    568 }
    569 
    570 int edid_lpcm_support(const unsigned char *edid_data, int ext)
    571 {
    572 	const unsigned char *edid_ext = edid_data + EDID_SIZE;
    573 	int dbc;
    574 	int off_dtd = edid_ext[CEA_DTD_OFFSET];
    575 
    576 	/* No if no extension, which can happen for two reasons */
    577 	/* a) ext < 1 indicates no data was read into the extension area */
    578 	/* b) edid_data[126] < 1 indicates EDID does not use extension area */
    579 	if ((ext < 1) || (edid_data[EDID_EXT_FLAG] < 1))
    580 		return 0;
    581 
    582 	/* No if extension is not CEA rev 3 */
    583 	if (!((edid_ext[EEXT_TAG] == 0x02) && (edid_ext[EEXT_REV] == 0x03)))
    584 		return 0;
    585 
    586 	/* If DBC block is not empty look for audio info */
    587 	if (off_dtd <= CEA_DBC_START)
    588 		goto done_dtd;
    589 
    590 	/* Between offset 4 and off_dtd is the Data Block Collection */
    591 	/* There may be none, in which case off_dtd == 4             */
    592 	dbc = CEA_DBC_START;
    593 	while (dbc < off_dtd) {
    594 		int db_len = edid_ext[dbc + DBC_TAG_LENGTH] & DBC_LEN_MASK;
    595 		int dbp = dbc + 1;
    596 		unsigned char dbc_type;
    597 
    598 		/* Audio Data Block, type LPCM, return bitmap of frequencies */
    599 		dbc_type = edid_ext[dbc + DBC_TAG_LENGTH] >> DBC_TAG_SHIFT;
    600 		if ((dbc_type == DBC_TAG_AUDIO) &&
    601 		    (((edid_ext[dbp + DBCA_FORMAT]>>3) & 0xF) == DBCA_FMT_LPCM))
    602 			return edid_ext[dbp + DBCA_RATE];
    603 
    604 		dbc += db_len + 1;
    605 	}
    606 	/* Get here if failed to find LPCM info in DBC block */
    607 
    608 done_dtd:
    609 	/* Last chance is to look for Basic Audio support. Return bitmap for 32,
    610 	 * 44.1, 48 */
    611 	if (edid_ext[CEA_SUPPORT] & 0x40)
    612 		return 0x7;
    613 
    614 	return 0;
    615 }
    616 
    617 
    618 int edid_has_hdmi_info(const unsigned char *edid_data, int ext)
    619 {
    620 	const unsigned char *edid_ext = edid_data + EDID_SIZE;
    621 	int dbc;
    622 	int off_dtd = edid_ext[CEA_DTD_OFFSET];
    623 
    624 	/* No if no extension, which can happen for two reasons */
    625 	/* a) ext < 1 indicates no data was read into the extension area */
    626 	/* b) edid_data[126] < 1 indicates EDID does not use extension area */
    627 	if ((ext < 1) || (edid_data[EDID_EXT_FLAG] < 1))
    628 		return 0;
    629 
    630 	/* No if extension is not CEA rev 3 */
    631 	if (!((edid_ext[EEXT_TAG] == 0x02) && (edid_ext[EEXT_REV] == 0x03)))
    632 		return 0;
    633 
    634 	/* No if block is empty */
    635 	if (off_dtd < CEA_DBC_START)
    636 		return 0;
    637 
    638 	/* Between offset 4 and off_dtd is the Data Block Collection */
    639 	/* There may be none, in which case off_dtd == 4             */
    640 	dbc = CEA_DBC_START;
    641 	while (dbc < off_dtd) {
    642 		int db_len = edid_ext[dbc + DBC_TAG_LENGTH] & DBC_LEN_MASK;
    643 		int dbp = dbc + 1;
    644 		unsigned char dbc_type;
    645 
    646 		dbc_type = edid_ext[dbc + DBC_TAG_LENGTH] >> DBC_TAG_SHIFT;
    647 		if (dbc_type == DBC_TAG_VENDOR) {
    648 			/* Vendor Data Block */
    649 			if ((edid_ext[dbp + DBCVND_IEEE_LO] == 0x03) &&
    650 			    (edid_ext[dbp + DBCVND_IEEE_MID] == 0x0C) &&
    651 			    (edid_ext[dbp + DBCVND_IEEE_HI] == 0x00))
    652 				return 1;
    653 		}
    654 		dbc += db_len + 1;
    655 	}
    656 	return 0;
    657 }
    658 
    659 /* Print out an EDID */
    660 void show_edid(FILE *outfile, unsigned char *edid_data, int ext)
    661 {
    662 	int i;
    663 	int edidver = edid_data[EDID_VERSION];
    664 	int edidrev = edid_data[EDID_REVISION];
    665 	unsigned char *edid_ext;
    666 	unsigned char edid_features;
    667 
    668 	if (!edid_valid(edid_data)) {
    669 		fprintf(outfile, "Block does not contain EDID header\n");
    670 		return;
    671 	}
    672 	/* unsigned edid_data so the right shifts pull in zeros */
    673 	fprintf(outfile, "Manufacturer ID %c%c%c, product ID 0x%x\n",
    674 		'@' + (edid_data[EDID_MFG_EID]>>2),
    675 		'@' + (((edid_data[EDID_MFG_EID] & 3)<<3) +
    676 			(edid_data[EDID_MFG_EID+1]>>5)),
    677 		'@' + (edid_data[EDID_MFG_EID+1] & 0x1f),
    678 		edid_data[EDID_MFG_PROD_LO] + (edid_data[EDID_MFG_PROD_HI]<<8));
    679 	fprintf(outfile,
    680 		"Manufactured wk %d of %d. Edid version %d.%d\n",
    681 		edid_data[EDID_MFG_WEEK], 1990+edid_data[EDID_MFG_YEAR],
    682 		edidver, edidrev);
    683 	fprintf(outfile,
    684 		"Input: %s, vid level %d, %s, %s %s %s %s sync, %dx%dcm, Gamma %f\n",
    685 		(edid_data[EDID_VIDEO_IN] & 0x80) ? "digital" : "analog",
    686 		(edid_data[EDID_VIDEO_IN]>>5) & 3,
    687 		(edid_data[EDID_VIDEO_IN] * 0x10) ? "Blank to black" : "",
    688 		(edid_data[EDID_VIDEO_IN] * 0x08) ? "Separate" : "",
    689 		(edid_data[EDID_VIDEO_IN] * 0x04) ? "Composite" : "",
    690 		(edid_data[EDID_VIDEO_IN] * 0x02) ? "On-green" : "",
    691 		(edid_data[EDID_VIDEO_IN] * 0x01) ? "Serration V" : "",
    692 		edid_data[EDID_MAX_HSIZE], edid_data[EDID_MAX_VSIZE],
    693 		1.0+((float)edid_data[EDID_GAMMA]/100.0));
    694 
    695 	edid_features = edid_data[EDID_FEATURES];
    696 	fprintf(outfile, "Features: %s %s %s %s %s %s %s\n",
    697 		(edid_features & 0x80) ? "standby" : "",
    698 		(edid_features & 0x40) ? "suspend" : "",
    699 		(edid_features & 0x20) ? "active-off" : "",
    700 		(edid_features & 0x18) ? "colour" : "monochrome",
    701 		(edid_features & 0x04) ? "std-cspace" : "non-std-cspace",
    702 		(edid_features & 0x02) ? "preferred-timing" : "",
    703 		(edid_features & 0x01) ? "default-GTF" : "");
    704 
    705 	fprintf(outfile, "Established Timing:\n");
    706 	if (edid_data[EDID_ESTTIME1] & 0x80)
    707 		fprintf(outfile, "720x400@70\n");
    708 	if (edid_data[EDID_ESTTIME1] & 0x40)
    709 		fprintf(outfile, "720x400@88\n");
    710 	if (edid_data[EDID_ESTTIME1] & 0x20)
    711 		fprintf(outfile, "640x480@60\n");
    712 	if (edid_data[EDID_ESTTIME1] & 0x10)
    713 		fprintf(outfile, "640x480@67\n");
    714 	if (edid_data[EDID_ESTTIME1] & 0x08)
    715 		fprintf(outfile, "640x480@72\n");
    716 	if (edid_data[EDID_ESTTIME1] & 0x04)
    717 		fprintf(outfile, "640x480@75\n");
    718 	if (edid_data[EDID_ESTTIME1] & 0x02)
    719 		fprintf(outfile, "800x600@56\n");
    720 	if (edid_data[EDID_ESTTIME1] & 0x01)
    721 		fprintf(outfile, "800x600@60\n");
    722 	if (edid_data[EDID_ESTTIME2] & 0x80)
    723 		fprintf(outfile, "800x600@72\n");
    724 	if (edid_data[EDID_ESTTIME2] & 0x40)
    725 		fprintf(outfile, "800x600@75\n");
    726 	if (edid_data[EDID_ESTTIME2] & 0x20)
    727 		fprintf(outfile, "832x624@75\n");
    728 	if (edid_data[EDID_ESTTIME2] & 0x10)
    729 		fprintf(outfile, "1024x768i@87\n");
    730 	if (edid_data[EDID_ESTTIME2] & 0x08)
    731 		fprintf(outfile, "1024x768@60\n");
    732 	if (edid_data[EDID_ESTTIME2] & 0x04)
    733 		fprintf(outfile, "1024x768@70\n");
    734 	if (edid_data[EDID_ESTTIME2] & 0x02)
    735 		fprintf(outfile, "1024x768@75\n");
    736 	if (edid_data[EDID_ESTTIME2] & 0x01)
    737 		fprintf(outfile, "1280x1024@75\n");
    738 	if (edid_data[EDID_MFGTIME]  & 0x80)
    739 		fprintf(outfile, "1152x870@75\n");
    740 
    741 	fprintf(outfile, "Standard timing:\n");
    742 	for (i = 0; i < EDID_N_STDTIME; i++) {
    743 		int hinfo = edid_data[EDID_STDTIMEH + 2 * i];
    744 		int vinfo = edid_data[EDID_STDTIMEV + 2 * i];
    745 		int hres, vres;
    746 
    747 		/* 01 01 is pad by spec, but 00 00 and 20 20 are see in wild */
    748 		if (((hinfo == 0x01) && (vinfo == 0x01)) ||
    749 		    ((hinfo == 0x00) && (vinfo == 0x00)) ||
    750 		    ((hinfo == 0x20) && (vinfo == 0x20)))
    751 			continue;
    752 		hres = (hinfo * 8) + 248;
    753 		switch (vinfo >> 6) {
    754 		case ASPECT_16_10:
    755 			vres = (hres * 10)/16;
    756 			break;
    757 		case ASPECT_4_3:
    758 			vres = (hres * 3)/4;
    759 			break;
    760 		case ASPECT_5_4:
    761 			vres = (hres * 4)/5;
    762 			break;
    763 		case ASPECT_16_9:
    764 			vres = (hres * 9)/16;
    765 			break;
    766 			/* Default only hit if compiler broken */
    767 		default:
    768 			vres = 0;
    769 		}
    770 		fprintf(outfile, "%d: %dx%d@%d\n",
    771 			i, hres, vres, 60 + (vinfo & 0x3f));
    772 	}
    773 
    774 	fprintf(outfile, "Descriptor blocks:\n");
    775 	for (i = 0; i < EDID_N_DTDS; i++)
    776 		show_edid_dtd(outfile,
    777 			      edid_data + (EDID_DTD_BASE + i * DTD_SIZE));
    778 	fprintf(outfile,
    779 		"EDID contains %d extensions\n",
    780 		edid_data[EDID_EXT_FLAG]);
    781 
    782 	edid_ext = edid_data + EDID_SIZE;
    783 
    784 	if ((ext >= 1) && (edid_data[EDID_EXT_FLAG] >= 1)) {
    785 		unsigned char eext_tag = edid_ext[EEXT_TAG];
    786 		if ((eext_tag == 0x02) && (edid_ext[EEXT_REV] == 0x03)) {
    787 			show_cea_timing(outfile, edid_ext);
    788 		} else {
    789 			char *tagtype;
    790 			switch (eext_tag) {
    791 			case 0x01:
    792 				tagtype = "LCD Timings";
    793 				break;
    794 			case 0x02:
    795 				tagtype = "CEA";
    796 				break;
    797 			case 0x20:
    798 				tagtype = "EDID 2.0";
    799 				break;
    800 			case 0x30:
    801 				tagtype = "Color Information";
    802 				break;
    803 			case 0x40:
    804 				tagtype = "DVI Feature";
    805 				break;
    806 			case 0x50:
    807 				tagtype = "Touch Screen Map";
    808 				break;
    809 			case 0xF0:
    810 				tagtype = "Block Map";
    811 				break;
    812 			case 0xFF:
    813 				tagtype = "Manufacturer";
    814 				break;
    815 			default:
    816 				tagtype = "Unknown";
    817 			}
    818 			fprintf(outfile,
    819 				"EDID %s ext tag 0x%02x rev 0x%02x skipped\n",
    820 				tagtype,
    821 				edid_ext[EEXT_TAG],
    822 				edid_ext[EEXT_REV]);
    823 		}
    824 	}
    825 }
    826 
    827 
    828 /* Pixel counts normally round to 8 */
    829 #define CLOSE_ENOUGH(a, b) (abs((a)-(b)) < 16)
    830 
    831 /* These match order of defines ASPECT_x_y in edid_utils.h */
    832 char *aspect_to_str[]={"16:10","4:3","5:4","16:9"};
    833 
    834 int find_aspect(int h, int v)
    835 {
    836 	if (CLOSE_ENOUGH((h * 3), (v * 4)))
    837 		return ASPECT_4_3;
    838 	if (CLOSE_ENOUGH((h * 4), (v * 5)))
    839 		return ASPECT_5_4;
    840 	if (CLOSE_ENOUGH((h * 9), (v * 16)))
    841 		return ASPECT_16_9;
    842 	if (CLOSE_ENOUGH((h * 10), (v * 16)))
    843 		return ASPECT_16_10;
    844 
    845 	return -1;
    846 }
    847 
    848 int find_aspect_fromisize(unsigned char *edid_data)
    849 {
    850 	int hsiz = edid_data[EDID_MAX_HSIZE];
    851 	int vsiz = edid_data[EDID_MAX_VSIZE];
    852 	int res;
    853 
    854 	/* Zero size for projector */
    855 	/* Only use this code if there was no preferred resolution */
    856 	/* So assume it is an older 4:3 projector not a video one  */
    857 	if ((hsiz == 0) && (vsiz == 0))
    858 		return ASPECT_4_3;
    859 
    860 	res = find_aspect(hsiz, vsiz);
    861 
    862 	/* If things didn't work out, assume the old 4:3 case */
    863 	if (res < 0)
    864 		return ASPECT_4_3;
    865 	else
    866 		return res;
    867 }
    868 
    869 int edid_get_monitor_name(const unsigned char *edid_data,
    870 			  char *buf,
    871 			  unsigned int buf_size)
    872 {
    873 	int i;
    874 	const unsigned char *dtd;
    875 
    876 	for (i = 0; i < EDID_N_DTDS; i++) {
    877 		dtd = edid_data + (EDID_DTD_BASE + i * DTD_SIZE);
    878 		if (dtd[DTD_PCLK_LO] == 0x00 && dtd[DTD_PCLK_HI] == 0x00 &&
    879 		    dtd[DTD_TYPETAG] == DTDTYPE_NAME) {
    880 			get_dtd_string((const char *)dtd + DTD_STRING,
    881 				       buf, buf_size);
    882 			return 0;
    883 		}
    884 	}
    885 
    886 	return -1;
    887 }
    888