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 */
12void 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
30unsigned 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
49unsigned 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
68unsigned 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
103unsigned 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
138unsigned 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 */
174unsigned 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
193static unsigned char *test_edids[N_TEST_EDIDS] = {
194	test_edid1, test_edid2, test_edid3, test_edid4, test_edid5,
195	test_edid6
196};
197
198int 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
206int 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
215static 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) */
228void 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
305char *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
312char *uscanstr[4] = {
313	"not supported",
314	"always overscan",
315	"always underscan",
316	"supports both over- and underscan",
317};
318
319static 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
368static 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
410static 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
458void 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
558int 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
570int 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
608done_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
618int 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 */
660void 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 */
832char *aspect_to_str[]={"16:10","4:3","5:4","16:9"};
833
834int 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
848int 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
869int 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