xref: /aosp_15_r20/external/edid-decode/edid-decode.cpp (revision 193032a37cc83cffc1526215991f3c21671f4245)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright 2006-2012 Red Hat, Inc.
4  * Copyright 2018-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
5  *
6  * Author: Adam Jackson <[email protected]>
7  * Maintainer: Hans Verkuil <[email protected]>
8  */
9 
10 #include <ctype.h>
11 #include <fcntl.h>
12 #include <getopt.h>
13 #include <math.h>
14 #include <stdarg.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 
19 #include "edid-decode.h"
20 
21 #define STR(x) #x
22 #define STRING(x) STR(x)
23 
24 static edid_state state;
25 
26 static unsigned char edid[EDID_PAGE_SIZE * EDID_MAX_BLOCKS];
27 static bool odd_hex_digits;
28 
29 enum output_format {
30 	OUT_FMT_DEFAULT,
31 	OUT_FMT_HEX,
32 	OUT_FMT_RAW,
33 	OUT_FMT_CARRAY,
34 	OUT_FMT_XML,
35 };
36 
37 /*
38  * Options
39  * Please keep in alphabetical order of the short option.
40  * That makes it easier to see which options are still free.
41  */
42 enum Option {
43 	OptCheck = 'c',
44 	OptCheckInline = 'C',
45 	OptFBModeTimings = 'F',
46 	OptHelp = 'h',
47 	OptOnlyHexDump = 'H',
48 	OptLongTimings = 'L',
49 	OptNativeTimings = 'n',
50 	OptOutputFormat = 'o',
51 	OptPreferredTimings = 'p',
52 	OptPhysicalAddress = 'P',
53 	OptSkipHexDump = 's',
54 	OptShortTimings = 'S',
55 	OptV4L2Timings = 'V',
56 	OptXModeLineTimings = 'X',
57 	OptSkipSHA = 128,
58 	OptHideSerialNumbers,
59 	OptVersion,
60 	OptSTD,
61 	OptDMT,
62 	OptVIC,
63 	OptHDMIVIC,
64 	OptCVT,
65 	OptGTF,
66 	OptListEstTimings,
67 	OptListDMTs,
68 	OptListVICs,
69 	OptListHDMIVICs,
70 	OptLast = 256
71 };
72 
73 static char options[OptLast];
74 
75 static struct option long_options[] = {
76 	{ "help", no_argument, 0, OptHelp },
77 	{ "output-format", required_argument, 0, OptOutputFormat },
78 	{ "native-timings", no_argument, 0, OptNativeTimings },
79 	{ "preferred-timings", no_argument, 0, OptPreferredTimings },
80 	{ "physical-address", no_argument, 0, OptPhysicalAddress },
81 	{ "skip-hex-dump", no_argument, 0, OptSkipHexDump },
82 	{ "only-hex-dump", no_argument, 0, OptOnlyHexDump },
83 	{ "skip-sha", no_argument, 0, OptSkipSHA },
84 	{ "hide-serial-numbers", no_argument, 0, OptHideSerialNumbers },
85 	{ "version", no_argument, 0, OptVersion },
86 	{ "check-inline", no_argument, 0, OptCheckInline },
87 	{ "check", no_argument, 0, OptCheck },
88 	{ "short-timings", no_argument, 0, OptShortTimings },
89 	{ "long-timings", no_argument, 0, OptLongTimings },
90 	{ "xmodeline", no_argument, 0, OptXModeLineTimings },
91 	{ "fbmode", no_argument, 0, OptFBModeTimings },
92 	{ "v4l2-timings", no_argument, 0, OptV4L2Timings },
93 	{ "std", required_argument, 0, OptSTD },
94 	{ "dmt", required_argument, 0, OptDMT },
95 	{ "vic", required_argument, 0, OptVIC },
96 	{ "hdmi-vic", required_argument, 0, OptHDMIVIC },
97 	{ "cvt", required_argument, 0, OptCVT },
98 	{ "gtf", required_argument, 0, OptGTF },
99 	{ "list-established-timings", no_argument, 0, OptListEstTimings },
100 	{ "list-dmts", no_argument, 0, OptListDMTs },
101 	{ "list-vics", no_argument, 0, OptListVICs },
102 	{ "list-hdmi-vics", no_argument, 0, OptListHDMIVICs },
103 	{ 0, 0, 0, 0 }
104 };
105 
usage(void)106 static void usage(void)
107 {
108 	printf("Usage: edid-decode <options> [in [out]]\n"
109 	       "  [in]                  EDID file to parse. Read from standard input if none given\n"
110 	       "                        or if the input filename is '-'.\n"
111 	       "  [out]                 Output the read EDID to this file. Write to standard output\n"
112 	       "                        if the output filename is '-'.\n"
113 	       "\nOptions:\n"
114 	       "  -o, --output-format <fmt>\n"
115 	       "                        If [out] is specified, then write the EDID in this format\n"
116 	       "                        <fmt> is one of:\n"
117 	       "                        hex:    hex numbers in ascii text (default for stdout)\n"
118 	       "                        raw:    binary data (default unless writing to stdout)\n"
119 	       "                        carray: c-program struct\n"
120 	       "                        xml:    XML data\n"
121 	       "  -c, --check           Check if the EDID conforms to the standards, failures and\n"
122 	       "                        warnings are reported at the end.\n"
123 	       "  -C, --check-inline    Check if the EDID conforms to the standards, failures and\n"
124 	       "                        warnings are reported inline.\n"
125 	       "  -n, --native-timings  Report the native timings.\n"
126 	       "  -p, --preferred-timings Report the preferred timings.\n"
127 	       "  -P, --physical-address Only report the CEC physical address.\n"
128 	       "  -S, --short-timings   Report all video timings in a short format.\n"
129 	       "  -L, --long-timings    Report all video timings in a long format.\n"
130 	       "  -X, --xmodeline       Report all long video timings in Xorg.conf format.\n"
131 	       "  -F, --fbmode          Report all long video timings in fb.modes format.\n"
132 	       "  -V, --v4l2-timings    Report all long video timings in v4l2-dv-timings.h format.\n"
133 	       "  -s, --skip-hex-dump   Skip the initial hex dump of the EDID.\n"
134 	       "  -H, --only-hex-dump   Only output the hex dump of the EDID.\n"
135 	       "  --skip-sha            Skip the SHA report.\n"
136 	       "  --hide-serial-numbers Replace serial numbers with '...'\n"
137 	       "  --version             show the edid-decode version (SHA)\n"
138 	       "  --std <byte1>,<byte2> Show the standard timing represented by these two bytes.\n"
139 	       "  --dmt <dmt>           Show the timings for the DMT with the given DMT ID.\n"
140 	       "  --vic <vic>           Show the timings for this VIC.\n"
141 	       "  --hdmi-vic <hdmivic>  Show the timings for this HDMI VIC.\n"
142 	       "  --cvt w=<width>,h=<height>,fps=<fps>[,rb=<rb>][,interlaced][,overscan][,alt][,hblank=<hblank]\n"
143 	       "                        Calculate the CVT timings for the given format.\n"
144 	       "                        <fps> is frames per second for progressive timings,\n"
145 	       "                        or fields per second for interlaced timings.\n"
146 	       "                        <rb> can be 0 (no reduced blanking, default), or\n"
147 	       "                        1-3 for the reduced blanking version.\n"
148 	       "                        If 'interlaced' is given, then this is an interlaced format.\n"
149 	       "                        If 'overscan' is given, then this is an overscanned format.\n"
150 	       "                        If 'alt' is given and <rb>=2, then report the timings\n"
151 	       "                        optimized for video: 1000 / 1001 * <fps>.\n"
152 	       "                        If 'alt' is given and <rb>=3, then the horizontal blanking\n"
153 	       "                        is 160 instead of 80 pixels.\n"
154 	       "                        If 'hblank' is given and <rb>=3, then the horizontal blanking\n"
155 	       "                        is <hblank> pixels (range of 80-200), overriding 'alt'.\n"
156 	       "  --gtf w=<width>,h=<height>[,fps=<fps>][,horfreq=<horfreq>][,pixclk=<pixclk>][,interlaced]\n"
157 	       "        [,overscan][,secondary][,C=<c>][,M=<m>][,K=<k>][,J=<j>]\n"
158 	       "                        Calculate the GTF timings for the given format.\n"
159 	       "                        <fps> is frames per second for progressive timings,\n"
160 	       "                        or fields per second for interlaced timings.\n"
161 	       "                        <horfreq> is the horizontal frequency in kHz.\n"
162 	       "                        <pixclk> is the pixel clock frequency in MHz.\n"
163 	       "                        Only one of fps, horfreq or pixclk must be given.\n"
164 	       "                        If 'interlaced' is given, then this is an interlaced format.\n"
165 	       "                        If 'overscan' is given, then this is an overscanned format.\n"
166 	       "                        If 'secondary' is given, then the secondary GTF is used for\n"
167 	       "                        reduced blanking, where <c>, <m>, <k> and <j> are parameters\n"
168 	       "                        for the secondary curve.\n"
169 	       "  --list-established-timings List all known Established Timings.\n"
170 	       "  --list-dmts           List all known DMTs.\n"
171 	       "  --list-vics           List all known VICs.\n"
172 	       "  --list-hdmi-vics      List all known HDMI VICs.\n"
173 	       "  -h, --help            Display this help message.\n");
174 }
175 
176 static std::string s_msgs[EDID_MAX_BLOCKS + 1][2];
177 
msg(bool is_warn,const char * fmt,...)178 void msg(bool is_warn, const char *fmt, ...)
179 {
180 	char buf[1024] = "";
181 	va_list ap;
182 
183 	va_start(ap, fmt);
184 	vsprintf(buf, fmt, ap);
185 	va_end(ap);
186 
187 	if (is_warn)
188 		state.warnings++;
189 	else
190 		state.failures++;
191 	if (state.data_block.empty())
192 		s_msgs[state.block_nr][is_warn] += std::string("  ") + buf;
193 	else
194 		s_msgs[state.block_nr][is_warn] += "  " + state.data_block + ": " + buf;
195 
196 	if (options[OptCheckInline])
197 		printf("%s: %s", is_warn ? "WARN" : "FAIL", buf);
198 }
199 
show_msgs(bool is_warn)200 static void show_msgs(bool is_warn)
201 {
202 	printf("\n%s:\n\n", is_warn ? "Warnings" : "Failures");
203 	for (unsigned i = 0; i < state.num_blocks; i++) {
204 		if (s_msgs[i][is_warn].empty())
205 			continue;
206 		printf("Block %u, %s:\n%s",
207 		       i, block_name(edid[i * EDID_PAGE_SIZE]).c_str(),
208 		       s_msgs[i][is_warn].c_str());
209 	}
210 	if (s_msgs[EDID_MAX_BLOCKS][is_warn].empty())
211 		return;
212 	printf("EDID:\n%s",
213 	       s_msgs[EDID_MAX_BLOCKS][is_warn].c_str());
214 }
215 
216 
do_checksum(const char * prefix,const unsigned char * x,size_t len)217 void do_checksum(const char *prefix, const unsigned char *x, size_t len)
218 {
219 	unsigned char check = x[len - 1];
220 	unsigned char sum = 0;
221 	unsigned i;
222 
223 	printf("%sChecksum: 0x%02hhx", prefix, check);
224 
225 	for (i = 0; i < len-1; i++)
226 		sum += x[i];
227 
228 	if ((unsigned char)(check + sum) != 0) {
229 		printf(" (should be 0x%02x)\n", -sum & 0xff);
230 		fail("Invalid checksum 0x%02x (should be 0x%02x).\n",
231 		     check, -sum & 0xff);
232 		return;
233 	}
234 	printf("\n");
235 }
236 
gcd(unsigned a,unsigned b)237 static unsigned gcd(unsigned a, unsigned b)
238 {
239 	while (b) {
240 		unsigned t = b;
241 
242 		b = a % b;
243 		a = t;
244 	}
245 	return a;
246 }
247 
calc_ratio(struct timings * t)248 void calc_ratio(struct timings *t)
249 {
250 	unsigned d = gcd(t->hact, t->vact);
251 
252 	if (d == 0) {
253 		t->hratio = t->vratio = 0;
254 		return;
255 	}
256 	t->hratio = t->hact / d;
257 	t->vratio = t->vact / d;
258 }
259 
dtd_type(unsigned cnt)260 std::string edid_state::dtd_type(unsigned cnt)
261 {
262 	unsigned len = std::to_string(cta.preparsed_total_dtds).length();
263 	char buf[16];
264 	sprintf(buf, "DTD %*u", len, cnt);
265 	return buf;
266 }
267 
match_timings(const timings & t1,const timings & t2)268 bool edid_state::match_timings(const timings &t1, const timings &t2)
269 {
270 	if (t1.hact != t2.hact ||
271 	    t1.vact != t2.vact ||
272 	    t1.rb != t2.rb ||
273 	    t1.interlaced != t2.interlaced ||
274 	    t1.hfp != t2.hfp ||
275 	    t1.hbp != t2.hbp ||
276 	    t1.hsync != t2.hsync ||
277 	    t1.pos_pol_hsync != t2.pos_pol_hsync ||
278 	    t1.hratio != t2.hratio ||
279 	    t1.vfp != t2.vfp ||
280 	    t1.vbp != t2.vbp ||
281 	    t1.vsync != t2.vsync ||
282 	    t1.pos_pol_vsync != t2.pos_pol_vsync ||
283 	    t1.vratio != t2.vratio ||
284 	    t1.pixclk_khz != t2.pixclk_khz)
285 		return false;
286 	return true;
287 }
288 
or_str(std::string & s,const std::string & flag,unsigned & num_flags)289 static void or_str(std::string &s, const std::string &flag, unsigned &num_flags)
290 {
291 	if (!num_flags)
292 		s = flag;
293 	else if (num_flags % 2 == 0)
294 		s = s + " | \\\n\t\t" + flag;
295 	else
296 		s = s + " | " + flag;
297 	num_flags++;
298 }
299 
300 /*
301  * Return true if the timings are a close, but not identical,
302  * match. The only differences allowed are polarities and
303  * porches and syncs, provided the total blanking remains the
304  * same.
305  */
timings_close_match(const timings & t1,const timings & t2)306 bool timings_close_match(const timings &t1, const timings &t2)
307 {
308 	// We don't want to deal with borders, you're on your own
309 	// if you are using those.
310 	if (t1.hborder || t1.vborder ||
311 	    t2.hborder || t2.vborder)
312 		return false;
313 	if (t1.hact != t2.hact || t1.vact != t2.vact ||
314 	    t1.interlaced != t2.interlaced ||
315 	    t1.pixclk_khz != t2.pixclk_khz ||
316 	    t1.hfp + t1.hsync + t1.hbp != t2.hfp + t2.hsync + t2.hbp ||
317 	    t1.vfp + t1.vsync + t1.vbp != t2.vfp + t2.vsync + t2.vbp)
318 		return false;
319 	if (t1.hfp == t2.hfp &&
320 	    t1.hsync == t2.hsync &&
321 	    t1.hbp == t2.hbp &&
322 	    t1.pos_pol_hsync == t2.pos_pol_hsync &&
323 	    t1.vfp == t2.vfp &&
324 	    t1.vsync == t2.vsync &&
325 	    t1.vbp == t2.vbp &&
326 	    t1.pos_pol_vsync == t2.pos_pol_vsync)
327 		return false;
328 	return true;
329 }
330 
print_modeline(unsigned indent,const struct timings * t,double refresh)331 static void print_modeline(unsigned indent, const struct timings *t, double refresh)
332 {
333 	unsigned offset = (!t->even_vtotal && t->interlaced) ? 1 : 0;
334 	unsigned hfp = t->hborder + t->hfp;
335 	unsigned hbp = t->hborder + t->hbp;
336 	unsigned vfp = t->vborder + t->vfp;
337 	unsigned vbp = t->vborder + t->vbp;
338 
339 	printf("%*sModeline \"%ux%u_%.2f%s\" %.3f  %u %u %u %u  %u %u %u %u  %cHSync",
340 	       indent, "",
341 	       t->hact, t->vact, refresh,
342 	       t->interlaced ? "i" : "", t->pixclk_khz / 1000.0,
343 	       t->hact, t->hact + hfp, t->hact + hfp + t->hsync,
344 	       t->hact + hfp + t->hsync + hbp,
345 	       t->vact, t->vact + vfp, t->vact + vfp + t->vsync,
346 	       t->vact + vfp + t->vsync + vbp + offset,
347 	       t->pos_pol_hsync ? '+' : '-');
348 	if (!t->no_pol_vsync)
349 		printf(" %cVSync", t->pos_pol_vsync ? '+' : '-');
350 	if (t->interlaced)
351 		printf(" Interlace");
352 	printf("\n");
353 }
354 
print_fbmode(unsigned indent,const struct timings * t,double refresh,double hor_freq_khz)355 static void print_fbmode(unsigned indent, const struct timings *t,
356 			 double refresh, double hor_freq_khz)
357 {
358 	printf("%*smode \"%ux%u-%u%s\"\n",
359 	       indent, "",
360 	       t->hact, t->vact,
361 	       (unsigned)(0.5 + (t->interlaced ? refresh / 2.0 : refresh)),
362 	       t->interlaced ? "-lace" : "");
363 	printf("%*s# D: %.2f MHz, H: %.3f kHz, V: %.2f Hz\n",
364 	       indent + 8, "",
365 	       t->pixclk_khz / 1000.0, hor_freq_khz, refresh);
366 	printf("%*sgeometry %u %u %u %u 32\n",
367 	       indent + 8, "",
368 	       t->hact, t->vact, t->hact, t->vact);
369 	unsigned mult = t->interlaced ? 2 : 1;
370 	unsigned offset = !t->even_vtotal && t->interlaced;
371 	unsigned hfp = t->hborder + t->hfp;
372 	unsigned hbp = t->hborder + t->hbp;
373 	unsigned vfp = t->vborder + t->vfp;
374 	unsigned vbp = t->vborder + t->vbp;
375 	printf("%*stimings %llu %d %d %d %u %u %u\n",
376 	       indent + 8, "",
377 	       (unsigned long long)(1000000000.0 / (double)(t->pixclk_khz) + 0.5),
378 	       hbp, hfp, mult * vbp, mult * vfp + offset, t->hsync, mult * t->vsync);
379 	if (t->interlaced)
380 		printf("%*slaced true\n", indent + 8, "");
381 	if (t->pos_pol_hsync)
382 		printf("%*shsync high\n", indent + 8, "");
383 	if (t->pos_pol_vsync)
384 		printf("%*svsync high\n", indent + 8, "");
385 	printf("%*sendmode\n", indent, "");
386 }
387 
print_v4l2_timing(const struct timings * t,double refresh,const char * type)388 static void print_v4l2_timing(const struct timings *t,
389 			      double refresh, const char *type)
390 {
391 	printf("\t#define V4L2_DV_BT_%uX%u%c%u_%02u { \\\n",
392 	       t->hact, t->vact, t->interlaced ? 'I' : 'P',
393 	       (unsigned)refresh, (unsigned)(0.5 + 100.0 * (refresh - (unsigned)refresh)));
394 	printf("\t\t.type = V4L2_DV_BT_656_1120, \\\n");
395 	printf("\t\tV4L2_INIT_BT_TIMINGS(%u, %u, %u, ",
396 	       t->hact, t->vact, t->interlaced);
397 	if (!t->pos_pol_hsync && !t->pos_pol_vsync)
398 		printf("0, \\\n");
399 	else if (t->pos_pol_hsync && t->pos_pol_vsync)
400 		printf("\\\n\t\t\tV4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \\\n");
401 	else if (t->pos_pol_hsync)
402 		printf("V4L2_DV_HSYNC_POS_POL, \\\n");
403 	else
404 		printf("V4L2_DV_VSYNC_POS_POL, \\\n");
405 	unsigned hfp = t->hborder + t->hfp;
406 	unsigned hbp = t->hborder + t->hbp;
407 	unsigned vfp = t->vborder + t->vfp;
408 	unsigned vbp = t->vborder + t->vbp;
409 	printf("\t\t\t%lluULL, %d, %u, %d, %u, %u, %d, %u, %u, %d, \\\n",
410 	       t->pixclk_khz * 1000ULL, hfp, t->hsync, hbp,
411 	       vfp, t->vsync, vbp,
412 	       t->interlaced ? vfp : 0,
413 	       t->interlaced ? t->vsync : 0,
414 	       t->interlaced ? vbp + !t->even_vtotal : 0);
415 
416 	std::string flags;
417 	unsigned num_flags = 0;
418 	unsigned vic = 0;
419 	unsigned hdmi_vic = 0;
420 	const char *std = "0";
421 
422 	if (t->interlaced && !t->even_vtotal)
423 		or_str(flags, "V4L2_DV_FL_HALF_LINE", num_flags);
424 	if (!memcmp(type, "VIC", 3)) {
425 		or_str(flags, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags);
426 		or_str(flags, "V4L2_DV_FL_IS_CE_VIDEO", num_flags);
427 		vic = strtoul(type + 4, 0, 0);
428 	}
429 	if (!memcmp(type, "HDMI VIC", 8)) {
430 		or_str(flags, "V4L2_DV_FL_HAS_HDMI_VIC", num_flags);
431 		or_str(flags, "V4L2_DV_FL_IS_CE_VIDEO", num_flags);
432 		hdmi_vic = strtoul(type + 9, 0, 0);
433 		vic = hdmi_vic_to_vic(hdmi_vic);
434 		if (vic)
435 			or_str(flags, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags);
436 	}
437 	if (vic && (fmod(refresh, 6)) == 0.0)
438 		or_str(flags, "V4L2_DV_FL_CAN_REDUCE_FPS", num_flags);
439 	if (t->rb)
440 		or_str(flags, "V4L2_DV_FL_REDUCED_BLANKING", num_flags);
441 	if (t->hratio && t->vratio)
442 		or_str(flags, "V4L2_DV_FL_HAS_PICTURE_ASPECT", num_flags);
443 
444 	if (!memcmp(type, "VIC", 3) || !memcmp(type, "HDMI VIC", 8))
445 		std = "V4L2_DV_BT_STD_CEA861";
446 	else if (!memcmp(type, "DMT", 3))
447 		std = "V4L2_DV_BT_STD_DMT";
448 	else if (!memcmp(type, "CVT", 3))
449 		std = "V4L2_DV_BT_STD_CVT";
450 	else if (!memcmp(type, "GTF", 3))
451 		std = "V4L2_DV_BT_STD_GTF";
452 	printf("\t\t\t%s, \\\n", std);
453 	printf("\t\t\t%s, \\\n", flags.empty() ? "0" : flags.c_str());
454 	printf("\t\t\t{ %u, %u }, %u, %u) \\\n",
455 	       t->hratio, t->vratio, vic, hdmi_vic);
456 	printf("\t}\n");
457 }
458 
print_detailed_timing(unsigned indent,const struct timings * t)459 static void print_detailed_timing(unsigned indent, const struct timings *t)
460 {
461 	printf("%*sHfront %4d Hsync %3u Hback %3d Hpol %s",
462 	       indent, "",
463 	       t->hfp, t->hsync, t->hbp, t->pos_pol_hsync ? "P" : "N");
464 	if (t->hborder)
465 		printf(" Hborder %u", t->hborder);
466 	printf("\n");
467 
468 	printf("%*sVfront %4u Vsync %3u Vback %3d",
469 	       indent, "", t->vfp, t->vsync, t->vbp);
470 	if (!t->no_pol_vsync)
471 		printf(" Vpol %s", t->pos_pol_vsync ? "P" : "N");
472 	if (t->vborder)
473 		printf(" Vborder %u", t->vborder);
474 	if (t->even_vtotal) {
475 		printf(" Both Fields");
476 	} else if (t->interlaced) {
477 		printf(" Vfront +0.5 Odd Field\n");
478 		printf("%*sVfront %4d Vsync %3u Vback %3d",
479 		       indent, "", t->vfp, t->vsync, t->vbp);
480 		if (!t->no_pol_vsync)
481 			printf(" Vpol %s", t->pos_pol_vsync ? "P" : "N");
482 		if (t->vborder)
483 			printf(" Vborder %u", t->vborder);
484 		printf(" Vback  +0.5 Even Field");
485 	}
486 	printf("\n");
487 }
488 
print_timings(const char * prefix,const struct timings * t,const char * type,const char * flags,bool detailed,bool do_checks)489 bool edid_state::print_timings(const char *prefix, const struct timings *t,
490 			       const char *type, const char *flags,
491 			       bool detailed, bool do_checks)
492 {
493 	if (!t) {
494 		// Should not happen
495 		if (do_checks)
496 			fail("Unknown video timings.\n");
497 		return false;
498 	}
499 
500 	if (detailed && options[OptShortTimings])
501 		detailed = false;
502 	if (options[OptLongTimings])
503 		detailed = true;
504 
505 	unsigned vact = t->vact;
506 	unsigned hbl = t->hfp + t->hsync + t->hbp + 2 * t->hborder;
507 	unsigned vbl = t->vfp + t->vsync + t->vbp + 2 * t->vborder;
508 	unsigned htotal = t->hact + hbl;
509 	double hor_freq_khz = htotal ? (double)t->pixclk_khz / htotal : 0;
510 
511 	if (t->interlaced)
512 		vact /= 2;
513 
514 	if (t->ycbcr420)
515 		hor_freq_khz /= 2;
516 
517 	double vtotal = vact + vbl;
518 
519 	bool ok = true;
520 
521 	if (!t->hact || !hbl || !t->hfp || !t->hsync ||
522 	    !vact || !vbl || (!t->vfp && !t->interlaced && !t->even_vtotal) || !t->vsync) {
523 		if (do_checks)
524 			fail("0 values in the video timing:\n"
525 			     "    Horizontal Active/Blanking %u/%u\n"
526 			     "    Horizontal Frontporch/Sync Width %u/%u\n"
527 			     "    Vertical Active/Blanking %u/%u\n"
528 			     "    Vertical Frontporch/Sync Width %u/%u\n",
529 			     t->hact, hbl, t->hfp, t->hsync, vact, vbl, t->vfp, t->vsync);
530 		ok = false;
531 	}
532 
533 	if (t->even_vtotal)
534 		vtotal = vact + t->vfp + t->vsync + t->vbp;
535 	else if (t->interlaced)
536 		vtotal = vact + t->vfp + t->vsync + t->vbp + 0.5;
537 
538 	double refresh = (double)t->pixclk_khz * 1000.0 / (htotal * vtotal);
539 
540 	std::string s;
541 	unsigned rb = t->rb & ~RB_ALT;
542 	if (rb) {
543 		bool alt = t->rb & RB_ALT;
544 		s = "RB";
545 		if (rb == RB_CVT_V2)
546 			s += std::string("v2") + (alt ? ",video-optimized" : "");
547 		else if (rb == RB_CVT_V3)
548 			s += std::string("v3") + (alt ? ",h-blank-160" : "");
549 	}
550 	add_str(s, flags);
551 	if (t->hsize_mm || t->vsize_mm)
552 		add_str(s, std::to_string(t->hsize_mm) + " mm x " + std::to_string(t->vsize_mm) + " mm");
553 	if (t->hsize_mm > dtd_max_hsize_mm)
554 		dtd_max_hsize_mm = t->hsize_mm;
555 	if (t->vsize_mm > dtd_max_vsize_mm)
556 		dtd_max_vsize_mm = t->vsize_mm;
557 	if (!s.empty())
558 		s = " (" + s + ")";
559 	unsigned pixclk_khz = t->pixclk_khz / (t->ycbcr420 ? 2 : 1);
560 
561 	char buf[10];
562 
563 	sprintf(buf, "%u%s", t->vact, t->interlaced ? "i" : "");
564 	printf("%s%s: %5ux%-5s %7.3f Hz %3u:%-3u %7.3f kHz %8.3f MHz%s\n",
565 	       prefix, type,
566 	       t->hact, buf,
567 	       refresh,
568 	       t->hratio, t->vratio,
569 	       hor_freq_khz,
570 	       pixclk_khz / 1000.0,
571 	       s.c_str());
572 
573 	unsigned len = strlen(prefix) + 2;
574 
575 	if (!t->ycbcr420 && detailed && options[OptXModeLineTimings])
576 		print_modeline(len, t, refresh);
577 	else if (!t->ycbcr420 && detailed && options[OptFBModeTimings])
578 		print_fbmode(len, t, refresh, hor_freq_khz);
579 	else if (!t->ycbcr420 && detailed && options[OptV4L2Timings])
580 		print_v4l2_timing(t, refresh, type);
581 	else if (detailed)
582 		print_detailed_timing(len + strlen(type) + 6, t);
583 
584 	if (!do_checks)
585 		return ok;
586 
587 	if (!memcmp(type, "DTD", 3)) {
588 		unsigned vic, dmt;
589 		const timings *vic_t = cta_close_match_to_vic(*t, vic);
590 
591 		if (vic_t)
592 			warn("DTD is similar but not identical to VIC %u.\n", vic);
593 
594 		const timings *dmt_t = close_match_to_dmt(*t, dmt);
595 		if (!vic_t && dmt_t)
596 			warn("DTD is similar but not identical to DMT 0x%02x.\n", dmt);
597 	}
598 
599 	if (refresh) {
600 		min_vert_freq_hz = min(min_vert_freq_hz, refresh);
601 		max_vert_freq_hz = max(max_vert_freq_hz, refresh);
602 	}
603 	if (hor_freq_khz) {
604 		min_hor_freq_hz = min(min_hor_freq_hz, hor_freq_khz * 1000.0);
605 		max_hor_freq_hz = max(max_hor_freq_hz, hor_freq_khz * 1000.0);
606 		max_pixclk_khz = max(max_pixclk_khz, pixclk_khz);
607 		if (t->pos_pol_hsync && !t->pos_pol_vsync && t->vsync == 3)
608 			base.max_pos_neg_hor_freq_khz = hor_freq_khz;
609 	}
610 
611 	if (t->ycbcr420 && t->pixclk_khz < 590000)
612 		warn_once("Some YCbCr 4:2:0 timings are invalid for HDMI (which requires an RGB timings pixel rate >= 590 MHz).\n");
613 	if (t->hfp <= 0)
614 		fail("0 or negative horizontal front porch.\n");
615 	if (t->hbp <= 0)
616 		fail("0 or negative horizontal back porch.\n");
617 	if (t->vbp <= 0)
618 		fail("0 or negative vertical back porch.\n");
619 	if (!base.max_display_width_mm && !base.max_display_height_mm) {
620 		/* this is valid */
621 	} else if (!t->hsize_mm && !t->vsize_mm) {
622 		/* this is valid */
623 	} else if (t->hsize_mm > base.max_display_width_mm + 9 ||
624 		   t->vsize_mm > base.max_display_height_mm + 9) {
625 		fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
626 		     t->hsize_mm, t->vsize_mm, base.max_display_width_mm, base.max_display_height_mm);
627 	} else if (t->hsize_mm < base.max_display_width_mm - 9 &&
628 		   t->vsize_mm < base.max_display_height_mm - 9) {
629 		fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
630 		     t->hsize_mm, t->vsize_mm, base.max_display_width_mm, base.max_display_height_mm);
631 	}
632 	return ok;
633 }
634 
containerid2s(const unsigned char * x)635 std::string containerid2s(const unsigned char *x)
636 {
637 	char buf[40];
638 
639 	sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
640 		x[0], x[1], x[2], x[3],
641 		x[4], x[5],
642 		x[6], x[7],
643 		x[8], x[9],
644 		x[10], x[11], x[12], x[13], x[14], x[15]);
645 	return buf;
646 }
647 
utohex(unsigned char x)648 std::string utohex(unsigned char x)
649 {
650 	char buf[10];
651 
652 	sprintf(buf, "0x%02hhx", x);
653 	return buf;
654 }
655 
oui_name(unsigned oui,bool reverse)656 const char *oui_name(unsigned oui, bool reverse)
657 {
658 	if (reverse)
659 		oui = (oui >> 16) | (oui & 0xff00) | ((oui & 0xff) << 16);
660 
661 	switch (oui) {
662 	case 0x00001a: return "AMD";
663 	case 0x000c03: return "HDMI";
664 	case 0x00044b: return "NVIDIA";
665 	case 0x000c6e: return "ASUS";
666 	case 0x0010fa: return "Apple";
667 	case 0x0014b9: return "MSTAR";
668 	case 0x00d046: return "Dolby";
669 	case 0x00e047: return "InFocus";
670 	case 0x3a0292: return "VESA";
671 	case 0x90848b: return "HDR10+";
672 	case 0xc45dd8: return "HDMI Forum";
673 	case 0xca125c: return "Microsoft";
674 	default: return NULL;
675 	}
676 }
677 
ouitohex(unsigned oui)678 std::string ouitohex(unsigned oui)
679 {
680 	char buf[32];
681 
682 	sprintf(buf, "%02X-%02X-%02X", (oui >> 16) & 0xff, (oui >> 8) & 0xff, oui & 0xff);
683 	return buf;
684 }
685 
memchk(const unsigned char * x,unsigned len,unsigned char v)686 bool memchk(const unsigned char *x, unsigned len, unsigned char v)
687 {
688 	for (unsigned i = 0; i < len; i++)
689 		if (x[i] != v)
690 			return false;
691 	return true;
692 }
693 
hex_block(const char * prefix,const unsigned char * x,unsigned length,bool show_ascii,unsigned step)694 void hex_block(const char *prefix, const unsigned char *x,
695 	       unsigned length, bool show_ascii, unsigned step)
696 {
697 	unsigned i, j;
698 
699 	if (!length)
700 		return;
701 
702 	for (i = 0; i < length; i += step) {
703 		unsigned len = min(step, length - i);
704 
705 		printf("%s", prefix);
706 		for (j = 0; j < len; j++)
707 			printf("%s%02x", j ? " " : "", x[i + j]);
708 
709 		if (show_ascii) {
710 			for (j = len; j < step; j++)
711 				printf("   ");
712 			printf(" '");
713 			for (j = 0; j < len; j++)
714 				printf("%c", x[i + j] >= ' ' && x[i + j] <= '~' ? x[i + j] : '.');
715 			printf("'");
716 		}
717 		printf("\n");
718 	}
719 }
720 
edid_add_byte(const char * s,bool two_digits=true)721 static bool edid_add_byte(const char *s, bool two_digits = true)
722 {
723 	char buf[3];
724 
725 	if (state.edid_size == sizeof(edid))
726 		return false;
727 	buf[0] = s[0];
728 	buf[1] = two_digits ? s[1] : 0;
729 	buf[2] = 0;
730 	edid[state.edid_size++] = strtoul(buf, NULL, 16);
731 	return true;
732 }
733 
extract_edid_quantumdata(const char * start)734 static bool extract_edid_quantumdata(const char *start)
735 {
736 	/* Parse QuantumData 980 EDID files */
737 	do {
738 		start = strstr(start, ">");
739 		if (!start)
740 			return false;
741 		start++;
742 		for (unsigned i = 0; start[i] && start[i + 1] && i < 256; i += 2)
743 			if (!edid_add_byte(start + i))
744 				return false;
745 		start = strstr(start, "<BLOCK");
746 	} while (start);
747 	return state.edid_size;
748 }
749 
750 static const char *ignore_chars = ",:;";
751 
extract_edid_hex(const char * s,bool require_two_digits=true)752 static bool extract_edid_hex(const char *s, bool require_two_digits = true)
753 {
754 	for (; *s; s++) {
755 		if (isspace(*s) || strchr(ignore_chars, *s))
756 			continue;
757 
758 		if (*s == '0' && tolower(s[1]) == 'x') {
759 			s++;
760 			continue;
761 		}
762 
763 		/* Read one or two hex digits from the log */
764 		if (!isxdigit(s[0])) {
765 			if (state.edid_size && state.edid_size % 128 == 0)
766 				break;
767 			return false;
768 		}
769 		if (require_two_digits && !isxdigit(s[1])) {
770 			odd_hex_digits = true;
771 			return false;
772 		}
773 		if (!edid_add_byte(s, isxdigit(s[1])))
774 			return false;
775 		if (isxdigit(s[1]))
776 			s++;
777 	}
778 	return state.edid_size;
779 }
780 
extract_edid_xrandr(const char * start)781 static bool extract_edid_xrandr(const char *start)
782 {
783 	static const char indentation1[] = "                ";
784 	static const char indentation2[] = "\t\t";
785 	/* Used to detect that we've gone past the EDID property */
786 	static const char half_indentation1[] = "        ";
787 	static const char half_indentation2[] = "\t";
788 	const char *indentation;
789 	const char *s;
790 
791 	for (;;) {
792 		unsigned j;
793 
794 		/* Get the next start of the line of EDID hex, assuming spaces for indentation */
795 		s = strstr(start, indentation = indentation1);
796 		/* Did we skip the start of another property? */
797 		if (s && s > strstr(start, half_indentation1))
798 			break;
799 
800 		/* If we failed, retry assuming tabs for indentation */
801 		if (!s) {
802 			s = strstr(start, indentation = indentation2);
803 			/* Did we skip the start of another property? */
804 			if (s && s > strstr(start, half_indentation2))
805 				break;
806 		}
807 
808 		if (!s)
809 			break;
810 
811 		start = s + strlen(indentation);
812 
813 		for (j = 0; j < 16; j++, start += 2) {
814 			/* Read a %02x from the log */
815 			if (!isxdigit(start[0]) || !isxdigit(start[1])) {
816 				if (j)
817 					break;
818 				return false;
819 			}
820 			if (!edid_add_byte(start))
821 				return false;
822 		}
823 	}
824 	return state.edid_size;
825 }
826 
extract_edid_xorg(const char * start)827 static bool extract_edid_xorg(const char *start)
828 {
829 	bool find_first_num = true;
830 
831 	for (; *start; start++) {
832 		if (find_first_num) {
833 			const char *s;
834 
835 			/* skip ahead to the : */
836 			s = strstr(start, ": \t");
837 			if (!s)
838 				s = strstr(start, ":     ");
839 			if (!s)
840 				break;
841 			start = s;
842 			/* and find the first number */
843 			while (!isxdigit(start[1]))
844 				start++;
845 			find_first_num = false;
846 			continue;
847 		} else {
848 			/* Read a %02x from the log */
849 			if (!isxdigit(*start)) {
850 				find_first_num = true;
851 				continue;
852 			}
853 			if (!edid_add_byte(start))
854 				return false;
855 			start++;
856 		}
857 	}
858 	return state.edid_size;
859 }
860 
extract_edid(int fd,FILE * error)861 static bool extract_edid(int fd, FILE *error)
862 {
863 	std::vector<char> edid_data;
864 	char buf[EDID_PAGE_SIZE];
865 
866 	for (;;) {
867 		ssize_t i = read(fd, buf, sizeof(buf));
868 
869 		if (i < 0)
870 			return false;
871 		if (i == 0)
872 			break;
873 		edid_data.insert(edid_data.end(), buf, buf + i);
874 	}
875 
876 	if (edid_data.empty()) {
877 		state.edid_size = 0;
878 		return false;
879 	}
880 
881 	const char *data = &edid_data[0];
882 	const char *start;
883 
884 	/* Look for edid-decode output */
885 	start = strstr(data, "EDID (hex):");
886 	if (!start)
887 		start = strstr(data, "edid-decode (hex):");
888 	if (start)
889 		return extract_edid_hex(strchr(start, ':'));
890 
891 	/* Look for C-array */
892 	start = strstr(data, "unsigned char edid[] = {");
893 	if (start)
894 		return extract_edid_hex(strchr(start, '{') + 1, false);
895 
896 	/* Look for QuantumData EDID output */
897 	start = strstr(data, "<BLOCK");
898 	if (start)
899 		return extract_edid_quantumdata(start);
900 
901 	/* Look for xrandr --verbose output (lines of 16 hex bytes) */
902 	start = strstr(data, "EDID_DATA:");
903 	if (!start)
904 		start = strstr(data, "EDID:");
905 	if (start)
906 		return extract_edid_xrandr(start);
907 
908 	/* Look for an EDID in an Xorg.0.log file */
909 	start = strstr(data, "EDID (in hex):");
910 	if (start)
911 		start = strstr(start, "(II)");
912 	if (start)
913 		return extract_edid_xorg(start);
914 
915 	unsigned i;
916 
917 	/* Is the EDID provided in hex? */
918 	for (i = 0; i < 32 && (isspace(data[i]) || strchr(ignore_chars, data[i]) ||
919 			       tolower(data[i]) == 'x' || isxdigit(data[i])); i++);
920 
921 	if (i == 32)
922 		return extract_edid_hex(data);
923 
924 	/* Assume binary */
925 	if (edid_data.size() > sizeof(edid)) {
926 		fprintf(error, "Binary EDID length %zu is greater than %zu.\n",
927 			edid_data.size(), sizeof(edid));
928 		return false;
929 	}
930 	memcpy(edid, data, edid_data.size());
931 	state.edid_size = edid_data.size();
932 	return true;
933 }
934 
crc_calc(const unsigned char * b)935 static unsigned char crc_calc(const unsigned char *b)
936 {
937 	unsigned char sum = 0;
938 	unsigned i;
939 
940 	for (i = 0; i < 127; i++)
941 		sum += b[i];
942 	return 256 - sum;
943 }
944 
crc_ok(const unsigned char * b)945 static int crc_ok(const unsigned char *b)
946 {
947 	return crc_calc(b) == b[127];
948 }
949 
hexdumpedid(FILE * f,const unsigned char * edid,unsigned size)950 static void hexdumpedid(FILE *f, const unsigned char *edid, unsigned size)
951 {
952 	unsigned b, i, j;
953 
954 	for (b = 0; b < size / 128; b++) {
955 		const unsigned char *buf = edid + 128 * b;
956 
957 		if (b)
958 			fprintf(f, "\n");
959 		for (i = 0; i < 128; i += 0x10) {
960 			fprintf(f, "%02x", buf[i]);
961 			for (j = 1; j < 0x10; j++) {
962 				fprintf(f, " %02x", buf[i + j]);
963 			}
964 			fprintf(f, "\n");
965 		}
966 		if (!crc_ok(buf))
967 			fprintf(f, "Block %u has a checksum error (should be 0x%02x).\n",
968 				b, crc_calc(buf));
969 	}
970 }
971 
carraydumpedid(FILE * f,const unsigned char * edid,unsigned size)972 static void carraydumpedid(FILE *f, const unsigned char *edid, unsigned size)
973 {
974 	unsigned b, i, j;
975 
976 	fprintf(f, "const unsigned char edid[] = {\n");
977 	for (b = 0; b < size / 128; b++) {
978 		const unsigned char *buf = edid + 128 * b;
979 
980 		if (b)
981 			fprintf(f, "\n");
982 		for (i = 0; i < 128; i += 8) {
983 			fprintf(f, "\t0x%02x,", buf[i]);
984 			for (j = 1; j < 8; j++) {
985 				fprintf(f, " 0x%02x,", buf[i + j]);
986 			}
987 			fprintf(f, "\n");
988 		}
989 		if (!crc_ok(buf))
990 			fprintf(f, "\t/* Block %u has a checksum error (should be 0x%02x). */\n",
991 				b, crc_calc(buf));
992 	}
993 	fprintf(f, "};\n");
994 }
995 
996 // This format can be read by the QuantumData EDID editor
xmldumpedid(FILE * f,const unsigned char * edid,unsigned size)997 static void xmldumpedid(FILE *f, const unsigned char *edid, unsigned size)
998 {
999 	fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
1000 	fprintf(f, "<DATAOBJ>\n");
1001 	fprintf(f, "    <HEADER TYPE=\"DID\" VERSION=\"1.0\"/>\n");
1002 	fprintf(f, "    <DATA>\n");
1003 	for (unsigned b = 0; b < size / 128; b++) {
1004 		const unsigned char *buf = edid + 128 * b;
1005 
1006 		fprintf(f, "        <BLOCK%u>", b);
1007 		for (unsigned i = 0; i < 128; i++)
1008 			fprintf(f, "%02X", buf[i]);
1009 		fprintf(f, "</BLOCK%u>\n", b);
1010 	}
1011 	fprintf(f, "    </DATA>\n");
1012 	fprintf(f, "</DATAOBJ>\n");
1013 }
1014 
1015 
edid_to_file(const char * to_file,enum output_format out_fmt)1016 static int edid_to_file(const char *to_file, enum output_format out_fmt)
1017 {
1018 	FILE *out;
1019 
1020 	if (!strcmp(to_file, "-")) {
1021 		to_file = "stdout";
1022 		out = stdout;
1023 	} else if ((out = fopen(to_file, "w")) == NULL) {
1024 		perror(to_file);
1025 		return -1;
1026 	}
1027 	if (out_fmt == OUT_FMT_DEFAULT)
1028 		out_fmt = out == stdout ? OUT_FMT_HEX : OUT_FMT_RAW;
1029 
1030 	switch (out_fmt) {
1031 	default:
1032 	case OUT_FMT_HEX:
1033 		hexdumpedid(out, edid, state.edid_size);
1034 		break;
1035 	case OUT_FMT_RAW:
1036 		fwrite(edid, state.edid_size, 1, out);
1037 		break;
1038 	case OUT_FMT_CARRAY:
1039 		carraydumpedid(out, edid, state.edid_size);
1040 		break;
1041 	case OUT_FMT_XML:
1042 		xmldumpedid(out, edid, state.edid_size);
1043 		break;
1044 	}
1045 
1046 	if (out != stdout)
1047 		fclose(out);
1048 	return 0;
1049 }
1050 
edid_from_file(const char * from_file,FILE * error)1051 static int edid_from_file(const char *from_file, FILE *error)
1052 {
1053 #ifdef O_BINARY
1054 	// Windows compatibility
1055 	int flags = O_RDONLY | O_BINARY;
1056 #else
1057 	int flags = O_RDONLY;
1058 #endif
1059 	int fd;
1060 
1061 	if (!strcmp(from_file, "-")) {
1062 		from_file = "stdin";
1063 		fd = 0;
1064 	} else if ((fd = open(from_file, flags)) == -1) {
1065 		perror(from_file);
1066 		return -1;
1067 	}
1068 
1069 	odd_hex_digits = false;
1070 	if (!extract_edid(fd, error)) {
1071 		if (!state.edid_size) {
1072 			fprintf(error, "EDID of '%s' was empty.\n", from_file);
1073 			return -1;
1074 		}
1075 		fprintf(error, "EDID extract of '%s' failed: ", from_file);
1076 		if (odd_hex_digits)
1077 			fprintf(error, "odd number of hexadecimal digits.\n");
1078 		else
1079 			fprintf(error, "unknown format.\n");
1080 		return -1;
1081 	}
1082 	if (state.edid_size % EDID_PAGE_SIZE) {
1083 		fprintf(error, "EDID length %u is not a multiple of %u.\n",
1084 			state.edid_size, EDID_PAGE_SIZE);
1085 		return -1;
1086 	}
1087 	state.num_blocks = state.edid_size / EDID_PAGE_SIZE;
1088 	if (fd != 0)
1089 		close(fd);
1090 
1091 	if (memcmp(edid, "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", 8)) {
1092 		fprintf(error, "No EDID header found in '%s'.\n", from_file);
1093 		return -1;
1094 	}
1095 	return 0;
1096 }
1097 
1098 /* generic extension code */
1099 
block_name(unsigned char block)1100 std::string block_name(unsigned char block)
1101 {
1102 	char buf[10];
1103 
1104 	switch (block) {
1105 	case 0x00: return "Base EDID";
1106 	case 0x02: return "CTA-861 Extension Block";
1107 	case 0x10: return "Video Timing Extension Block";
1108 	case 0x20: return "EDID 2.0 Extension Block";
1109 	case 0x40: return "Display Information Extension Block";
1110 	case 0x50: return "Localized String Extension Block";
1111 	case 0x60: return "Microdisplay Interface Extension Block";
1112 	case 0x70: return "DisplayID Extension Block";
1113 	case 0xf0: return "Block Map Extension Block";
1114 	case 0xff: return "Manufacturer-Specific Extension Block";
1115 	default:
1116 		sprintf(buf, " 0x%02x", block);
1117 		return std::string("Unknown EDID Extension Block") + buf;
1118 	}
1119 }
1120 
parse_block_map(const unsigned char * x)1121 void edid_state::parse_block_map(const unsigned char *x)
1122 {
1123 	unsigned last_valid_block_tag = 0;
1124 	bool fail_once = false;
1125 	unsigned offset = 1;
1126 	unsigned i;
1127 
1128 	if (block_nr == 1)
1129 		block_map.saw_block_1 = true;
1130 	else if (!block_map.saw_block_1)
1131 		fail("No EDID Block Map Extension found in block 1.\n");
1132 	else if (block_nr == 128)
1133 		block_map.saw_block_128 = true;
1134 
1135 	if (block_nr > 1)
1136 		offset = 128;
1137 
1138 	for (i = 1; i < 127; i++) {
1139 		unsigned block = offset + i;
1140 
1141 		if (x[i]) {
1142 			last_valid_block_tag++;
1143 			if (i != last_valid_block_tag && !fail_once) {
1144 				fail("Valid block tags are not consecutive.\n");
1145 				fail_once = true;
1146 			}
1147 			printf("  Block %3u: %s\n", block, block_name(x[i]).c_str());
1148 			if (block >= num_blocks) {
1149 				if (!fail_once)
1150 					fail("Invalid block number %u.\n", block);
1151 				fail_once = true;
1152 			} else if (x[i] != edid[block * EDID_PAGE_SIZE]) {
1153 				fail("Block %u tag mismatch: expected 0x%02x, but got 0x%02x.\n",
1154 				     block, edid[block * EDID_PAGE_SIZE], x[i]);
1155 			}
1156 		} else if (block < num_blocks) {
1157 			fail("Block %u tag mismatch: expected 0x%02x, but got 0x00.\n",
1158 			     block, edid[block * EDID_PAGE_SIZE]);
1159 		}
1160 	}
1161 }
1162 
preparse_extension(const unsigned char * x)1163 void edid_state::preparse_extension(const unsigned char *x)
1164 {
1165 	switch (x[0]) {
1166 	case 0x02:
1167 		has_cta = true;
1168 		preparse_cta_block(x);
1169 		break;
1170 	case 0x70:
1171 		has_dispid = true;
1172 		preparse_displayid_block(x);
1173 		break;
1174 	}
1175 }
1176 
parse_extension(const unsigned char * x)1177 void edid_state::parse_extension(const unsigned char *x)
1178 {
1179 	block = block_name(x[0]);
1180 	data_block.clear();
1181 
1182 	printf("\n");
1183 	if (block_nr && x[0] == 0)
1184 		block = "Unknown EDID Extension Block 0x00";
1185 	printf("Block %u, %s:\n", block_nr, block.c_str());
1186 
1187 	switch (x[0]) {
1188 	case 0x02:
1189 		parse_cta_block(x);
1190 		break;
1191 	case 0x10:
1192 		parse_vtb_ext_block(x);
1193 		break;
1194 	case 0x20:
1195 		fail("Deprecated extension block for EDID 2.0, do not use.\n");
1196 		break;
1197 	case 0x40:
1198 		parse_di_ext_block(x);
1199 		break;
1200 	case 0x50:
1201 		parse_ls_ext_block(x);
1202 		break;
1203 	case 0x70:
1204 		parse_displayid_block(x);
1205 		break;
1206 	case 0xf0:
1207 		parse_block_map(x);
1208 		if (block_nr != 1 && block_nr != 128)
1209 			fail("Must be used in block 1 and 128.\n");
1210 		break;
1211 	default:
1212 		hex_block("  ", x, EDID_PAGE_SIZE);
1213 		fail("Unknown Extension Block.\n");
1214 		break;
1215 	}
1216 
1217 	data_block.clear();
1218 	do_checksum("", x, EDID_PAGE_SIZE);
1219 }
1220 
parse_edid()1221 int edid_state::parse_edid()
1222 {
1223 	hide_serial_numbers = options[OptHideSerialNumbers];
1224 
1225 	for (unsigned i = 1; i < num_blocks; i++)
1226 		preparse_extension(edid + i * EDID_PAGE_SIZE);
1227 
1228 	if (options[OptPhysicalAddress]) {
1229 		printf("%x.%x.%x.%x\n",
1230 		       (cta.preparsed_phys_addr >> 12) & 0xf,
1231 		       (cta.preparsed_phys_addr >> 8) & 0xf,
1232 		       (cta.preparsed_phys_addr >> 4) & 0xf,
1233 		       cta.preparsed_phys_addr & 0xf);
1234 		return 0;
1235 	}
1236 
1237 	if (!options[OptSkipHexDump]) {
1238 		printf("edid-decode (hex):\n\n");
1239 		for (unsigned i = 0; i < num_blocks; i++) {
1240 			hex_block("", edid + i * EDID_PAGE_SIZE, EDID_PAGE_SIZE, false);
1241 			if (i == num_blocks - 1 && options[OptOnlyHexDump])
1242 				return 0;
1243 			printf("\n");
1244 		}
1245 		printf("----------------\n\n");
1246 	}
1247 
1248 	block = block_name(0x00);
1249 	printf("Block %u, %s:\n", block_nr, block.c_str());
1250 	parse_base_block(edid);
1251 
1252 	for (unsigned i = 1; i < num_blocks; i++) {
1253 		block_nr++;
1254 		printf("\n----------------\n");
1255 		parse_extension(edid + i * EDID_PAGE_SIZE);
1256 	}
1257 
1258 	block = "";
1259 	block_nr = EDID_MAX_BLOCKS;
1260 
1261 	if (has_cta)
1262 		cta_resolve_svrs();
1263 
1264 	if (options[OptPreferredTimings] && base.preferred_timing.is_valid()) {
1265 		printf("\n----------------\n");
1266 		printf("\nPreferred Video Timing if only Block 0 is parsed:\n");
1267 		print_timings("  ", base.preferred_timing, true, false);
1268 	}
1269 
1270 	if (options[OptNativeTimings] &&
1271 	    base.preferred_timing.is_valid() && base.preferred_is_also_native) {
1272 		printf("\n----------------\n");
1273 		printf("\nNative Video Timing if only Block 0 is parsed:\n");
1274 		print_timings("  ", base.preferred_timing, true, false);
1275 	}
1276 
1277 	if (options[OptPreferredTimings] && !cta.preferred_timings.empty()) {
1278 		printf("\n----------------\n");
1279 		printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
1280 		       cta.preferred_timings.size() > 1 ? "s" : "");
1281 		for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
1282 		     iter != cta.preferred_timings.end(); ++iter)
1283 			print_timings("  ", *iter, true, false);
1284 	}
1285 
1286 	if (options[OptNativeTimings] && !cta.native_timings.empty()) {
1287 		printf("\n----------------\n");
1288 		printf("\nNative Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
1289 		       cta.native_timings.size() > 1 ? "s" : "");
1290 		for (vec_timings_ext::iterator iter = cta.native_timings.begin();
1291 		     iter != cta.native_timings.end(); ++iter)
1292 			print_timings("  ", *iter, true, false);
1293 	}
1294 
1295 	if (options[OptPreferredTimings] && !dispid.preferred_timings.empty()) {
1296 		printf("\n----------------\n");
1297 		printf("\nPreferred Video Timing%s if Block 0 and DisplayID Blocks are parsed:\n",
1298 		       dispid.preferred_timings.size() > 1 ? "s" : "");
1299 		for (vec_timings_ext::iterator iter = dispid.preferred_timings.begin();
1300 		     iter != dispid.preferred_timings.end(); ++iter)
1301 			print_timings("  ", *iter, true, false);
1302 	}
1303 
1304 	if (!options[OptCheck] && !options[OptCheckInline])
1305 		return 0;
1306 
1307 	check_base_block();
1308 	if (has_cta)
1309 		check_cta_blocks();
1310 	if (has_dispid)
1311 		check_displayid_blocks();
1312 
1313 	printf("\n----------------\n");
1314 
1315 	if (!options[OptSkipSHA] && strlen(STRING(SHA))) {
1316 		printf("\nedid-decode SHA: %s %s\n", STRING(SHA), STRING(DATE));
1317 	}
1318 
1319 	if (options[OptCheck]) {
1320 		if (warnings)
1321 			show_msgs(true);
1322 		if (failures)
1323 			show_msgs(false);
1324 	}
1325 	printf("\nEDID conformity: %s\n", failures ? "FAIL" : "PASS");
1326 	return failures ? -2 : 0;
1327 }
1328 
1329 enum cvt_opts {
1330 	CVT_WIDTH = 0,
1331 	CVT_HEIGHT,
1332 	CVT_FPS,
1333 	CVT_INTERLACED,
1334 	CVT_OVERSCAN,
1335 	CVT_RB,
1336 	CVT_ALT,
1337 	CVT_RB_H_BLANK,
1338 };
1339 
parse_cvt_subopt(char ** subopt_str,double * value)1340 static int parse_cvt_subopt(char **subopt_str, double *value)
1341 {
1342 	int opt;
1343 	char *opt_str;
1344 
1345 	static const char * const subopt_list[] = {
1346 		"w",
1347 		"h",
1348 		"fps",
1349 		"interlaced",
1350 		"overscan",
1351 		"rb",
1352 		"alt",
1353 		"hblank",
1354 		nullptr
1355 	};
1356 
1357 	opt = getsubopt(subopt_str, (char* const*) subopt_list, &opt_str);
1358 
1359 	if (opt == -1) {
1360 		fprintf(stderr, "Invalid suboptions specified.\n");
1361 		usage();
1362 		std::exit(EXIT_FAILURE);
1363 	}
1364 	if (opt_str == nullptr && opt != CVT_INTERLACED && opt != CVT_ALT &&
1365 	    opt != CVT_OVERSCAN) {
1366 		fprintf(stderr, "No value given to suboption <%s>.\n",
1367 				subopt_list[opt]);
1368 		usage();
1369 		std::exit(EXIT_FAILURE);
1370 	}
1371 
1372 	if (opt_str)
1373 		*value = strtod(opt_str, nullptr);
1374 	return opt;
1375 }
1376 
parse_cvt(char * optarg)1377 static void parse_cvt(char *optarg)
1378 {
1379 	unsigned w = 0, h = 0;
1380 	double fps = 0;
1381 	unsigned rb = RB_NONE;
1382 	unsigned rb_h_blank = 0;
1383 	bool interlaced = false;
1384 	bool alt = false;
1385 	bool overscan = false;
1386 
1387 	while (*optarg != '\0') {
1388 		int opt;
1389 		double opt_val;
1390 
1391 		opt = parse_cvt_subopt(&optarg, &opt_val);
1392 
1393 		switch (opt) {
1394 		case CVT_WIDTH:
1395 			w = round(opt_val);
1396 			break;
1397 		case CVT_HEIGHT:
1398 			h = round(opt_val);
1399 			break;
1400 		case CVT_FPS:
1401 			fps = opt_val;
1402 			break;
1403 		case CVT_RB:
1404 			rb = opt_val;
1405 			break;
1406 		case CVT_OVERSCAN:
1407 			overscan = true;
1408 			break;
1409 		case CVT_INTERLACED:
1410 			interlaced = opt_val;
1411 			break;
1412 		case CVT_ALT:
1413 			alt = opt_val;
1414 			break;
1415 		case CVT_RB_H_BLANK:
1416 			rb_h_blank = opt_val;
1417 			break;
1418 		default:
1419 			break;
1420 		}
1421 	}
1422 
1423 	if (!w || !h || !fps) {
1424 		fprintf(stderr, "Missing width, height and/or fps.\n");
1425 		usage();
1426 		std::exit(EXIT_FAILURE);
1427 	}
1428 	if (interlaced)
1429 		fps /= 2;
1430 	timings t = state.calc_cvt_mode(w, h, fps, rb, interlaced, overscan, alt, rb_h_blank);
1431 	state.print_timings("", &t, "CVT", "", true, false);
1432 }
1433 
1434 struct gtf_parsed_data {
1435 	unsigned w, h;
1436 	double freq;
1437 	double C, M, K, J;
1438 	bool overscan;
1439 	bool interlaced;
1440 	bool secondary;
1441 	bool params_from_edid;
1442 	enum gtf_ip_parm ip_parm;
1443 };
1444 
1445 enum gtf_opts {
1446 	GTF_WIDTH = 0,
1447 	GTF_HEIGHT,
1448 	GTF_FPS,
1449 	GTF_HORFREQ,
1450 	GTF_PIXCLK,
1451 	GTF_INTERLACED,
1452 	GTF_OVERSCAN,
1453 	GTF_SECONDARY,
1454 	GTF_C2,
1455 	GTF_M,
1456 	GTF_K,
1457 	GTF_J2,
1458 };
1459 
parse_gtf_subopt(char ** subopt_str,double * value)1460 static int parse_gtf_subopt(char **subopt_str, double *value)
1461 {
1462 	int opt;
1463 	char *opt_str;
1464 
1465 	static const char * const subopt_list[] = {
1466 		"w",
1467 		"h",
1468 		"fps",
1469 		"horfreq",
1470 		"pixclk",
1471 		"interlaced",
1472 		"overscan",
1473 		"secondary",
1474 		"C",
1475 		"M",
1476 		"K",
1477 		"J",
1478 		nullptr
1479 	};
1480 
1481 	opt = getsubopt(subopt_str, (char * const *)subopt_list, &opt_str);
1482 
1483 	if (opt == -1) {
1484 		fprintf(stderr, "Invalid suboptions specified.\n");
1485 		usage();
1486 		std::exit(EXIT_FAILURE);
1487 	}
1488 	if (opt_str == nullptr && opt != GTF_INTERLACED && opt != GTF_OVERSCAN &&
1489 	    opt != GTF_SECONDARY) {
1490 		fprintf(stderr, "No value given to suboption <%s>.\n",
1491 				subopt_list[opt]);
1492 		usage();
1493 		std::exit(EXIT_FAILURE);
1494 	}
1495 
1496 	if (opt == GTF_C2 || opt == GTF_J2)
1497 		*value = round(2.0 * strtod(opt_str, nullptr));
1498 	else if (opt_str)
1499 		*value = strtod(opt_str, nullptr);
1500 	return opt;
1501 }
1502 
parse_gtf(char * optarg,gtf_parsed_data & data)1503 static void parse_gtf(char *optarg, gtf_parsed_data &data)
1504 {
1505 	memset(&data, 0, sizeof(data));
1506 	data.params_from_edid = true;
1507 	data.C = 40;
1508 	data.M = 600;
1509 	data.K = 128;
1510 	data.J = 20;
1511 
1512 	while (*optarg != '\0') {
1513 		int opt;
1514 		double opt_val;
1515 
1516 		opt = parse_gtf_subopt(&optarg, &opt_val);
1517 
1518 		switch (opt) {
1519 		case GTF_WIDTH:
1520 			data.w = round(opt_val);
1521 			break;
1522 		case GTF_HEIGHT:
1523 			data.h = round(opt_val);
1524 			break;
1525 		case GTF_FPS:
1526 			data.freq = opt_val;
1527 			data.ip_parm = gtf_ip_vert_freq;
1528 			break;
1529 		case GTF_HORFREQ:
1530 			data.freq = opt_val;
1531 			data.ip_parm = gtf_ip_hor_freq;
1532 			break;
1533 		case GTF_PIXCLK:
1534 			data.freq = opt_val;
1535 			data.ip_parm = gtf_ip_clk_freq;
1536 			break;
1537 		case GTF_INTERLACED:
1538 			data.interlaced = true;
1539 			break;
1540 		case GTF_OVERSCAN:
1541 			data.overscan = true;
1542 			break;
1543 		case GTF_SECONDARY:
1544 			data.secondary = true;
1545 			break;
1546 		case GTF_C2:
1547 			data.C = opt_val / 2.0;
1548 			data.params_from_edid = false;
1549 			break;
1550 		case GTF_M:
1551 			data.M = round(opt_val);
1552 			data.params_from_edid = false;
1553 			break;
1554 		case GTF_K:
1555 			data.K = round(opt_val);
1556 			data.params_from_edid = false;
1557 			break;
1558 		case GTF_J2:
1559 			data.J = opt_val / 2.0;
1560 			data.params_from_edid = false;
1561 			break;
1562 		default:
1563 			break;
1564 		}
1565 	}
1566 
1567 	if (!data.w || !data.h) {
1568 		fprintf(stderr, "Missing width and/or height.\n");
1569 		usage();
1570 		std::exit(EXIT_FAILURE);
1571 	}
1572 	if (!data.freq) {
1573 		fprintf(stderr, "One of fps, horfreq or pixclk must be given.\n");
1574 		usage();
1575 		std::exit(EXIT_FAILURE);
1576 	}
1577 	if (!data.secondary)
1578 		data.params_from_edid = false;
1579 	if (data.interlaced && data.ip_parm == gtf_ip_vert_freq)
1580 		data.freq /= 2;
1581 }
1582 
show_gtf(gtf_parsed_data & data)1583 static void show_gtf(gtf_parsed_data &data)
1584 {
1585 	timings t;
1586 
1587 	t = state.calc_gtf_mode(data.w, data.h, data.freq, data.interlaced,
1588 				data.ip_parm, data.overscan, data.secondary,
1589 				data.C, data.M, data.K, data.J);
1590 	calc_ratio(&t);
1591 	state.print_timings("", &t, "GTF", "", true, false);
1592 }
1593 
main(int argc,char ** argv)1594 int main(int argc, char **argv)
1595 {
1596 	char short_options[26 * 2 * 2 + 1];
1597 	enum output_format out_fmt = OUT_FMT_DEFAULT;
1598 	gtf_parsed_data gtf_data;
1599 	int ret;
1600 
1601 	while (1) {
1602 		int option_index = 0;
1603 		unsigned idx = 0;
1604 		unsigned i, val;
1605 		const timings *t;
1606 		char buf[16];
1607 
1608 		for (i = 0; long_options[i].name; i++) {
1609 			if (!isalpha(long_options[i].val))
1610 				continue;
1611 			short_options[idx++] = long_options[i].val;
1612 			if (long_options[i].has_arg == required_argument)
1613 				short_options[idx++] = ':';
1614 		}
1615 		short_options[idx] = 0;
1616 		int ch = getopt_long(argc, argv, short_options,
1617 				     long_options, &option_index);
1618 		if (ch == -1)
1619 			break;
1620 
1621 		options[ch] = 1;
1622 		switch (ch) {
1623 		case OptHelp:
1624 			usage();
1625 			return -1;
1626 		case OptOutputFormat:
1627 			if (!strcmp(optarg, "hex")) {
1628 				out_fmt = OUT_FMT_HEX;
1629 			} else if (!strcmp(optarg, "raw")) {
1630 				out_fmt = OUT_FMT_RAW;
1631 			} else if (!strcmp(optarg, "carray")) {
1632 				out_fmt = OUT_FMT_CARRAY;
1633 			} else if (!strcmp(optarg, "xml")) {
1634 				out_fmt = OUT_FMT_XML;
1635 			} else {
1636 				usage();
1637 				exit(1);
1638 			}
1639 			break;
1640 		case OptSTD: {
1641 			unsigned char byte1, byte2 = 0;
1642 			char *endptr;
1643 
1644 			byte1 = strtoul(optarg, &endptr, 0);
1645 			if (*endptr == ',')
1646 				byte2 = strtoul(endptr + 1, NULL, 0);
1647 			state.print_standard_timing("", byte1, byte2, false, true);
1648 			break;
1649 		}
1650 		case OptDMT:
1651 			val = strtoul(optarg, NULL, 0);
1652 			t = find_dmt_id(val);
1653 			if (t) {
1654 				sprintf(buf, "DMT 0x%02x", val);
1655 				state.print_timings("", t, buf, "", true, false);
1656 			} else {
1657 				fprintf(stderr, "Unknown DMT code 0x%02x.\n", val);
1658 			}
1659 			break;
1660 		case OptVIC:
1661 			val = strtoul(optarg, NULL, 0);
1662 			t = find_vic_id(val);
1663 			if (t) {
1664 				sprintf(buf, "VIC %3u", val);
1665 				state.print_timings("", t, buf, "", true, false);
1666 			} else {
1667 				fprintf(stderr, "Unknown VIC code %u.\n", val);
1668 			}
1669 			break;
1670 		case OptHDMIVIC:
1671 			val = strtoul(optarg, NULL, 0);
1672 			t = find_hdmi_vic_id(val);
1673 			if (t) {
1674 				sprintf(buf, "HDMI VIC %u", val);
1675 				state.print_timings("", t, buf, "", true, false);
1676 			} else {
1677 				fprintf(stderr, "Unknown HDMI VIC code %u.\n", val);
1678 			}
1679 			break;
1680 		case OptCVT:
1681 			parse_cvt(optarg);
1682 			break;
1683 		case OptGTF:
1684 			parse_gtf(optarg, gtf_data);
1685 			break;
1686 		case ':':
1687 			fprintf(stderr, "Option '%s' requires a value.\n",
1688 				argv[optind]);
1689 			usage();
1690 			return -1;
1691 		case '?':
1692 			fprintf(stderr, "Unknown argument '%s'.\n",
1693 				argv[optind]);
1694 			usage();
1695 			return -1;
1696 		}
1697 	}
1698 	if (optind == argc && options[OptVersion]) {
1699 		if (strlen(STRING(SHA)))
1700 			printf("edid-decode SHA: %s %s\n", STRING(SHA), STRING(DATE));
1701 		else
1702 			printf("edid-decode SHA: not available\n");
1703 		return 0;
1704 	}
1705 
1706 	if (options[OptListEstTimings])
1707 		state.list_established_timings();
1708 	if (options[OptListDMTs])
1709 		state.list_dmts();
1710 	if (options[OptListVICs])
1711 		state.cta_list_vics();
1712 	if (options[OptListHDMIVICs])
1713 		state.cta_list_hdmi_vics();
1714 
1715 	if (options[OptListEstTimings] || options[OptListDMTs] ||
1716 	    options[OptListVICs] || options[OptListHDMIVICs])
1717 		return 0;
1718 
1719 	if (options[OptCVT] || options[OptDMT] || options[OptVIC] ||
1720 	    options[OptHDMIVIC] || options[OptSTD])
1721 		return 0;
1722 
1723 	if (options[OptGTF] && (!gtf_data.params_from_edid || optind == argc)) {
1724 		show_gtf(gtf_data);
1725 		return 0;
1726 	}
1727 
1728 	if (optind == argc)
1729 		ret = edid_from_file("-", stdout);
1730 	else
1731 		ret = edid_from_file(argv[optind], argv[optind + 1] ? stderr : stdout);
1732 
1733 	if (ret && options[OptPhysicalAddress]) {
1734 		printf("f.f.f.f\n");
1735 		return 0;
1736 	}
1737 	if (optind < argc - 1)
1738 		return ret ? ret : edid_to_file(argv[optind + 1], out_fmt);
1739 
1740 	if (options[OptGTF]) {
1741 		timings t;
1742 
1743 		// Find the Secondary Curve
1744 		state.preparse_detailed_block(edid + 0x36);
1745 		state.preparse_detailed_block(edid + 0x48);
1746 		state.preparse_detailed_block(edid + 0x5a);
1747 		state.preparse_detailed_block(edid + 0x6c);
1748 
1749 		t = state.calc_gtf_mode(gtf_data.w, gtf_data.h, gtf_data.freq,
1750 					gtf_data.interlaced, gtf_data.ip_parm,
1751 					gtf_data.overscan);
1752 		unsigned hbl = t.hfp + t.hsync + t.hbp;
1753 		unsigned htotal = t.hact + hbl;
1754 		double hor_freq_khz = htotal ? (double)t.pixclk_khz / htotal : 0;
1755 
1756 		if (state.base.supports_sec_gtf &&
1757 		    hor_freq_khz >= state.base.sec_gtf_start_freq) {
1758 			t = state.calc_gtf_mode(gtf_data.w, gtf_data.h, gtf_data.freq,
1759 						gtf_data.interlaced, gtf_data.ip_parm,
1760 						gtf_data.overscan, true,
1761 						state.base.C, state.base.M,
1762 						state.base.K, state.base.J);
1763 		}
1764 		calc_ratio(&t);
1765 		if (t.hfp <= 0)
1766 			state.print_timings("", &t, "GTF", "INVALID: Hfront <= 0", true, false);
1767 		else
1768 			state.print_timings("", &t, "GTF", "", true, false);
1769 		return 0;
1770 	}
1771 
1772 	return ret ? ret : state.parse_edid();
1773 }
1774 
1775 #ifdef __EMSCRIPTEN__
1776 /*
1777  * The surrounding JavaScript implementation will call this function
1778  * each time it wants to decode an EDID. So this should reset all the
1779  * state and start over.
1780  */
parse_edid(const char * input)1781 extern "C" int parse_edid(const char *input)
1782 {
1783 	for (unsigned i = 0; i < EDID_MAX_BLOCKS + 1; i++) {
1784 		s_msgs[i][0].clear();
1785 		s_msgs[i][1].clear();
1786 	}
1787 	options[OptCheck] = 1;
1788 	options[OptPreferredTimings] = 1;
1789 	options[OptNativeTimings] = 1;
1790 	state = edid_state();
1791 	int ret = edid_from_file(input, stderr);
1792 	return ret ? ret : state.parse_edid();
1793 }
1794 #endif
1795