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