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 */
show_edid_data(FILE * outfile,unsigned char * edid_data,int items,int base)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
get_test_edid(int n,unsigned char * dst)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
show_test_edid(FILE * outfile,int n)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
get_dtd_string(const char * str,char * buf,int buf_size)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) */
show_edid_dtd(FILE * outfile,unsigned char * base)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
show_audio_dbc(FILE * outfile,const unsigned char * edid_ext,int dbc)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
show_vendor_dbc(FILE * outfile,const unsigned char * edid_ext,int dbp)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
show_extended_dbc(FILE * outfile,const unsigned char * edid_ext,int dbc)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
show_cea_timing(FILE * outfile,unsigned char * edid_ext)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
edid_valid(const unsigned char * edid_data)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
edid_lpcm_support(const unsigned char * edid_data,int ext)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
edid_has_hdmi_info(const unsigned char * edid_data,int ext)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 */
show_edid(FILE * outfile,unsigned char * edid_data,int ext)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
find_aspect(int h,int v)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
find_aspect_fromisize(unsigned char * edid_data)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
edid_get_monitor_name(const unsigned char * edid_data,char * buf,unsigned int buf_size)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