edid-decode.1: document the --infoframe option
[edid-decode.git] / edid-decode.cpp
blob863364415ff26bc12a78948350a89be4337e59d2
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.
6 * Author: Adam Jackson <ajax@nwnk.net>
7 * Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
8 */
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>
19 #include "edid-decode.h"
21 #define STR(x) #x
22 #define STRING(x) STR(x)
24 static edid_state state;
26 static unsigned char edid[EDID_PAGE_SIZE * EDID_MAX_BLOCKS];
27 static bool odd_hex_digits;
29 enum output_format {
30 OUT_FMT_DEFAULT,
31 OUT_FMT_HEX,
32 OUT_FMT_RAW,
33 OUT_FMT_CARRAY,
34 OUT_FMT_XML,
38 * Options
39 * Please keep in alphabetical order of the short option.
40 * That makes it easier to see which options are still free.
42 enum Option {
43 OptCheck = 'c',
44 OptCheckInline = 'C',
45 OptFBModeTimings = 'F',
46 OptHelp = 'h',
47 OptOnlyHexDump = 'H',
48 OptInfoFrame = 'I',
49 OptLongTimings = 'L',
50 OptNativeResolution = 'n',
51 OptNTSC = 'N',
52 OptOutputFormat = 'o',
53 OptPreferredTimings = 'p',
54 OptPhysicalAddress = 'P',
55 OptSkipHexDump = 's',
56 OptShortTimings = 'S',
57 OptV4L2Timings = 'V',
58 OptXModeLineTimings = 'X',
59 OptSkipSHA = 128,
60 OptHideSerialNumbers,
61 OptReplaceUniqueIDs,
62 OptVersion,
63 OptDiag,
64 OptSTD,
65 OptDMT,
66 OptVIC,
67 OptHDMIVIC,
68 OptCVT,
69 OptGTF,
70 OptOVT,
71 OptListEstTimings,
72 OptListDMTs,
73 OptListVICs,
74 OptListHDMIVICs,
75 OptListRIDTimings,
76 OptListRIDs,
77 OptLast = 256
80 static char options[OptLast];
82 #ifndef __EMSCRIPTEN__
83 static struct option long_options[] = {
84 { "help", no_argument, 0, OptHelp },
85 { "output-format", required_argument, 0, OptOutputFormat },
86 { "native-resolution", no_argument, 0, OptNativeResolution },
87 { "preferred-timings", no_argument, 0, OptPreferredTimings },
88 { "physical-address", no_argument, 0, OptPhysicalAddress },
89 { "skip-hex-dump", no_argument, 0, OptSkipHexDump },
90 { "only-hex-dump", no_argument, 0, OptOnlyHexDump },
91 { "skip-sha", no_argument, 0, OptSkipSHA },
92 { "hide-serial-numbers", no_argument, 0, OptHideSerialNumbers },
93 { "replace-unique-ids", no_argument, 0, OptReplaceUniqueIDs },
94 { "version", no_argument, 0, OptVersion },
95 { "check-inline", no_argument, 0, OptCheckInline },
96 { "check", no_argument, 0, OptCheck },
97 { "short-timings", no_argument, 0, OptShortTimings },
98 { "long-timings", no_argument, 0, OptLongTimings },
99 { "ntsc", no_argument, 0, OptNTSC },
100 { "xmodeline", no_argument, 0, OptXModeLineTimings },
101 { "fbmode", no_argument, 0, OptFBModeTimings },
102 { "v4l2-timings", no_argument, 0, OptV4L2Timings },
103 { "diagonal", required_argument, 0, OptDiag },
104 { "std", required_argument, 0, OptSTD },
105 { "dmt", required_argument, 0, OptDMT },
106 { "vic", required_argument, 0, OptVIC },
107 { "hdmi-vic", required_argument, 0, OptHDMIVIC },
108 { "cvt", required_argument, 0, OptCVT },
109 { "gtf", required_argument, 0, OptGTF },
110 { "ovt", required_argument, 0, OptOVT },
111 { "list-established-timings", no_argument, 0, OptListEstTimings },
112 { "list-dmts", no_argument, 0, OptListDMTs },
113 { "list-vics", no_argument, 0, OptListVICs },
114 { "list-hdmi-vics", no_argument, 0, OptListHDMIVICs },
115 { "list-rid-timings", required_argument, 0, OptListRIDTimings },
116 { "list-rids", no_argument, 0, OptListRIDs },
117 { "infoframe", required_argument, 0, OptInfoFrame },
118 { 0, 0, 0, 0 }
121 static void usage(void)
123 printf("Usage: edid-decode <options> [in [out]]\n"
124 " [in] EDID file to parse. Read from standard input if none given\n"
125 " and --infoframe was not used, or if the input filename is '-'.\n"
126 " [out] Output the read EDID to this file. Write to standard output\n"
127 " if the output filename is '-'.\n"
128 "\nOptions:\n"
129 " -o, --output-format <fmt>\n"
130 " If [out] is specified, then write the EDID in this format.\n"
131 " <fmt> is one of:\n"
132 " hex: hex numbers in ascii text (default for stdout)\n"
133 " raw: binary data (default unless writing to stdout)\n"
134 " carray: c-program struct\n"
135 " xml: XML data\n"
136 " -c, --check Check if the EDID conforms to the standards, failures and\n"
137 " warnings are reported at the end.\n"
138 " -C, --check-inline Check if the EDID conforms to the standards, failures and\n"
139 " warnings are reported inline.\n"
140 " -n, --native-resolution Report the native resolution.\n"
141 " -p, --preferred-timings Report the preferred timings.\n"
142 " -P, --physical-address Only report the CEC physical address.\n"
143 " -S, --short-timings Report all video timings in a short format.\n"
144 " -L, --long-timings Report all video timings in a long format.\n"
145 " -N, --ntsc Report the video timings suitable for NTSC-based video.\n"
146 " -X, --xmodeline Report all long video timings in Xorg.conf format.\n"
147 " -F, --fbmode Report all long video timings in fb.modes format.\n"
148 " -V, --v4l2-timings Report all long video timings in v4l2-dv-timings.h format.\n"
149 " -s, --skip-hex-dump Skip the initial hex dump of the EDID.\n"
150 " -H, --only-hex-dump Only output the hex dump of the EDID.\n"
151 " --skip-sha Skip the SHA report.\n"
152 " --hide-serial-numbers Hide serial numbers with '...'.\n"
153 " --replace-unique-ids Replace unique IDs (serial numbers, dates, Container IDs) with fixed values.\n"
154 " --version Show the edid-decode version (SHA).\n"
155 " --diagonal <inches> Set the display's diagonal in inches.\n"
156 " --std <byte1>,<byte2> Show the standard timing represented by these two bytes.\n"
157 " --dmt <dmt> Show the timings for the DMT with the given DMT ID.\n"
158 " --vic <vic> Show the timings for this VIC.\n"
159 " --hdmi-vic <hdmivic> Show the timings for this HDMI VIC.\n"
160 " --cvt w=<width>,h=<height>,fps=<fps>[,rb=<rb>][,interlaced][,overscan][,alt][,hblank=<hblank>][,vblank=<vblank>][,early-vsync]\n"
161 " Calculate the CVT timings for the given format.\n"
162 " <fps> is frames per second for progressive timings,\n"
163 " or fields per second for interlaced timings.\n"
164 " <rb> can be 0 (no reduced blanking, default), or\n"
165 " 1-3 for the reduced blanking version.\n"
166 " If 'interlaced' is given, then this is an interlaced format.\n"
167 " If 'overscan' is given, then this is an overscanned format.\n"
168 " If 'alt' is given and <rb>=2, then report the timings\n"
169 " optimized for video: 1000 / 1001 * <fps>.\n"
170 " If 'alt' is given and <rb>=3, then the horizontal blanking\n"
171 " is 160 instead of 80 pixels.\n"
172 " If 'hblank' is given and <rb>=3, then the horizontal blanking\n"
173 " is <hblank> pixels (range of 80-200), overriding 'alt'.\n"
174 " If 'vblank' is given and <rb>=3, then the vertical blanking\n"
175 " time is <vblank> microseconds (range of 460-705 or 300-440).\n"
176 " If 'early-vsync' is given and <rb=3>, then select early vsync.\n"
177 " --gtf w=<width>,h=<height>[,fps=<fps>][,horfreq=<horfreq>][,pixclk=<pixclk>][,interlaced]\n"
178 " [,overscan][,secondary][,C=<c>][,M=<m>][,K=<k>][,J=<j>]\n"
179 " Calculate the GTF timings for the given format.\n"
180 " <fps> is frames per second for progressive timings,\n"
181 " or fields per second for interlaced timings.\n"
182 " <horfreq> is the horizontal frequency in kHz.\n"
183 " <pixclk> is the pixel clock frequency in MHz.\n"
184 " Only one of fps, horfreq or pixclk must be given.\n"
185 " If 'interlaced' is given, then this is an interlaced format.\n"
186 " If 'overscan' is given, then this is an overscanned format.\n"
187 " If 'secondary' is given, then the secondary GTF is used for\n"
188 " reduced blanking, where <c>, <m>, <k> and <j> are parameters\n"
189 " for the secondary curve.\n"
190 " --ovt (rid=<rid>|w=<width>,h=<height>),fps=<fps>\n"
191 " Calculate the OVT timings for the given format.\n"
192 " Either specify a RID or explicitly specify width and height.\n"
193 " --list-established-timings List all known Established Timings.\n"
194 " --list-dmts List all known DMTs.\n"
195 " --list-vics List all known VICs.\n"
196 " --list-hdmi-vics List all known HDMI VICs.\n"
197 " --list-rids List all known RIDs.\n"
198 " --list-rid-timings <rid> List all timings for RID <rid> or all known RIDs if <rid> is 0.\n"
199 " -I, --infoframe <file> Parse the InfoFrame from <file> that was sent to this display.\n"
200 " This option can be specified multiple times for different InfoFrame files.\n"
201 " -h, --help Display this help message.\n");
203 #endif
205 static std::string s_msgs[EDID_MAX_BLOCKS + 1][2];
207 void msg(bool is_warn, const char *fmt, ...)
209 char buf[1024] = "";
210 va_list ap;
212 va_start(ap, fmt);
213 vsprintf(buf, fmt, ap);
214 va_end(ap);
216 if (is_warn)
217 state.warnings++;
218 else
219 state.failures++;
220 if (state.data_block.empty())
221 s_msgs[state.block_nr][is_warn] += std::string(" ") + buf;
222 else
223 s_msgs[state.block_nr][is_warn] += " " + state.data_block + ": " + buf;
225 if (options[OptCheckInline])
226 printf("%s: %s", is_warn ? "WARN" : "FAIL", buf);
229 static void show_msgs(bool is_warn)
231 printf("\n%s:\n\n", is_warn ? "Warnings" : "Failures");
232 for (unsigned i = 0; i < state.num_blocks; i++) {
233 if (s_msgs[i][is_warn].empty())
234 continue;
235 printf("Block %u, %s:\n%s",
236 i, block_name(edid[i * EDID_PAGE_SIZE]).c_str(),
237 s_msgs[i][is_warn].c_str());
239 if (s_msgs[EDID_MAX_BLOCKS][is_warn].empty())
240 return;
241 printf("EDID:\n%s",
242 s_msgs[EDID_MAX_BLOCKS][is_warn].c_str());
246 void replace_checksum(unsigned char *x, size_t len)
248 unsigned char sum = 0;
249 unsigned i;
251 for (i = 0; i < len - 1; i++)
252 sum += x[i];
253 x[len - 1] = -sum & 0xff;
256 void do_checksum(const char *prefix, const unsigned char *x, size_t len, size_t checksum_pos,
257 unsigned unused_bytes)
259 unsigned char check = x[checksum_pos];
260 unsigned char sum = 0;
261 unsigned i;
263 for (i = 0; i < len; i++) {
264 if (i != checksum_pos)
265 sum += x[i];
268 printf("%sChecksum: 0x%02hhx", prefix, check);
269 if ((unsigned char)(check + sum) != 0) {
270 printf(" (should be 0x%02x)", -sum & 0xff);
271 fail("Invalid checksum 0x%02x (should be 0x%02x).\n",
272 check, -sum & 0xff);
274 if (unused_bytes)
275 printf(" Unused space in Extension Block: %u byte%s",
276 unused_bytes, unused_bytes > 1 ? "s" : "");
277 printf("\n");
280 unsigned gcd(unsigned a, unsigned b)
282 while (b) {
283 unsigned t = b;
285 b = a % b;
286 a = t;
288 return a;
291 void calc_ratio(struct timings *t)
293 unsigned d = gcd(t->hact, t->vact);
295 if (d == 0) {
296 t->hratio = t->vratio = 0;
297 return;
299 t->hratio = t->hact / d;
300 t->vratio = t->vact / d;
302 if (t->hratio == 8 && t->vratio == 5) {
303 t->hratio = 16;
304 t->vratio = 10;
308 unsigned calc_fps(const struct timings *t)
310 unsigned vact = t->vact;
311 unsigned vbl = t->vfp + t->vsync + t->vbp + 2 * t->vborder;
312 unsigned hbl = t->hfp + t->hsync + t->hbp + 2 * t->hborder;
313 unsigned htotal = t->hact + hbl;
315 if (t->interlaced)
316 vact /= 2;
318 double vtotal = vact + vbl;
320 if (t->even_vtotal)
321 vtotal = vact + t->vfp + t->vsync + t->vbp;
322 else if (t->interlaced)
323 vtotal = vact + t->vfp + t->vsync + t->vbp + 0.5;
325 return t->pixclk_khz * 1000.0 / (htotal * vtotal);
328 std::string edid_state::dtd_type(unsigned cnt)
330 unsigned len = std::to_string(cta.preparsed_total_dtds).length();
331 char buf[16];
332 sprintf(buf, "DTD %*u", len, cnt);
333 return buf;
336 bool match_timings(const timings &t1, const timings &t2)
338 if (t1.hact != t2.hact ||
339 t1.vact != t2.vact ||
340 t1.rb != t2.rb ||
341 t1.interlaced != t2.interlaced ||
342 t1.hfp != t2.hfp ||
343 t1.hbp != t2.hbp ||
344 t1.hsync != t2.hsync ||
345 t1.pos_pol_hsync != t2.pos_pol_hsync ||
346 t1.hratio != t2.hratio ||
347 t1.vfp != t2.vfp ||
348 t1.vbp != t2.vbp ||
349 t1.vsync != t2.vsync ||
350 t1.pos_pol_vsync != t2.pos_pol_vsync ||
351 t1.vratio != t2.vratio ||
352 t1.pixclk_khz != t2.pixclk_khz)
353 return false;
354 return true;
357 static void or_str(std::string &s, const std::string &flag, unsigned &num_flags)
359 if (!num_flags)
360 s = flag;
361 else if (num_flags % 2 == 0)
362 s = s + " | \\\n\t\t" + flag;
363 else
364 s = s + " | " + flag;
365 num_flags++;
369 * Return true if the timings are a close, but not identical,
370 * match. The only differences allowed are polarities and
371 * porches and syncs, provided the total blanking remains the
372 * same.
374 bool timings_close_match(const timings &t1, const timings &t2)
376 // We don't want to deal with borders, you're on your own
377 // if you are using those.
378 if (t1.hborder || t1.vborder ||
379 t2.hborder || t2.vborder)
380 return false;
381 if (t1.hact != t2.hact || t1.vact != t2.vact ||
382 t1.interlaced != t2.interlaced ||
383 t1.pixclk_khz != t2.pixclk_khz ||
384 t1.hfp + t1.hsync + t1.hbp != t2.hfp + t2.hsync + t2.hbp ||
385 t1.vfp + t1.vsync + t1.vbp != t2.vfp + t2.vsync + t2.vbp)
386 return false;
387 if (t1.hfp == t2.hfp &&
388 t1.hsync == t2.hsync &&
389 t1.hbp == t2.hbp &&
390 t1.pos_pol_hsync == t2.pos_pol_hsync &&
391 t1.vfp == t2.vfp &&
392 t1.vsync == t2.vsync &&
393 t1.vbp == t2.vbp &&
394 t1.pos_pol_vsync == t2.pos_pol_vsync)
395 return false;
396 return true;
399 static void print_modeline(unsigned indent, const struct timings *t, double refresh)
401 unsigned offset = (!t->even_vtotal && t->interlaced) ? 1 : 0;
402 unsigned hfp = t->hborder + t->hfp;
403 unsigned hbp = t->hborder + t->hbp;
404 unsigned vfp = t->vborder + t->vfp;
405 unsigned vbp = t->vborder + t->vbp;
407 printf("%*sModeline \"%ux%u_%.2f%s\" %.3f %u %u %u %u %u %u %u %u %cHSync",
408 indent, "",
409 t->hact, t->vact, refresh,
410 t->interlaced ? "i" : "", t->pixclk_khz / 1000.0,
411 t->hact, t->hact + hfp, t->hact + hfp + t->hsync,
412 t->hact + hfp + t->hsync + hbp,
413 t->vact, t->vact + vfp, t->vact + vfp + t->vsync,
414 t->vact + vfp + t->vsync + vbp + offset,
415 t->pos_pol_hsync ? '+' : '-');
416 if (!t->no_pol_vsync)
417 printf(" %cVSync", t->pos_pol_vsync ? '+' : '-');
418 if (t->interlaced)
419 printf(" Interlace");
420 printf("\n");
423 static void print_fbmode(unsigned indent, const struct timings *t,
424 double refresh, double hor_freq_khz)
426 printf("%*smode \"%ux%u-%u%s\"\n",
427 indent, "",
428 t->hact, t->vact,
429 (unsigned)(0.5 + (t->interlaced ? refresh / 2.0 : refresh)),
430 t->interlaced ? "-lace" : "");
431 printf("%*s# D: %.2f MHz, H: %.3f kHz, V: %.2f Hz\n",
432 indent + 8, "",
433 t->pixclk_khz / 1000.0, hor_freq_khz, refresh);
434 printf("%*sgeometry %u %u %u %u 32\n",
435 indent + 8, "",
436 t->hact, t->vact, t->hact, t->vact);
437 unsigned mult = t->interlaced ? 2 : 1;
438 unsigned offset = !t->even_vtotal && t->interlaced;
439 unsigned hfp = t->hborder + t->hfp;
440 unsigned hbp = t->hborder + t->hbp;
441 unsigned vfp = t->vborder + t->vfp;
442 unsigned vbp = t->vborder + t->vbp;
443 printf("%*stimings %llu %d %d %d %u %u %u\n",
444 indent + 8, "",
445 (unsigned long long)(1000000000.0 / (double)(t->pixclk_khz) + 0.5),
446 hbp, hfp, mult * vbp, mult * vfp + offset, t->hsync, mult * t->vsync);
447 if (t->interlaced)
448 printf("%*slaced true\n", indent + 8, "");
449 if (t->pos_pol_hsync)
450 printf("%*shsync high\n", indent + 8, "");
451 if (t->pos_pol_vsync)
452 printf("%*svsync high\n", indent + 8, "");
453 printf("%*sendmode\n", indent, "");
456 static void print_v4l2_timing(const struct timings *t,
457 double refresh, const char *type)
459 printf("\t#define V4L2_DV_BT_%uX%u%c%u_%02u { \\\n",
460 t->hact, t->vact, t->interlaced ? 'I' : 'P',
461 (unsigned)refresh, (unsigned)(0.5 + 100.0 * (refresh - (unsigned)refresh)));
462 printf("\t\t.type = V4L2_DV_BT_656_1120, \\\n");
463 printf("\t\tV4L2_INIT_BT_TIMINGS(%u, %u, %u, ",
464 t->hact, t->vact, t->interlaced);
465 if (!t->pos_pol_hsync && !t->pos_pol_vsync)
466 printf("0, \\\n");
467 else if (t->pos_pol_hsync && t->pos_pol_vsync)
468 printf("\\\n\t\t\tV4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \\\n");
469 else if (t->pos_pol_hsync)
470 printf("V4L2_DV_HSYNC_POS_POL, \\\n");
471 else
472 printf("V4L2_DV_VSYNC_POS_POL, \\\n");
473 unsigned hfp = t->hborder + t->hfp;
474 unsigned hbp = t->hborder + t->hbp;
475 unsigned vfp = t->vborder + t->vfp;
476 unsigned vbp = t->vborder + t->vbp;
477 printf("\t\t\t%lluULL, %d, %u, %d, %u, %u, %d, %u, %u, %d, \\\n",
478 t->pixclk_khz * 1000ULL, hfp, t->hsync, hbp,
479 vfp, t->vsync, vbp,
480 t->interlaced ? vfp : 0,
481 t->interlaced ? t->vsync : 0,
482 t->interlaced ? vbp + !t->even_vtotal : 0);
484 std::string flags;
485 unsigned num_flags = 0;
486 unsigned vic = 0;
487 unsigned hdmi_vic = 0;
488 const char *std = "0";
490 if (t->interlaced && !t->even_vtotal)
491 or_str(flags, "V4L2_DV_FL_HALF_LINE", num_flags);
492 if (!memcmp(type, "VIC", 3)) {
493 or_str(flags, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags);
494 or_str(flags, "V4L2_DV_FL_IS_CE_VIDEO", num_flags);
495 vic = strtoul(type + 4, 0, 0);
497 if (!memcmp(type, "HDMI VIC", 8)) {
498 or_str(flags, "V4L2_DV_FL_HAS_HDMI_VIC", num_flags);
499 or_str(flags, "V4L2_DV_FL_IS_CE_VIDEO", num_flags);
500 hdmi_vic = strtoul(type + 9, 0, 0);
501 vic = hdmi_vic_to_vic(hdmi_vic);
502 if (vic)
503 or_str(flags, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags);
505 if (vic && (fmod(refresh, 6)) == 0.0)
506 or_str(flags, "V4L2_DV_FL_CAN_REDUCE_FPS", num_flags);
507 if (t->rb)
508 or_str(flags, "V4L2_DV_FL_REDUCED_BLANKING", num_flags);
509 if (t->hratio && t->vratio)
510 or_str(flags, "V4L2_DV_FL_HAS_PICTURE_ASPECT", num_flags);
512 if (!memcmp(type, "VIC", 3) || !memcmp(type, "HDMI VIC", 8))
513 std = "V4L2_DV_BT_STD_CEA861";
514 else if (!memcmp(type, "DMT", 3))
515 std = "V4L2_DV_BT_STD_DMT";
516 else if (!memcmp(type, "CVT", 3))
517 std = "V4L2_DV_BT_STD_CVT";
518 else if (!memcmp(type, "GTF", 3))
519 std = "V4L2_DV_BT_STD_GTF";
520 printf("\t\t\t%s, \\\n", std);
521 printf("\t\t\t%s, \\\n", flags.empty() ? "0" : flags.c_str());
522 printf("\t\t\t{ %u, %u }, %u, %u) \\\n",
523 t->hratio, t->vratio, vic, hdmi_vic);
524 printf("\t}\n");
527 static void print_detailed_timing(unsigned indent, const struct timings *t)
529 printf("%*sHfront %4d Hsync %3u Hback %4d Hpol %s",
530 indent, "",
531 t->hfp, t->hsync, t->hbp, t->pos_pol_hsync ? "P" : "N");
532 if (t->hborder)
533 printf(" Hborder %u", t->hborder);
534 printf("\n");
536 printf("%*sVfront %4u Vsync %3u Vback %4d",
537 indent, "", t->vfp, t->vsync, t->vbp);
538 if (!t->no_pol_vsync)
539 printf(" Vpol %s", t->pos_pol_vsync ? "P" : "N");
540 if (t->vborder)
541 printf(" Vborder %u", t->vborder);
542 if (t->even_vtotal) {
543 printf(" Both Fields");
544 } else if (t->interlaced) {
545 printf(" Vfront +0.5 Odd Field\n");
546 printf("%*sVfront %4d Vsync %3u Vback %4d",
547 indent, "", t->vfp, t->vsync, t->vbp);
548 if (!t->no_pol_vsync)
549 printf(" Vpol %s", t->pos_pol_vsync ? "P" : "N");
550 if (t->vborder)
551 printf(" Vborder %u", t->vborder);
552 printf(" Vback +0.5 Even Field");
554 printf("\n");
557 bool edid_state::print_timings(const char *prefix, const struct timings *t,
558 const char *type, const char *flags,
559 bool detailed, bool do_checks, unsigned ntsc)
561 if (!t) {
562 // Should not happen
563 if (do_checks)
564 fail("Unknown video timings.\n");
565 return false;
568 if (detailed && options[OptShortTimings])
569 detailed = false;
570 if (options[OptLongTimings])
571 detailed = true;
573 unsigned vact = t->vact;
574 unsigned hbl = t->hfp + t->hsync + t->hbp + 2 * t->hborder;
575 unsigned vbl = t->vfp + t->vsync + t->vbp + 2 * t->vborder;
576 unsigned htotal = t->hact + hbl;
577 double hor_freq_khz = htotal ? (double)t->pixclk_khz / htotal : 0;
579 if (t->interlaced)
580 vact /= 2;
582 double out_hor_freq_khz = hor_freq_khz;
583 if (t->ycbcr420)
584 hor_freq_khz /= 2;
586 double vtotal = vact + vbl;
588 bool ok = true;
590 if (!t->hact || !hbl || !t->hfp || !t->hsync ||
591 !vact || !vbl || (!t->vfp && !t->interlaced && !t->even_vtotal) || !t->vsync) {
592 if (do_checks)
593 fail("0 values in the video timing:\n"
594 " Horizontal Active/Blanking %u/%u\n"
595 " Horizontal Frontporch/Sync Width %u/%u\n"
596 " Vertical Active/Blanking %u/%u\n"
597 " Vertical Frontporch/Sync Width %u/%u\n",
598 t->hact, hbl, t->hfp, t->hsync, vact, vbl, t->vfp, t->vsync);
599 ok = false;
602 if (t->even_vtotal)
603 vtotal = vact + t->vfp + t->vsync + t->vbp;
604 else if (t->interlaced)
605 vtotal = vact + t->vfp + t->vsync + t->vbp + 0.5;
607 double refresh = t->pixclk_khz * 1000.0 / (htotal * vtotal);
608 double pixclk = t->pixclk_khz * 1000.0;
609 if (((ntsc > 1 && options[OptNTSC]) || ntsc == 1) && fmod(refresh, 6.0) == 0) {
610 const double ntsc_fact = 1000.0 / 1001.0;
611 pixclk *= ntsc_fact;
612 refresh *= ntsc_fact;
613 out_hor_freq_khz *= ntsc_fact;
616 std::string s;
617 unsigned rb = t->rb & ~RB_ALT;
618 if (rb) {
619 bool alt = t->rb & RB_ALT;
620 s = "RB";
621 if (rb == RB_CVT_V2)
622 s += std::string("v2") + (alt ? ",video-optimized" : "");
623 else if (rb == RB_CVT_V3)
624 s += std::string("v3") + (alt ? ",h-blank-160" : "");
626 add_str(s, flags);
627 if (t->hsize_mm || t->vsize_mm)
628 add_str(s, std::to_string(t->hsize_mm) + " mm x " + std::to_string(t->vsize_mm) + " mm");
629 if (t->hsize_mm > dtd_max_hsize_mm)
630 dtd_max_hsize_mm = t->hsize_mm;
631 if (t->vsize_mm > dtd_max_vsize_mm)
632 dtd_max_vsize_mm = t->vsize_mm;
633 if (!s.empty())
634 s = " (" + s + ")";
635 unsigned pixclk_khz = t->pixclk_khz / (t->ycbcr420 ? 2 : 1);
637 char buf[10];
639 sprintf(buf, "%u%s", t->vact, t->interlaced ? "i" : "");
640 printf("%s%s: %5ux%-5s %10.6f Hz %3u:%-3u %8.3f kHz %13.6f MHz%s\n",
641 prefix, type,
642 t->hact, buf,
643 refresh,
644 t->hratio, t->vratio,
645 out_hor_freq_khz,
646 pixclk / 1000000.0,
647 s.c_str());
649 unsigned len = strlen(prefix) + 2;
651 if (!t->ycbcr420 && detailed && options[OptXModeLineTimings])
652 print_modeline(len, t, refresh);
653 else if (!t->ycbcr420 && detailed && options[OptFBModeTimings])
654 print_fbmode(len, t, refresh, hor_freq_khz);
655 else if (!t->ycbcr420 && detailed && options[OptV4L2Timings])
656 print_v4l2_timing(t, refresh, type);
657 else if (detailed)
658 print_detailed_timing(len + strlen(type) + 6, t);
660 if (!do_checks)
661 return ok;
663 if (!memcmp(type, "DTD", 3)) {
664 unsigned vic, dmt;
665 const timings *vic_t = cta_close_match_to_vic(*t, vic);
667 // We report this even if there is no CTA block since it
668 // is still likely that the actual VIC timings were intended.
669 if (vic_t)
670 warn("DTD is similar but not identical to VIC %u.\n", vic);
672 if (cta_matches_vic(*t, vic) && has_cta &&
673 !cta.preparsed_has_vic[0][vic]) {
674 warn("DTD is identical to VIC %u, which is not present in the CTA Ext Block.\n", vic);
676 if (cta.preparsed_max_vic_pixclk_khz && t->pixclk_khz > 340000 &&
677 t->pixclk_khz > cta.preparsed_max_vic_pixclk_khz)
678 cta.warn_about_hdmi_2x_dtd = true;
681 const timings *dmt_t = close_match_to_dmt(*t, dmt);
682 if (!vic_t && dmt_t)
683 warn("DTD is similar but not identical to DMT 0x%02x.\n", dmt);
686 if (refresh) {
687 min_vert_freq_hz = min(min_vert_freq_hz, refresh);
688 max_vert_freq_hz = max(max_vert_freq_hz, refresh);
690 if (hor_freq_khz) {
691 min_hor_freq_hz = min(min_hor_freq_hz, hor_freq_khz * 1000.0);
692 max_hor_freq_hz = max(max_hor_freq_hz, hor_freq_khz * 1000.0);
693 max_pixclk_khz = max(max_pixclk_khz, pixclk_khz);
694 if (t->pos_pol_hsync && !t->pos_pol_vsync && t->vsync == 3)
695 base.max_pos_neg_hor_freq_khz = hor_freq_khz;
698 if (t->ycbcr420 && t->pixclk_khz < 590000)
699 warn_once("Some YCbCr 4:2:0 timings are invalid for HDMI 2.1 (which requires an RGB timings pixel rate >= 590 MHz).\n");
700 if (t->hfp <= 0)
701 fail("0 or negative horizontal front porch.\n");
702 if (t->hbp <= 0)
703 fail("0 or negative horizontal back porch.\n");
704 if (t->vbp <= 0)
705 fail("0 or negative vertical back porch.\n");
706 if (!base.max_display_width_mm && !base.max_display_height_mm) {
707 /* this is valid */
708 } else if (!t->hsize_mm && !t->vsize_mm) {
709 /* this is valid */
710 } else if (t->hsize_mm > base.max_display_width_mm + 9 ||
711 t->vsize_mm > base.max_display_height_mm + 9) {
712 fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
713 t->hsize_mm, t->vsize_mm, base.max_display_width_mm, base.max_display_height_mm);
714 } else if (t->hsize_mm < base.max_display_width_mm - 9 &&
715 t->vsize_mm < base.max_display_height_mm - 9) {
716 fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
717 t->hsize_mm, t->vsize_mm, base.max_display_width_mm, base.max_display_height_mm);
719 return ok;
722 std::string containerid2s(const unsigned char *x)
724 char buf[40];
726 sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
727 x[0], x[1], x[2], x[3],
728 x[4], x[5],
729 x[6], x[7],
730 x[8], x[9],
731 x[10], x[11], x[12], x[13], x[14], x[15]);
732 return buf;
735 std::string utohex(unsigned char x)
737 char buf[10];
739 sprintf(buf, "0x%02hhx", x);
740 return buf;
743 const char *oui_name(unsigned oui, unsigned *ouinum)
745 unsigned ouinumscratch;
746 if (!ouinum) ouinum = &ouinumscratch;
747 const char *name;
748 switch (oui) {
749 #define oneoui(c,k,n) case c: *ouinum = kOUI_##k; name = n; break;
750 #include "oui.h"
751 default: *ouinum = 0; name = NULL; break;
753 return name;
756 void edid_state::data_block_oui(std::string block_name, const unsigned char *x,
757 unsigned length, unsigned *ouinum, bool ignorezeros, bool do_ascii, bool big_endian,
758 bool silent)
760 std::string buf;
761 char ascii[4];
762 unsigned oui;
763 const char *ouiname = NULL;
764 bool matched_reverse = false;
765 bool matched_ascii = false;
766 bool valid_ascii = false;
768 if (big_endian)
769 oui = ((length > 0 ? x[0] : 0) << 16) + ((length > 1 ? x[1] : 0) << 8) + (length > 2 ? x[2] : 0);
770 else
771 oui = ((length > 2 ? x[2] : 0) << 16) + ((length > 1 ? x[1] : 0) << 8) + (length > 0 ? x[0] : 0);
773 buf = ouitohex(oui);
774 if (length < 3) {
775 sprintf(ascii, "?"); // some characters are null
776 if (ouinum) *ouinum = 0; // doesn't match a known OUI
777 } else {
778 valid_ascii = (x[0] >= 'A' && x[1] >= 'A' && x[2] >= 'A' && x[0] <= 'Z' && x[1] <= 'Z' && x[2] <= 'Z');
779 sprintf(ascii, "%c%c%c", x[0], x[1], x[2]);
781 ouiname = oui_name(oui, ouinum);
782 if (!ouiname) {
783 big_endian = !big_endian;
784 unsigned reversedoui = ((oui & 0xff) << 16) + (oui & 0x00ff00) + (oui >> 16);
785 ouiname = oui_name(reversedoui, ouinum);
786 if (ouiname) {
787 oui = reversedoui;
788 buf = ouitohex(oui);
789 matched_reverse = true;
790 } else if (do_ascii && valid_ascii) {
791 unsigned asciioui = (x[0] << 24) + (x[1] << 16) + (x[2] << 8);
792 ouiname = oui_name(asciioui, ouinum);
793 if (ouiname) {
794 matched_ascii = true;
800 std::string name;
801 if (ouiname) {
802 if (matched_ascii)
803 name = block_name + " (" + ouiname + ")" + ", PNP ID '" + ascii + "'";
804 else
805 name = block_name + " (" + ouiname + ")" + ", OUI " + buf;
806 } else if (do_ascii && valid_ascii) {
807 name = block_name + ", PNP ID '" + ascii + "'";
808 } else {
809 name = block_name + ", OUI " + buf;
811 // assign string to data_block before outputting errors
812 data_block = name;
814 if (oui || !ignorezeros) {
815 if (!silent)
816 printf(" %s:\n", data_block.c_str());
817 if (length < 3)
818 fail("Data block length (%d) is not enough to contain an OUI.\n", length);
819 else if (ouiname) {
820 if (do_ascii && !valid_ascii)
821 warn("Expected PNP ID but found OUI.\n");
822 if (matched_reverse)
823 fail("Endian-ness (%s) of OUI is different than expected (%s).\n", big_endian ? "be" : "le", big_endian ? "le" : "be");
825 else {
826 if (valid_ascii)
827 warn("Unknown OUI %s (possible PNP %s).\n", buf.c_str(), ascii);
828 else
829 warn("Unknown OUI %s.\n", buf.c_str());
834 std::string ouitohex(unsigned oui)
836 char buf[32];
838 sprintf(buf, "%02X-%02X-%02X", (oui >> 16) & 0xff, (oui >> 8) & 0xff, oui & 0xff);
839 return buf;
842 bool memchk(const unsigned char *x, unsigned len, unsigned char v)
844 for (unsigned i = 0; i < len; i++)
845 if (x[i] != v)
846 return false;
847 return true;
850 void hex_block(const char *prefix, const unsigned char *x,
851 unsigned length, bool show_ascii, unsigned step)
853 unsigned i, j;
855 for (i = 0; i < length; i += step) {
856 unsigned len = min(step, length - i);
858 printf("%s", prefix);
859 for (j = 0; j < len; j++)
860 printf("%s%02x", j ? " " : "", x[i + j]);
862 if (show_ascii) {
863 for (j = len; j < step; j++)
864 printf(" ");
865 printf(" '");
866 for (j = 0; j < len; j++)
867 printf("%c", x[i + j] >= ' ' && x[i + j] <= '~' ? x[i + j] : '.');
868 printf("'");
870 printf("\n");
874 static bool edid_add_byte(const char *s, bool two_digits = true)
876 char buf[3];
878 if (state.edid_size == sizeof(edid))
879 return false;
880 buf[0] = s[0];
881 buf[1] = two_digits ? s[1] : 0;
882 buf[2] = 0;
883 edid[state.edid_size++] = strtoul(buf, NULL, 16);
884 return true;
887 static bool extract_edid_quantumdata(const char *start)
889 /* Parse QuantumData 980 EDID files */
890 do {
891 start = strstr(start, ">");
892 if (!start)
893 return false;
894 start++;
895 for (unsigned i = 0; start[i] && start[i + 1] && i < 256; i += 2)
896 if (!edid_add_byte(start + i))
897 return false;
898 start = strstr(start, "<BLOCK");
899 } while (start);
900 return state.edid_size;
903 static const char *ignore_chars = ",:;";
905 static bool extract_edid_hex(const char *s, bool require_two_digits = true)
907 for (; *s; s++) {
908 if (isspace(*s) || strchr(ignore_chars, *s))
909 continue;
911 if (*s == '0' && tolower(s[1]) == 'x') {
912 s++;
913 continue;
916 /* Read one or two hex digits from the log */
917 if (!isxdigit(s[0])) {
918 if (state.edid_size && state.edid_size % 128 == 0)
919 break;
920 return false;
922 if (require_two_digits && !isxdigit(s[1])) {
923 odd_hex_digits = true;
924 return false;
926 if (!edid_add_byte(s, isxdigit(s[1])))
927 return false;
928 if (isxdigit(s[1]))
929 s++;
931 return state.edid_size;
934 static bool extract_edid_xrandr(const char *start)
936 static const char indentation1[] = " ";
937 static const char indentation2[] = "\t\t";
938 /* Used to detect that we've gone past the EDID property */
939 static const char half_indentation1[] = " ";
940 static const char half_indentation2[] = "\t";
941 const char *indentation;
942 const char *s;
944 for (;;) {
945 unsigned j;
947 /* Get the next start of the line of EDID hex, assuming spaces for indentation */
948 s = strstr(start, indentation = indentation1);
949 /* Did we skip the start of another property? */
950 if (s && s > strstr(start, half_indentation1))
951 break;
953 /* If we failed, retry assuming tabs for indentation */
954 if (!s) {
955 s = strstr(start, indentation = indentation2);
956 /* Did we skip the start of another property? */
957 if (s && s > strstr(start, half_indentation2))
958 break;
961 if (!s)
962 break;
964 start = s + strlen(indentation);
966 for (j = 0; j < 16; j++, start += 2) {
967 /* Read a %02x from the log */
968 if (!isxdigit(start[0]) || !isxdigit(start[1])) {
969 if (j)
970 break;
971 return false;
973 if (!edid_add_byte(start))
974 return false;
977 return state.edid_size;
980 static bool extract_edid_xorg(const char *start)
982 bool find_first_num = true;
984 for (; *start; start++) {
985 if (find_first_num) {
986 const char *s;
988 /* skip ahead to the : */
989 s = strstr(start, ": \t");
990 if (!s)
991 s = strstr(start, ": ");
992 if (!s)
993 break;
994 start = s;
995 /* and find the first number */
996 while (!isxdigit(start[1]))
997 start++;
998 find_first_num = false;
999 continue;
1000 } else {
1001 /* Read a %02x from the log */
1002 if (!isxdigit(*start)) {
1003 find_first_num = true;
1004 continue;
1006 if (!edid_add_byte(start))
1007 return false;
1008 start++;
1011 return state.edid_size;
1014 static bool extract_edid(int fd, FILE *error)
1016 std::vector<char> edid_data;
1017 char buf[EDID_PAGE_SIZE];
1019 for (;;) {
1020 ssize_t i = read(fd, buf, sizeof(buf));
1022 if (i < 0)
1023 return false;
1024 if (i == 0)
1025 break;
1026 edid_data.insert(edid_data.end(), buf, buf + i);
1029 if (edid_data.empty()) {
1030 state.edid_size = 0;
1031 return false;
1033 // Ensure it is safely terminated by a 0 char
1034 edid_data.push_back('\0');
1036 const char *data = &edid_data[0];
1037 const char *start;
1039 /* Look for edid-decode output */
1040 start = strstr(data, "EDID (hex):");
1041 if (!start)
1042 start = strstr(data, "edid-decode (hex):");
1043 if (start)
1044 return extract_edid_hex(strchr(start, ':'));
1046 /* Look for C-array */
1047 start = strstr(data, "unsigned char edid[] = {");
1048 if (start)
1049 return extract_edid_hex(strchr(start, '{') + 1, false);
1051 /* Look for QuantumData EDID output */
1052 start = strstr(data, "<BLOCK");
1053 if (start)
1054 return extract_edid_quantumdata(start);
1056 /* Look for xrandr --verbose output (lines of 16 hex bytes) */
1057 start = strstr(data, "EDID_DATA:");
1058 if (!start)
1059 start = strstr(data, "EDID:");
1060 if (start)
1061 return extract_edid_xrandr(start);
1063 /* Look for an EDID in an Xorg.0.log file */
1064 start = strstr(data, "EDID (in hex):");
1065 if (start)
1066 start = strstr(start, "(II)");
1067 if (start)
1068 return extract_edid_xorg(start);
1070 unsigned i;
1072 /* Is the EDID provided in hex? */
1073 for (i = 0; i < 32 && (isspace(data[i]) || strchr(ignore_chars, data[i]) ||
1074 tolower(data[i]) == 'x' || isxdigit(data[i])); i++);
1076 if (i == 32)
1077 return extract_edid_hex(data);
1079 // Drop the extra '\0' byte since we now assume binary data
1080 edid_data.pop_back();
1082 /* Assume binary */
1083 if (edid_data.size() > sizeof(edid)) {
1084 fprintf(error, "Binary EDID length %zu is greater than %zu.\n",
1085 edid_data.size(), sizeof(edid));
1086 return false;
1088 memcpy(edid, data, edid_data.size());
1089 state.edid_size = edid_data.size();
1090 return true;
1093 static int edid_from_file(const char *from_file, FILE *error)
1095 #ifdef O_BINARY
1096 // Windows compatibility
1097 int flags = O_RDONLY | O_BINARY;
1098 #else
1099 int flags = O_RDONLY;
1100 #endif
1101 int fd;
1103 if (!strcmp(from_file, "-")) {
1104 from_file = "stdin";
1105 fd = 0;
1106 } else if ((fd = open(from_file, flags)) == -1) {
1107 perror(from_file);
1108 return -1;
1111 odd_hex_digits = false;
1112 if (!extract_edid(fd, error)) {
1113 if (!state.edid_size) {
1114 fprintf(error, "EDID of '%s' was empty.\n", from_file);
1115 return -1;
1117 fprintf(error, "EDID extract of '%s' failed: ", from_file);
1118 if (odd_hex_digits)
1119 fprintf(error, "odd number of hexadecimal digits.\n");
1120 else
1121 fprintf(error, "unknown format.\n");
1122 return -1;
1124 if (state.edid_size % EDID_PAGE_SIZE) {
1125 fprintf(error, "EDID length %u is not a multiple of %u.\n",
1126 state.edid_size, EDID_PAGE_SIZE);
1127 return -1;
1129 state.num_blocks = state.edid_size / EDID_PAGE_SIZE;
1130 if (fd != 0)
1131 close(fd);
1133 if (memcmp(edid, "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", 8)) {
1134 fprintf(error, "No EDID header found in '%s'.\n", from_file);
1135 return -1;
1137 return 0;
1140 /* generic extension code */
1142 std::string block_name(unsigned char block)
1144 char buf[10];
1146 switch (block) {
1147 case 0x00: return "Base EDID";
1148 case 0x02: return "CTA-861 Extension Block";
1149 case 0x10: return "Video Timing Extension Block";
1150 case 0x20: return "EDID 2.0 Extension Block";
1151 case 0x40: return "Display Information Extension Block";
1152 case 0x50: return "Localized String Extension Block";
1153 case 0x60: return "Microdisplay Interface Extension Block";
1154 case 0x70: return "DisplayID Extension Block";
1155 case 0xf0: return "Block Map Extension Block";
1156 case 0xff: return "Manufacturer-Specific Extension Block";
1157 default:
1158 sprintf(buf, " 0x%02x", block);
1159 return std::string("Unknown EDID Extension Block") + buf;
1163 void edid_state::parse_block_map(const unsigned char *x)
1165 unsigned last_valid_block_tag = 0;
1166 bool fail_once = false;
1167 unsigned offset = 1;
1168 unsigned i;
1170 if (block_nr == 1)
1171 block_map.saw_block_1 = true;
1172 else if (!block_map.saw_block_1)
1173 fail("No EDID Block Map Extension found in block 1.\n");
1174 else if (block_nr == 128)
1175 block_map.saw_block_128 = true;
1177 if (block_nr > 1)
1178 offset = 128;
1180 for (i = 1; i < 127; i++) {
1181 unsigned block = offset + i;
1183 if (x[i]) {
1184 last_valid_block_tag++;
1185 if (i != last_valid_block_tag && !fail_once) {
1186 fail("Valid block tags are not consecutive.\n");
1187 fail_once = true;
1189 printf(" Block %3u: %s\n", block, block_name(x[i]).c_str());
1190 if (block >= num_blocks) {
1191 if (!fail_once)
1192 fail("Invalid block number %u.\n", block);
1193 fail_once = true;
1194 } else if (x[i] != edid[block * EDID_PAGE_SIZE]) {
1195 fail("Block %u tag mismatch: expected 0x%02x, but got 0x%02x.\n",
1196 block, edid[block * EDID_PAGE_SIZE], x[i]);
1198 } else if (block < num_blocks) {
1199 fail("Block %u tag mismatch: expected 0x%02x, but got 0x00.\n",
1200 block, edid[block * EDID_PAGE_SIZE]);
1205 void edid_state::preparse_extension(unsigned char *x)
1207 switch (x[0]) {
1208 case 0x02:
1209 has_cta = true;
1210 preparse_cta_block(x);
1211 break;
1212 case 0x50:
1213 preparse_ls_ext_block(x);
1214 break;
1215 case 0x70:
1216 has_dispid = true;
1217 preparse_displayid_block(x);
1218 break;
1222 void edid_state::parse_extension(const unsigned char *x)
1224 block = block_name(x[0]);
1225 data_block.clear();
1226 unused_bytes = 0;
1228 printf("\n");
1229 if (block_nr && x[0] == 0)
1230 block = "Unknown EDID Extension Block 0x00";
1231 printf("Block %u, %s:\n", block_nr, block.c_str());
1233 switch (x[0]) {
1234 case 0x02:
1235 parse_cta_block(x);
1236 break;
1237 case 0x10:
1238 parse_vtb_ext_block(x);
1239 break;
1240 case 0x20:
1241 fail("Deprecated extension block for EDID 2.0, do not use.\n");
1242 break;
1243 case 0x40:
1244 parse_di_ext_block(x);
1245 break;
1246 case 0x50:
1247 parse_ls_ext_block(x);
1248 break;
1249 case 0x70:
1250 parse_displayid_block(x);
1251 break;
1252 case 0xf0:
1253 parse_block_map(x);
1254 if (block_nr != 1 && block_nr != 128)
1255 fail("Must be used in block 1 and 128.\n");
1256 break;
1257 default:
1258 hex_block(" ", x, EDID_PAGE_SIZE);
1259 fail("Unknown Extension Block.\n");
1260 break;
1263 data_block.clear();
1264 do_checksum("", x, EDID_PAGE_SIZE, EDID_PAGE_SIZE - 1, unused_bytes);
1267 void edid_state::print_preferred_timings()
1269 if (base.preferred_timing.is_valid()) {
1270 printf("\n----------------\n");
1271 printf("\nPreferred Video Timing if only Block 0 is parsed:\n");
1272 print_timings(" ", base.preferred_timing, true, false);
1275 if (!cta.preferred_timings.empty()) {
1276 printf("\n----------------\n");
1277 printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
1278 cta.preferred_timings.size() > 1 ? "s" : "");
1279 for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
1280 iter != cta.preferred_timings.end(); ++iter)
1281 print_timings(" ", *iter, true, false);
1284 if (!cta.preferred_timings_vfpdb.empty()) {
1285 printf("\n----------------\n");
1286 printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed with VFPDB support:\n",
1287 cta.preferred_timings_vfpdb.size() > 1 ? "s" : "");
1288 for (vec_timings_ext::iterator iter = cta.preferred_timings_vfpdb.begin();
1289 iter != cta.preferred_timings_vfpdb.end(); ++iter)
1290 print_timings(" ", *iter, true, false);
1293 if (!dispid.preferred_timings.empty()) {
1294 printf("\n----------------\n");
1295 printf("\nPreferred Video Timing%s if Block 0 and DisplayID Blocks are parsed:\n",
1296 dispid.preferred_timings.size() > 1 ? "s" : "");
1297 for (vec_timings_ext::iterator iter = dispid.preferred_timings.begin();
1298 iter != dispid.preferred_timings.end(); ++iter)
1299 print_timings(" ", *iter, true, false);
1303 void edid_state::print_native_res()
1305 typedef std::pair<unsigned, unsigned> resolution;
1306 typedef std::set<resolution> resolution_set;
1307 resolution_set native_prog, native_int, native_nvrdb;
1308 unsigned native_width = 0, native_height = 0;
1309 unsigned native_width_int = 0, native_height_int = 0;
1311 // Note: it is also a mismatch if Block 0 does not define a
1312 // native resolution, but other blocks do.
1313 bool native_mismatch = false;
1314 bool native_int_mismatch = false;
1316 if (base.preferred_timing.is_valid() && base.preferred_is_also_native) {
1317 if (base.preferred_timing.t.interlaced) {
1318 native_width_int = base.preferred_timing.t.hact;
1319 native_height_int = base.preferred_timing.t.vact;
1320 } else {
1321 native_width = base.preferred_timing.t.hact;
1322 native_height = base.preferred_timing.t.vact;
1326 if (!native_width && dispid.native_width) {
1327 native_width = dispid.native_width;
1328 native_height = dispid.native_height;
1329 native_mismatch = true;
1330 } else if (dispid.native_width && native_width &&
1331 (dispid.native_width != native_width ||
1332 dispid.native_height != native_height)) {
1333 native_mismatch = true;
1336 for (vec_timings_ext::iterator iter = cta.native_timings.begin();
1337 iter != cta.native_timings.end(); ++iter) {
1338 if (iter->t.interlaced) {
1339 native_int.insert(std::pair<unsigned, unsigned>(iter->t.hact, iter->t.vact));
1340 if (!native_width_int) {
1341 native_width_int = iter->t.hact;
1342 native_height_int = iter->t.vact;
1343 native_int_mismatch = true;
1344 } else if (native_width_int &&
1345 (iter->t.hact != native_width_int ||
1346 iter->t.vact != native_height_int)) {
1347 native_int_mismatch = true;
1349 } else {
1350 native_prog.insert(std::pair<unsigned, unsigned>(iter->t.hact, iter->t.vact));
1351 if (!native_width) {
1352 native_width = iter->t.hact;
1353 native_height = iter->t.vact;
1354 native_mismatch = true;
1355 } else if (native_width &&
1356 (iter->t.hact != native_width ||
1357 iter->t.vact != native_height)) {
1358 native_mismatch = true;
1363 for (vec_timings_ext::iterator iter = cta.native_timing_nvrdb.begin();
1364 iter != cta.native_timing_nvrdb.end(); ++iter) {
1365 if (iter->t.interlaced) {
1366 fail("Interlaced native timing in NVRDB.\n");
1367 } else {
1368 native_nvrdb.insert(std::pair<unsigned, unsigned>(iter->t.hact, iter->t.vact));
1369 if (!native_width) {
1370 native_width = iter->t.hact;
1371 native_height = iter->t.vact;
1372 native_mismatch = true;
1373 } else if (native_width &&
1374 (iter->t.hact != native_width ||
1375 iter->t.vact != native_height)) {
1376 native_mismatch = true;
1381 if (diagonal) {
1382 if (image_width) {
1383 double w = image_width;
1384 double h = image_height;
1385 double d = sqrt(w * w + h * h) / 254.0;
1387 if (fabs(diagonal - d) >= 0.1)
1388 warn("Specified diagonal is %.1f\", calculated diagonal is %.1f\".\n",
1389 diagonal, d);
1391 if (native_width) {
1392 double w = native_width;
1393 double h = native_height;
1394 double d = diagonal * 254.0;
1395 double c = sqrt((d * d) / (w * w + h * h));
1397 w *= c;
1398 h *= c;
1400 if (image_width) {
1401 printf("\n----------------\n");
1402 printf("\nCalculated image size for a diagonal of %.1f\" is %.1fx%.1fmm.\n",
1403 diagonal, w / 10.0, h / 10.0);
1405 if (fabs((double)image_width - w) >= 100.0 ||
1406 fabs((double)image_height - h) >= 100.0)
1407 warn("Calculated image size is %.1fx%.1fmm, EDID image size is %.1fx%.1fmm.\n",
1408 w / 10.0, h / 10.0,
1409 image_width / 10.0, image_height / 10.0);
1410 } else {
1411 warn("No image size was specified, but it is calculated as %.1fx%.1fmm.\n",
1412 w / 10.0, h / 10.0);
1417 if (!options[OptNativeResolution])
1418 return;
1420 if (native_width == 0 && native_width_int == 0) {
1421 printf("\n----------------\n");
1422 printf("\nNo Native Video Resolution was defined.\n");
1423 return;
1426 if ((native_width || native_width_int) &&
1427 !native_mismatch && !native_int_mismatch) {
1428 printf("\n----------------\n");
1429 printf("\nNative Video Resolution%s:\n",
1430 native_width && native_width_int ? "s" : "");
1431 if (native_width)
1432 printf(" %ux%u\n", native_width, native_height);
1433 if (native_width_int)
1434 printf(" %ux%ui\n", native_width_int, native_height_int);
1435 return;
1438 if (base.preferred_timing.is_valid() && base.preferred_is_also_native) {
1439 printf("\n----------------\n");
1440 printf("\nNative Video Resolution if only Block 0 is parsed:\n");
1441 printf(" %ux%u%s\n",
1442 base.preferred_timing.t.hact, base.preferred_timing.t.vact,
1443 base.preferred_timing.t.interlaced ? "i" : "");
1446 if (!cta.native_timings.empty()) {
1447 printf("\n----------------\n");
1448 printf("\nNative Video Resolution%s if Block 0 and CTA-861 Blocks are parsed:\n",
1449 native_prog.size() + native_int.size() > 1 ? "s" : "");
1450 for (resolution_set::iterator iter = native_prog.begin();
1451 iter != native_prog.end(); ++iter)
1452 printf(" %ux%u\n", iter->first, iter->second);
1453 for (resolution_set::iterator iter = native_int.begin();
1454 iter != native_int.end(); ++iter)
1455 printf(" %ux%ui\n", iter->first, iter->second);
1458 if (!cta.native_timing_nvrdb.empty()) {
1459 printf("\n----------------\n");
1460 printf("\nNative Video Resolution if Block 0 and CTA-861 Blocks are parsed with NVRDB support:\n");
1461 for (resolution_set::iterator iter = native_nvrdb.begin();
1462 iter != native_nvrdb.end(); ++iter)
1463 printf(" %ux%u\n", iter->first, iter->second);
1466 if (dispid.native_width) {
1467 printf("\n----------------\n");
1468 printf("\nNative Video Resolution if the DisplayID Blocks are parsed:\n");
1469 printf(" %ux%u\n", dispid.native_width, dispid.native_height);
1473 int edid_state::parse_edid()
1475 hide_serial_numbers = options[OptHideSerialNumbers];
1476 replace_unique_ids = options[OptReplaceUniqueIDs];
1478 preparse_base_block(edid);
1479 if (replace_unique_ids)
1480 replace_checksum(edid, EDID_PAGE_SIZE);
1482 for (unsigned i = 1; i < num_blocks; i++)
1483 preparse_extension(edid + i * EDID_PAGE_SIZE);
1485 if (options[OptPhysicalAddress]) {
1486 printf("%x.%x.%x.%x\n",
1487 (cta.preparsed_phys_addr >> 12) & 0xf,
1488 (cta.preparsed_phys_addr >> 8) & 0xf,
1489 (cta.preparsed_phys_addr >> 4) & 0xf,
1490 cta.preparsed_phys_addr & 0xf);
1491 return 0;
1494 if (!options[OptSkipHexDump]) {
1495 printf("edid-decode (hex):\n\n");
1496 for (unsigned i = 0; i < num_blocks; i++) {
1497 hex_block("", edid + i * EDID_PAGE_SIZE, EDID_PAGE_SIZE, false);
1498 if (i == num_blocks - 1 && options[OptOnlyHexDump])
1499 return 0;
1500 printf("\n");
1502 printf("----------------\n\n");
1505 block = block_name(0x00);
1506 printf("Block %u, %s:\n", block_nr, block.c_str());
1507 parse_base_block(edid);
1509 for (unsigned i = 1; i < num_blocks; i++) {
1510 block_nr++;
1511 printf("\n----------------\n");
1512 parse_extension(edid + i * EDID_PAGE_SIZE);
1515 block = "";
1516 block_nr = EDID_MAX_BLOCKS;
1518 if (cta.has_svrs)
1519 cta_resolve_svrs();
1521 if (options[OptPreferredTimings])
1522 print_preferred_timings();
1524 print_native_res();
1526 if (!options[OptCheck] && !options[OptCheckInline])
1527 return 0;
1529 check_base_block(edid);
1530 if (has_cta)
1531 check_cta_blocks();
1532 if (has_dispid)
1533 check_displayid_blocks();
1535 printf("\n----------------\n");
1537 if (!options[OptSkipSHA] && strlen(STRING(SHA))) {
1538 options[OptSkipSHA] = 1;
1539 printf("\nedid-decode SHA: %s %s\n", STRING(SHA), STRING(DATE));
1542 if (options[OptCheck]) {
1543 if (warnings)
1544 show_msgs(true);
1545 if (failures)
1546 show_msgs(false);
1548 printf("\nEDID conformity: %s\n", failures ? "FAIL" : "PASS");
1549 return failures ? -2 : 0;
1552 /* InfoFrame parsing */
1554 static unsigned char infoframe[32];
1555 static unsigned if_size;
1557 static bool if_add_byte(const char *s)
1559 char buf[3];
1561 if (if_size == sizeof(infoframe))
1562 return false;
1563 buf[0] = s[0];
1564 buf[1] = s[1];
1565 buf[2] = 0;
1566 infoframe[if_size++] = strtoul(buf, NULL, 16);
1567 return true;
1570 static bool extract_if_hex(const char *s)
1572 for (; *s; s++) {
1573 if (isspace(*s))
1574 continue;
1576 /* Read one or two hex digits from the log */
1577 if (!isxdigit(s[0]))
1578 break;
1580 if (!isxdigit(s[1])) {
1581 odd_hex_digits = true;
1582 return false;
1584 if (!if_add_byte(s))
1585 return false;
1586 s++;
1588 return if_size;
1591 static bool extract_if(int fd)
1593 std::vector<char> if_data;
1594 char buf[128];
1596 for (;;) {
1597 ssize_t i = read(fd, buf, sizeof(buf));
1599 if (i < 0)
1600 return false;
1601 if (i == 0)
1602 break;
1603 if_data.insert(if_data.end(), buf, buf + i);
1606 if (if_data.empty()) {
1607 if_size = 0;
1608 return false;
1610 // Ensure it is safely terminated by a 0 char
1611 if_data.push_back('\0');
1613 const char *data = &if_data[0];
1614 const char *start;
1616 /* Look for edid-decode output */
1617 start = strstr(data, "edid-decode InfoFrame (hex):");
1618 if (start)
1619 return extract_if_hex(strchr(start, ':') + 1);
1621 // Drop the extra '\0' byte since we now assume binary data
1622 if_data.pop_back();
1624 if_size = if_data.size();
1626 /* Assume binary */
1627 if (if_size > sizeof(infoframe)) {
1628 fprintf(stderr, "Binary InfoFrame length %u is greater than %zu.\n",
1629 if_size, sizeof(infoframe));
1630 return false;
1632 memcpy(infoframe, data, if_size);
1633 return true;
1636 static int if_from_file(const char *from_file)
1638 #ifdef O_BINARY
1639 // Windows compatibility
1640 int flags = O_RDONLY | O_BINARY;
1641 #else
1642 int flags = O_RDONLY;
1643 #endif
1644 int fd;
1646 memset(infoframe, 0, sizeof(infoframe));
1647 if_size = 0;
1649 if ((fd = open(from_file, flags)) == -1) {
1650 perror(from_file);
1651 return -1;
1654 odd_hex_digits = false;
1655 if (!extract_if(fd)) {
1656 if (!if_size) {
1657 fprintf(stderr, "InfoFrame of '%s' was empty.\n", from_file);
1658 return -1;
1660 fprintf(stderr, "InfoFrame extraction of '%s' failed: ", from_file);
1661 if (odd_hex_digits)
1662 fprintf(stderr, "odd number of hexadecimal digits.\n");
1663 else
1664 fprintf(stderr, "unknown format.\n");
1665 return -1;
1667 close(fd);
1669 return 0;
1672 static void show_if_msgs(bool is_warn)
1674 printf("\n%s:\n\n", is_warn ? "Warnings" : "Failures");
1675 if (s_msgs[0][is_warn].empty())
1676 return;
1677 printf("InfoFrame:\n%s",
1678 s_msgs[0][is_warn].c_str());
1681 int edid_state::parse_if(const std::string &fname)
1683 int ret = if_from_file(fname.c_str());
1684 unsigned min_size = 4;
1685 bool is_hdmi = false;
1687 if (ret)
1688 return ret;
1690 state.block_nr = 0;
1691 state.data_block.clear();
1693 if (!options[OptSkipHexDump]) {
1694 printf("edid-decode InfoFrame (hex):\n\n");
1695 hex_block("", infoframe, if_size, false);
1696 if (options[OptOnlyHexDump])
1697 return 0;
1698 printf("\n----------------\n\n");
1701 if (infoframe[0] >= 0x80) {
1702 is_hdmi = true;
1703 min_size++;
1706 if (if_size < min_size) {
1707 fail("InfoFrame is too small to parse.\n");
1708 return -1;
1711 if (is_hdmi) {
1712 do_checksum("HDMI InfoFrame ", infoframe, if_size, 3);
1713 printf("\n");
1714 memcpy(infoframe + 3, infoframe + 4, if_size - 4);
1715 infoframe[0] &= 0x7f;
1716 if_size--;
1719 switch (infoframe[0]) {
1720 case 0x01:
1721 parse_if_vendor(infoframe, if_size);
1722 break;
1723 case 0x02:
1724 parse_if_avi(infoframe, if_size);
1725 break;
1726 case 0x03:
1727 parse_if_spd(infoframe, if_size);
1728 break;
1729 case 0x04:
1730 parse_if_audio(infoframe, if_size);
1731 break;
1732 case 0x05:
1733 parse_if_mpeg_source(infoframe, if_size);
1734 break;
1735 case 0x06:
1736 parse_if_ntsc_vbi(infoframe, if_size);
1737 break;
1738 case 0x07:
1739 parse_if_drm(infoframe, if_size);
1740 break;
1741 default:
1742 if (infoframe[0] <= 0x1f)
1743 fail("Reserved InfoFrame type %hhx.\n", infoframe[0]);
1744 else
1745 fail("Forbidden InfoFrame type %hhx.\n", infoframe[0]);
1746 break;
1749 if (!options[OptCheck] && !options[OptCheckInline])
1750 return 0;
1752 printf("\n----------------\n");
1754 if (!options[OptSkipSHA] && strlen(STRING(SHA))) {
1755 options[OptSkipSHA] = 1;
1756 printf("\nedid-decode SHA: %s %s\n", STRING(SHA), STRING(DATE));
1759 if (options[OptCheck]) {
1760 if (warnings)
1761 show_if_msgs(true);
1762 if (failures)
1763 show_if_msgs(false);
1766 printf("\n%s conformity: %s\n",
1767 state.data_block.empty() ? "InfoFrame" : state.data_block.c_str(),
1768 failures ? "FAIL" : "PASS");
1769 return failures ? -2 : 0;
1772 #ifndef __EMSCRIPTEN__
1774 static unsigned char crc_calc(const unsigned char *b)
1776 unsigned char sum = 0;
1777 unsigned i;
1779 for (i = 0; i < 127; i++)
1780 sum += b[i];
1781 return 256 - sum;
1784 static int crc_ok(const unsigned char *b)
1786 return crc_calc(b) == b[127];
1789 static void hexdumpedid(FILE *f, const unsigned char *edid, unsigned size)
1791 unsigned b, i, j;
1793 for (b = 0; b < size / 128; b++) {
1794 const unsigned char *buf = edid + 128 * b;
1796 if (b)
1797 fprintf(f, "\n");
1798 for (i = 0; i < 128; i += 0x10) {
1799 fprintf(f, "%02x", buf[i]);
1800 for (j = 1; j < 0x10; j++) {
1801 fprintf(f, " %02x", buf[i + j]);
1803 fprintf(f, "\n");
1805 if (!crc_ok(buf))
1806 fprintf(f, "Block %u has a checksum error (should be 0x%02x).\n",
1807 b, crc_calc(buf));
1811 static void carraydumpedid(FILE *f, const unsigned char *edid, unsigned size)
1813 unsigned b, i, j;
1815 fprintf(f, "const unsigned char edid[] = {\n");
1816 for (b = 0; b < size / 128; b++) {
1817 const unsigned char *buf = edid + 128 * b;
1819 if (b)
1820 fprintf(f, "\n");
1821 for (i = 0; i < 128; i += 8) {
1822 fprintf(f, "\t0x%02x,", buf[i]);
1823 for (j = 1; j < 8; j++) {
1824 fprintf(f, " 0x%02x,", buf[i + j]);
1826 fprintf(f, "\n");
1828 if (!crc_ok(buf))
1829 fprintf(f, "\t/* Block %u has a checksum error (should be 0x%02x). */\n",
1830 b, crc_calc(buf));
1832 fprintf(f, "};\n");
1835 // This format can be read by the QuantumData EDID editor
1836 static void xmldumpedid(FILE *f, const unsigned char *edid, unsigned size)
1838 fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
1839 fprintf(f, "<DATAOBJ>\n");
1840 fprintf(f, " <HEADER TYPE=\"DID\" VERSION=\"1.0\"/>\n");
1841 fprintf(f, " <DATA>\n");
1842 for (unsigned b = 0; b < size / 128; b++) {
1843 const unsigned char *buf = edid + 128 * b;
1845 fprintf(f, " <BLOCK%u>", b);
1846 for (unsigned i = 0; i < 128; i++)
1847 fprintf(f, "%02X", buf[i]);
1848 fprintf(f, "</BLOCK%u>\n", b);
1850 fprintf(f, " </DATA>\n");
1851 fprintf(f, "</DATAOBJ>\n");
1854 static int edid_to_file(const char *to_file, enum output_format out_fmt)
1856 FILE *out;
1858 if (!strcmp(to_file, "-")) {
1859 to_file = "stdout";
1860 out = stdout;
1861 } else if ((out = fopen(to_file, "w")) == NULL) {
1862 perror(to_file);
1863 return -1;
1865 if (out_fmt == OUT_FMT_DEFAULT)
1866 out_fmt = out == stdout ? OUT_FMT_HEX : OUT_FMT_RAW;
1868 switch (out_fmt) {
1869 default:
1870 case OUT_FMT_HEX:
1871 hexdumpedid(out, edid, state.edid_size);
1872 break;
1873 case OUT_FMT_RAW:
1874 fwrite(edid, state.edid_size, 1, out);
1875 break;
1876 case OUT_FMT_CARRAY:
1877 carraydumpedid(out, edid, state.edid_size);
1878 break;
1879 case OUT_FMT_XML:
1880 xmldumpedid(out, edid, state.edid_size);
1881 break;
1884 if (out != stdout)
1885 fclose(out);
1886 return 0;
1889 enum cvt_opts {
1890 CVT_WIDTH = 0,
1891 CVT_HEIGHT,
1892 CVT_FPS,
1893 CVT_INTERLACED,
1894 CVT_OVERSCAN,
1895 CVT_RB,
1896 CVT_ALT,
1897 CVT_RB_H_BLANK,
1898 CVT_RB_V_BLANK,
1899 CVT_EARLY_VSYNC,
1902 static int parse_cvt_subopt(char **subopt_str, double *value)
1904 int opt;
1905 char *opt_str;
1907 static const char * const subopt_list[] = {
1908 "w",
1909 "h",
1910 "fps",
1911 "interlaced",
1912 "overscan",
1913 "rb",
1914 "alt",
1915 "hblank",
1916 "vblank",
1917 "early-vsync",
1918 nullptr
1921 opt = getsubopt(subopt_str, (char* const*) subopt_list, &opt_str);
1923 if (opt == -1) {
1924 fprintf(stderr, "Invalid suboptions specified.\n");
1925 usage();
1926 std::exit(EXIT_FAILURE);
1928 if (opt_str == nullptr && opt != CVT_INTERLACED && opt != CVT_ALT &&
1929 opt != CVT_OVERSCAN && opt != CVT_EARLY_VSYNC) {
1930 fprintf(stderr, "No value given to suboption <%s>.\n",
1931 subopt_list[opt]);
1932 usage();
1933 std::exit(EXIT_FAILURE);
1936 if (opt_str)
1937 *value = strtod(opt_str, nullptr);
1938 return opt;
1941 static void parse_cvt(char *optarg)
1943 unsigned w = 0, h = 0;
1944 double fps = 0;
1945 unsigned rb = RB_NONE;
1946 unsigned rb_h_blank = 0;
1947 unsigned rb_v_blank = 460;
1948 bool interlaced = false;
1949 bool alt = false;
1950 bool overscan = false;
1951 bool early_vsync = false;
1953 while (*optarg != '\0') {
1954 int opt;
1955 double opt_val;
1957 opt = parse_cvt_subopt(&optarg, &opt_val);
1959 switch (opt) {
1960 case CVT_WIDTH:
1961 w = round(opt_val);
1962 break;
1963 case CVT_HEIGHT:
1964 h = round(opt_val);
1965 break;
1966 case CVT_FPS:
1967 fps = opt_val;
1968 break;
1969 case CVT_RB:
1970 rb = opt_val;
1971 break;
1972 case CVT_OVERSCAN:
1973 overscan = true;
1974 break;
1975 case CVT_INTERLACED:
1976 interlaced = opt_val;
1977 break;
1978 case CVT_ALT:
1979 alt = opt_val;
1980 break;
1981 case CVT_RB_H_BLANK:
1982 rb_h_blank = opt_val;
1983 break;
1984 case CVT_RB_V_BLANK:
1985 rb_v_blank = opt_val;
1986 if (rb_v_blank < 460) {
1987 fprintf(stderr, "vblank must be >= 460, set to 460.\n");
1988 rb_v_blank = 460;
1989 } else if (rb_v_blank > 705) {
1990 fprintf(stderr, "warning: vblank values > 705 might not be supported by RBv3 compliant sources.\n");
1992 break;
1993 case CVT_EARLY_VSYNC:
1994 early_vsync = true;
1995 break;
1996 default:
1997 break;
2001 if (!w || !h || !fps) {
2002 fprintf(stderr, "Missing width, height and/or fps.\n");
2003 usage();
2004 std::exit(EXIT_FAILURE);
2006 if (interlaced)
2007 fps /= 2;
2008 timings t = state.calc_cvt_mode(w, h, fps, rb, interlaced, overscan, alt,
2009 rb_h_blank, rb_v_blank, early_vsync);
2010 state.print_timings("", &t, "CVT", "", true, false);
2013 struct gtf_parsed_data {
2014 unsigned w, h;
2015 double freq;
2016 double C, M, K, J;
2017 bool overscan;
2018 bool interlaced;
2019 bool secondary;
2020 bool params_from_edid;
2021 enum gtf_ip_parm ip_parm;
2024 enum gtf_opts {
2025 GTF_WIDTH = 0,
2026 GTF_HEIGHT,
2027 GTF_FPS,
2028 GTF_HORFREQ,
2029 GTF_PIXCLK,
2030 GTF_INTERLACED,
2031 GTF_OVERSCAN,
2032 GTF_SECONDARY,
2033 GTF_C2,
2034 GTF_M,
2035 GTF_K,
2036 GTF_J2,
2039 static int parse_gtf_subopt(char **subopt_str, double *value)
2041 int opt;
2042 char *opt_str;
2044 static const char * const subopt_list[] = {
2045 "w",
2046 "h",
2047 "fps",
2048 "horfreq",
2049 "pixclk",
2050 "interlaced",
2051 "overscan",
2052 "secondary",
2053 "C",
2054 "M",
2055 "K",
2056 "J",
2057 nullptr
2060 opt = getsubopt(subopt_str, (char * const *)subopt_list, &opt_str);
2062 if (opt == -1) {
2063 fprintf(stderr, "Invalid suboptions specified.\n");
2064 usage();
2065 std::exit(EXIT_FAILURE);
2067 if (opt_str == nullptr && opt != GTF_INTERLACED && opt != GTF_OVERSCAN &&
2068 opt != GTF_SECONDARY) {
2069 fprintf(stderr, "No value given to suboption <%s>.\n",
2070 subopt_list[opt]);
2071 usage();
2072 std::exit(EXIT_FAILURE);
2075 if (opt == GTF_C2 || opt == GTF_J2)
2076 *value = round(2.0 * strtod(opt_str, nullptr));
2077 else if (opt_str)
2078 *value = strtod(opt_str, nullptr);
2079 return opt;
2082 static void parse_gtf(char *optarg, gtf_parsed_data &data)
2084 memset(&data, 0, sizeof(data));
2085 data.params_from_edid = true;
2086 data.C = 40;
2087 data.M = 600;
2088 data.K = 128;
2089 data.J = 20;
2091 while (*optarg != '\0') {
2092 int opt;
2093 double opt_val;
2095 opt = parse_gtf_subopt(&optarg, &opt_val);
2097 switch (opt) {
2098 case GTF_WIDTH:
2099 data.w = round(opt_val);
2100 break;
2101 case GTF_HEIGHT:
2102 data.h = round(opt_val);
2103 break;
2104 case GTF_FPS:
2105 data.freq = opt_val;
2106 data.ip_parm = gtf_ip_vert_freq;
2107 break;
2108 case GTF_HORFREQ:
2109 data.freq = opt_val;
2110 data.ip_parm = gtf_ip_hor_freq;
2111 break;
2112 case GTF_PIXCLK:
2113 data.freq = opt_val;
2114 data.ip_parm = gtf_ip_clk_freq;
2115 break;
2116 case GTF_INTERLACED:
2117 data.interlaced = true;
2118 break;
2119 case GTF_OVERSCAN:
2120 data.overscan = true;
2121 break;
2122 case GTF_SECONDARY:
2123 data.secondary = true;
2124 break;
2125 case GTF_C2:
2126 data.C = opt_val / 2.0;
2127 data.params_from_edid = false;
2128 break;
2129 case GTF_M:
2130 data.M = round(opt_val);
2131 data.params_from_edid = false;
2132 break;
2133 case GTF_K:
2134 data.K = round(opt_val);
2135 data.params_from_edid = false;
2136 break;
2137 case GTF_J2:
2138 data.J = opt_val / 2.0;
2139 data.params_from_edid = false;
2140 break;
2141 default:
2142 break;
2146 if (!data.w || !data.h) {
2147 fprintf(stderr, "Missing width and/or height.\n");
2148 usage();
2149 std::exit(EXIT_FAILURE);
2151 if (!data.freq) {
2152 fprintf(stderr, "One of fps, horfreq or pixclk must be given.\n");
2153 usage();
2154 std::exit(EXIT_FAILURE);
2156 if (!data.secondary)
2157 data.params_from_edid = false;
2158 if (data.interlaced && data.ip_parm == gtf_ip_vert_freq)
2159 data.freq /= 2;
2162 static void show_gtf(gtf_parsed_data &data)
2164 timings t;
2166 t = state.calc_gtf_mode(data.w, data.h, data.freq, data.interlaced,
2167 data.ip_parm, data.overscan, data.secondary,
2168 data.C, data.M, data.K, data.J);
2169 calc_ratio(&t);
2170 state.print_timings("", &t, "GTF", "", true, false);
2173 enum ovt_opts {
2174 OVT_RID,
2175 OVT_WIDTH,
2176 OVT_HEIGHT,
2177 OVT_FPS,
2180 static int parse_ovt_subopt(char **subopt_str, unsigned *value)
2182 int opt;
2183 char *opt_str;
2185 static const char * const subopt_list[] = {
2186 "rid",
2187 "w",
2188 "h",
2189 "fps",
2190 nullptr
2193 opt = getsubopt(subopt_str, (char* const*) subopt_list, &opt_str);
2195 if (opt == -1) {
2196 fprintf(stderr, "Invalid suboptions specified.\n");
2197 usage();
2198 std::exit(EXIT_FAILURE);
2200 if (opt_str == nullptr) {
2201 fprintf(stderr, "No value given to suboption <%s>.\n",
2202 subopt_list[opt]);
2203 usage();
2204 std::exit(EXIT_FAILURE);
2207 if (opt_str)
2208 *value = strtoul(opt_str, NULL, 0);
2209 return opt;
2212 static void parse_ovt(char *optarg)
2214 unsigned rid = 0;
2215 unsigned w = 0, h = 0;
2216 unsigned fps = 0;
2218 while (*optarg != '\0') {
2219 int opt;
2220 unsigned opt_val;
2222 opt = parse_ovt_subopt(&optarg, &opt_val);
2224 switch (opt) {
2225 case OVT_RID:
2226 rid = opt_val;
2227 break;
2228 case OVT_WIDTH:
2229 w = opt_val;
2230 break;
2231 case OVT_HEIGHT:
2232 h = opt_val;
2233 break;
2234 case OVT_FPS:
2235 fps = opt_val;
2236 break;
2237 default:
2238 break;
2242 if ((!rid && (!w || !h)) || !fps) {
2243 fprintf(stderr, "Missing rid, width, height and/or fps.\n");
2244 usage();
2245 std::exit(EXIT_FAILURE);
2247 unsigned hratio = 0, vratio = 0;
2248 if (rid) {
2249 const cta_rid *r = find_rid(rid);
2251 if (r) {
2252 w = r->hact;
2253 h = r->vact;
2254 hratio = r->hratio;
2255 vratio = r->vratio;
2258 timings t = state.calc_ovt_mode(w, h, hratio, vratio, fps);
2259 state.print_timings("", &t, "OVT", "", true, false);
2262 int main(int argc, char **argv)
2264 char short_options[26 * 2 * 2 + 1];
2265 enum output_format out_fmt = OUT_FMT_DEFAULT;
2266 gtf_parsed_data gtf_data;
2267 unsigned list_rid = 0;
2268 std::vector<std::string> if_names;
2269 int ret;
2271 while (1) {
2272 int option_index = 0;
2273 unsigned idx = 0;
2274 unsigned i, val;
2275 const timings *t;
2276 char buf[16];
2278 for (i = 0; long_options[i].name; i++) {
2279 if (!isalpha(long_options[i].val))
2280 continue;
2281 short_options[idx++] = long_options[i].val;
2282 if (long_options[i].has_arg == required_argument)
2283 short_options[idx++] = ':';
2285 short_options[idx] = 0;
2286 int ch = getopt_long(argc, argv, short_options,
2287 long_options, &option_index);
2288 if (ch == -1)
2289 break;
2291 options[ch] = 1;
2292 switch (ch) {
2293 case OptHelp:
2294 usage();
2295 return -1;
2296 case OptOutputFormat:
2297 if (!strcmp(optarg, "hex")) {
2298 out_fmt = OUT_FMT_HEX;
2299 } else if (!strcmp(optarg, "raw")) {
2300 out_fmt = OUT_FMT_RAW;
2301 } else if (!strcmp(optarg, "carray")) {
2302 out_fmt = OUT_FMT_CARRAY;
2303 } else if (!strcmp(optarg, "xml")) {
2304 out_fmt = OUT_FMT_XML;
2305 } else {
2306 usage();
2307 exit(1);
2309 break;
2310 case OptDiag:
2311 state.diagonal = strtod(optarg, NULL);
2312 break;
2313 case OptSTD: {
2314 unsigned char byte1, byte2 = 0;
2315 char *endptr;
2317 byte1 = strtoul(optarg, &endptr, 0);
2318 if (*endptr == ',')
2319 byte2 = strtoul(endptr + 1, NULL, 0);
2320 state.print_standard_timing("", byte1, byte2, false, true);
2321 break;
2323 case OptDMT:
2324 val = strtoul(optarg, NULL, 0);
2325 t = find_dmt_id(val);
2326 if (t) {
2327 sprintf(buf, "DMT 0x%02x", val);
2328 state.print_timings("", t, buf, "", true, false);
2329 } else {
2330 fprintf(stderr, "Unknown DMT code 0x%02x.\n", val);
2332 break;
2333 case OptVIC:
2334 val = strtoul(optarg, NULL, 0);
2335 t = find_vic_id(val);
2336 if (t) {
2337 sprintf(buf, "VIC %3u", val);
2338 state.print_timings("", t, buf, "", true, false);
2339 } else {
2340 fprintf(stderr, "Unknown VIC code %u.\n", val);
2342 break;
2343 case OptHDMIVIC:
2344 val = strtoul(optarg, NULL, 0);
2345 t = find_hdmi_vic_id(val);
2346 if (t) {
2347 sprintf(buf, "HDMI VIC %u", val);
2348 state.print_timings("", t, buf, "", true, false);
2349 } else {
2350 fprintf(stderr, "Unknown HDMI VIC code %u.\n", val);
2352 break;
2353 case OptCVT:
2354 parse_cvt(optarg);
2355 break;
2356 case OptGTF:
2357 parse_gtf(optarg, gtf_data);
2358 break;
2359 case OptOVT:
2360 parse_ovt(optarg);
2361 break;
2362 case OptListRIDTimings:
2363 list_rid = strtoul(optarg, NULL, 0);
2364 break;
2365 case OptInfoFrame:
2366 if_names.push_back(optarg);
2367 break;
2368 case ':':
2369 fprintf(stderr, "Option '%s' requires a value.\n",
2370 argv[optind]);
2371 usage();
2372 return -1;
2373 case '?':
2374 fprintf(stderr, "Unknown argument '%s'.\n",
2375 argv[optind]);
2376 usage();
2377 return -1;
2380 if (optind == argc && options[OptVersion]) {
2381 if (strlen(STRING(SHA)))
2382 printf("edid-decode SHA: %s %s\n", STRING(SHA), STRING(DATE));
2383 else
2384 printf("edid-decode SHA: not available\n");
2385 return 0;
2388 if (options[OptListEstTimings])
2389 state.list_established_timings();
2390 if (options[OptListDMTs])
2391 state.list_dmts();
2392 if (options[OptListVICs])
2393 state.cta_list_vics();
2394 if (options[OptListHDMIVICs])
2395 state.cta_list_hdmi_vics();
2396 if (options[OptListRIDs])
2397 state.cta_list_rids();
2398 if (options[OptListRIDTimings])
2399 state.cta_list_rid_timings(list_rid);
2401 if (options[OptListEstTimings] || options[OptListDMTs] ||
2402 options[OptListVICs] || options[OptListHDMIVICs] ||
2403 options[OptListRIDs] || options[OptListRIDTimings])
2404 return 0;
2406 if (options[OptCVT] || options[OptDMT] || options[OptVIC] ||
2407 options[OptHDMIVIC] || options[OptSTD] || options[OptOVT])
2408 return 0;
2410 if (options[OptGTF] && (!gtf_data.params_from_edid || optind == argc)) {
2411 show_gtf(gtf_data);
2412 return 0;
2415 if (optind == argc) {
2416 if (options[OptInfoFrame] && !options[OptGTF])
2417 ret = 0;
2418 else
2419 ret = edid_from_file("-", stdout);
2420 } else {
2421 ret = edid_from_file(argv[optind], argv[optind + 1] ? stderr : stdout);
2424 if (ret && options[OptPhysicalAddress]) {
2425 printf("f.f.f.f\n");
2426 return 0;
2428 if (optind < argc - 1)
2429 return ret ? ret : edid_to_file(argv[optind + 1], out_fmt);
2431 if (options[OptGTF]) {
2432 timings t;
2434 state.preparse_base_block(edid);
2436 t = state.calc_gtf_mode(gtf_data.w, gtf_data.h, gtf_data.freq,
2437 gtf_data.interlaced, gtf_data.ip_parm,
2438 gtf_data.overscan);
2439 unsigned hbl = t.hfp + t.hsync + t.hbp;
2440 unsigned htotal = t.hact + hbl;
2441 double hor_freq_khz = htotal ? (double)t.pixclk_khz / htotal : 0;
2443 if (state.base.supports_sec_gtf &&
2444 hor_freq_khz >= state.base.sec_gtf_start_freq) {
2445 t = state.calc_gtf_mode(gtf_data.w, gtf_data.h, gtf_data.freq,
2446 gtf_data.interlaced, gtf_data.ip_parm,
2447 gtf_data.overscan, true,
2448 state.base.C, state.base.M,
2449 state.base.K, state.base.J);
2451 calc_ratio(&t);
2452 if (t.hfp <= 0)
2453 state.print_timings("", &t, "GTF", "INVALID: Hfront <= 0", true, false);
2454 else
2455 state.print_timings("", &t, "GTF", "", true, false);
2456 return 0;
2459 if (!ret && state.edid_size)
2460 ret = state.parse_edid();
2462 bool show_line = state.edid_size;
2464 for (const auto &n : if_names) {
2465 if (show_line)
2466 printf("\n================\n\n");
2467 show_line = true;
2469 state.warnings = state.failures = 0;
2470 for (unsigned i = 0; i < EDID_MAX_BLOCKS + 1; i++) {
2471 s_msgs[i][0].clear();
2472 s_msgs[i][1].clear();
2474 int r = state.parse_if(n);
2475 if (r && !ret)
2476 ret = r;
2478 return ret;
2481 #else
2484 * The surrounding JavaScript implementation will call this function
2485 * each time it wants to decode an EDID. So this should reset all the
2486 * state and start over.
2488 extern "C" int parse_edid(const char *input)
2490 for (unsigned i = 0; i < EDID_MAX_BLOCKS + 1; i++) {
2491 s_msgs[i][0].clear();
2492 s_msgs[i][1].clear();
2494 options[OptCheck] = 1;
2495 options[OptPreferredTimings] = 1;
2496 options[OptNativeResolution] = 1;
2497 options[OptSkipSHA] = 0;
2498 state = edid_state();
2499 int ret = edid_from_file(input, stderr);
2500 return ret ? ret : state.parse_edid();
2503 #endif