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