• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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