edid-decode: export speaker_map as cta_speaker_map
[edid-decode.git] / edid-decode.cpp
blob70454812e2057e775380c958360fde0f538b85f9
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 OptLongTimings = 'L',
49 OptNativeResolution = 'n',
50 OptNTSC = 'N',
51 OptOutputFormat = 'o',
52 OptPreferredTimings = 'p',
53 OptPhysicalAddress = 'P',
54 OptSkipHexDump = 's',
55 OptShortTimings = 'S',
56 OptV4L2Timings = 'V',
57 OptXModeLineTimings = 'X',
58 OptSkipSHA = 128,
59 OptHideSerialNumbers,
60 OptReplaceUniqueIDs,
61 OptVersion,
62 OptDiag,
63 OptSTD,
64 OptDMT,
65 OptVIC,
66 OptHDMIVIC,
67 OptCVT,
68 OptGTF,
69 OptOVT,
70 OptListEstTimings,
71 OptListDMTs,
72 OptListVICs,
73 OptListHDMIVICs,
74 OptListRIDTimings,
75 OptListRIDs,
76 OptLast = 256
79 static char options[OptLast];
81 #ifndef __EMSCRIPTEN__
82 static struct option long_options[] = {
83 { "help", no_argument, 0, OptHelp },
84 { "output-format", required_argument, 0, OptOutputFormat },
85 { "native-resolution", no_argument, 0, OptNativeResolution },
86 { "preferred-timings", no_argument, 0, OptPreferredTimings },
87 { "physical-address", no_argument, 0, OptPhysicalAddress },
88 { "skip-hex-dump", no_argument, 0, OptSkipHexDump },
89 { "only-hex-dump", no_argument, 0, OptOnlyHexDump },
90 { "skip-sha", no_argument, 0, OptSkipSHA },
91 { "hide-serial-numbers", no_argument, 0, OptHideSerialNumbers },
92 { "replace-unique-ids", no_argument, 0, OptReplaceUniqueIDs },
93 { "version", no_argument, 0, OptVersion },
94 { "check-inline", no_argument, 0, OptCheckInline },
95 { "check", no_argument, 0, OptCheck },
96 { "short-timings", no_argument, 0, OptShortTimings },
97 { "long-timings", no_argument, 0, OptLongTimings },
98 { "ntsc", no_argument, 0, OptNTSC },
99 { "xmodeline", no_argument, 0, OptXModeLineTimings },
100 { "fbmode", no_argument, 0, OptFBModeTimings },
101 { "v4l2-timings", no_argument, 0, OptV4L2Timings },
102 { "diagonal", required_argument, 0, OptDiag },
103 { "std", required_argument, 0, OptSTD },
104 { "dmt", required_argument, 0, OptDMT },
105 { "vic", required_argument, 0, OptVIC },
106 { "hdmi-vic", required_argument, 0, OptHDMIVIC },
107 { "cvt", required_argument, 0, OptCVT },
108 { "gtf", required_argument, 0, OptGTF },
109 { "ovt", required_argument, 0, OptOVT },
110 { "list-established-timings", no_argument, 0, OptListEstTimings },
111 { "list-dmts", no_argument, 0, OptListDMTs },
112 { "list-vics", no_argument, 0, OptListVICs },
113 { "list-hdmi-vics", no_argument, 0, OptListHDMIVICs },
114 { "list-rid-timings", required_argument, 0, OptListRIDTimings },
115 { "list-rids", no_argument, 0, OptListRIDs },
116 { 0, 0, 0, 0 }
119 static void usage(void)
121 printf("Usage: edid-decode <options> [in [out]]\n"
122 " [in] EDID file to parse. Read from standard input if none given\n"
123 " or if the input filename is '-'.\n"
124 " [out] Output the read EDID to this file. Write to standard output\n"
125 " if the output filename is '-'.\n"
126 "\nOptions:\n"
127 " -o, --output-format <fmt>\n"
128 " If [out] is specified, then write the EDID in this format.\n"
129 " <fmt> is one of:\n"
130 " hex: hex numbers in ascii text (default for stdout)\n"
131 " raw: binary data (default unless writing to stdout)\n"
132 " carray: c-program struct\n"
133 " xml: XML data\n"
134 " -c, --check Check if the EDID conforms to the standards, failures and\n"
135 " warnings are reported at the end.\n"
136 " -C, --check-inline Check if the EDID conforms to the standards, failures and\n"
137 " warnings are reported inline.\n"
138 " -n, --native-resolution Report the native resolution.\n"
139 " -p, --preferred-timings Report the preferred timings.\n"
140 " -P, --physical-address Only report the CEC physical address.\n"
141 " -S, --short-timings Report all video timings in a short format.\n"
142 " -L, --long-timings Report all video timings in a long format.\n"
143 " -N, --ntsc Report the video timings suitable for NTSC-based video.\n"
144 " -X, --xmodeline Report all long video timings in Xorg.conf format.\n"
145 " -F, --fbmode Report all long video timings in fb.modes format.\n"
146 " -V, --v4l2-timings Report all long video timings in v4l2-dv-timings.h format.\n"
147 " -s, --skip-hex-dump Skip the initial hex dump of the EDID.\n"
148 " -H, --only-hex-dump Only output the hex dump of the EDID.\n"
149 " --skip-sha Skip the SHA report.\n"
150 " --hide-serial-numbers Hide serial numbers with '...'.\n"
151 " --replace-unique-ids Replace unique IDs (serial numbers, dates, Container IDs) with fixed values.\n"
152 " --version Show the edid-decode version (SHA).\n"
153 " --diagonal <inches> Set the display's diagonal in inches.\n"
154 " --std <byte1>,<byte2> Show the standard timing represented by these two bytes.\n"
155 " --dmt <dmt> Show the timings for the DMT with the given DMT ID.\n"
156 " --vic <vic> Show the timings for this VIC.\n"
157 " --hdmi-vic <hdmivic> Show the timings for this HDMI VIC.\n"
158 " --cvt w=<width>,h=<height>,fps=<fps>[,rb=<rb>][,interlaced][,overscan][,alt][,hblank=<hblank>][,vblank=<vblank>][,early-vsync]\n"
159 " Calculate the CVT timings for the given format.\n"
160 " <fps> is frames per second for progressive timings,\n"
161 " or fields per second for interlaced timings.\n"
162 " <rb> can be 0 (no reduced blanking, default), or\n"
163 " 1-3 for the reduced blanking version.\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 'alt' is given and <rb>=2, then report the timings\n"
167 " optimized for video: 1000 / 1001 * <fps>.\n"
168 " If 'alt' is given and <rb>=3, then the horizontal blanking\n"
169 " is 160 instead of 80 pixels.\n"
170 " If 'hblank' is given and <rb>=3, then the horizontal blanking\n"
171 " is <hblank> pixels (range of 80-200), overriding 'alt'.\n"
172 " If 'vblank' is given and <rb>=3, then the vertical blanking\n"
173 " time is <vblank> microseconds (range of 460-705 or 300-440).\n"
174 " If 'early-vsync' is given and <rb=3>, then select early vsync.\n"
175 " --gtf w=<width>,h=<height>[,fps=<fps>][,horfreq=<horfreq>][,pixclk=<pixclk>][,interlaced]\n"
176 " [,overscan][,secondary][,C=<c>][,M=<m>][,K=<k>][,J=<j>]\n"
177 " Calculate the GTF timings for the given format.\n"
178 " <fps> is frames per second for progressive timings,\n"
179 " or fields per second for interlaced timings.\n"
180 " <horfreq> is the horizontal frequency in kHz.\n"
181 " <pixclk> is the pixel clock frequency in MHz.\n"
182 " Only one of fps, horfreq or pixclk must be given.\n"
183 " If 'interlaced' is given, then this is an interlaced format.\n"
184 " If 'overscan' is given, then this is an overscanned format.\n"
185 " If 'secondary' is given, then the secondary GTF is used for\n"
186 " reduced blanking, where <c>, <m>, <k> and <j> are parameters\n"
187 " for the secondary curve.\n"
188 " --ovt (rid=<rid>|w=<width>,h=<height>),fps=<fps>\n"
189 " Calculate the OVT timings for the given format.\n"
190 " Either specify a RID or explicitly specify width and height.\n"
191 " --list-established-timings List all known Established Timings.\n"
192 " --list-dmts List all known DMTs.\n"
193 " --list-vics List all known VICs.\n"
194 " --list-hdmi-vics List all known HDMI VICs.\n"
195 " --list-rids List all known RIDs.\n"
196 " --list-rid-timings <rid> List all timings for RID <rid> or all known RIDs if <rid> is 0.\n"
197 " -h, --help Display this help message.\n");
199 #endif
201 static std::string s_msgs[EDID_MAX_BLOCKS + 1][2];
203 void msg(bool is_warn, const char *fmt, ...)
205 char buf[1024] = "";
206 va_list ap;
208 va_start(ap, fmt);
209 vsprintf(buf, fmt, ap);
210 va_end(ap);
212 if (is_warn)
213 state.warnings++;
214 else
215 state.failures++;
216 if (state.data_block.empty())
217 s_msgs[state.block_nr][is_warn] += std::string(" ") + buf;
218 else
219 s_msgs[state.block_nr][is_warn] += " " + state.data_block + ": " + buf;
221 if (options[OptCheckInline])
222 printf("%s: %s", is_warn ? "WARN" : "FAIL", buf);
225 static void show_msgs(bool is_warn)
227 printf("\n%s:\n\n", is_warn ? "Warnings" : "Failures");
228 for (unsigned i = 0; i < state.num_blocks; i++) {
229 if (s_msgs[i][is_warn].empty())
230 continue;
231 printf("Block %u, %s:\n%s",
232 i, block_name(edid[i * EDID_PAGE_SIZE]).c_str(),
233 s_msgs[i][is_warn].c_str());
235 if (s_msgs[EDID_MAX_BLOCKS][is_warn].empty())
236 return;
237 printf("EDID:\n%s",
238 s_msgs[EDID_MAX_BLOCKS][is_warn].c_str());
242 void replace_checksum(unsigned char *x, size_t len)
244 unsigned char sum = 0;
245 unsigned i;
247 for (i = 0; i < len - 1; i++)
248 sum += x[i];
249 x[len - 1] = -sum & 0xff;
252 void do_checksum(const char *prefix, const unsigned char *x, size_t len, size_t checksum_pos,
253 unsigned unused_bytes)
255 unsigned char check = x[checksum_pos];
256 unsigned char sum = 0;
257 unsigned i;
259 for (i = 0; i < len; i++) {
260 if (i != checksum_pos)
261 sum += x[i];
264 printf("%sChecksum: 0x%02hhx", prefix, check);
265 if ((unsigned char)(check + sum) != 0) {
266 printf(" (should be 0x%02x)", -sum & 0xff);
267 fail("Invalid checksum 0x%02x (should be 0x%02x).\n",
268 check, -sum & 0xff);
270 if (unused_bytes)
271 printf(" Unused space in Extension Block: %u byte%s",
272 unused_bytes, unused_bytes > 1 ? "s" : "");
273 printf("\n");
276 unsigned gcd(unsigned a, unsigned b)
278 while (b) {
279 unsigned t = b;
281 b = a % b;
282 a = t;
284 return a;
287 void calc_ratio(struct timings *t)
289 unsigned d = gcd(t->hact, t->vact);
291 if (d == 0) {
292 t->hratio = t->vratio = 0;
293 return;
295 t->hratio = t->hact / d;
296 t->vratio = t->vact / d;
298 if (t->hratio == 8 && t->vratio == 5) {
299 t->hratio = 16;
300 t->vratio = 10;
304 std::string edid_state::dtd_type(unsigned cnt)
306 unsigned len = std::to_string(cta.preparsed_total_dtds).length();
307 char buf[16];
308 sprintf(buf, "DTD %*u", len, cnt);
309 return buf;
312 bool match_timings(const timings &t1, const timings &t2)
314 if (t1.hact != t2.hact ||
315 t1.vact != t2.vact ||
316 t1.rb != t2.rb ||
317 t1.interlaced != t2.interlaced ||
318 t1.hfp != t2.hfp ||
319 t1.hbp != t2.hbp ||
320 t1.hsync != t2.hsync ||
321 t1.pos_pol_hsync != t2.pos_pol_hsync ||
322 t1.hratio != t2.hratio ||
323 t1.vfp != t2.vfp ||
324 t1.vbp != t2.vbp ||
325 t1.vsync != t2.vsync ||
326 t1.pos_pol_vsync != t2.pos_pol_vsync ||
327 t1.vratio != t2.vratio ||
328 t1.pixclk_khz != t2.pixclk_khz)
329 return false;
330 return true;
333 static void or_str(std::string &s, const std::string &flag, unsigned &num_flags)
335 if (!num_flags)
336 s = flag;
337 else if (num_flags % 2 == 0)
338 s = s + " | \\\n\t\t" + flag;
339 else
340 s = s + " | " + flag;
341 num_flags++;
345 * Return true if the timings are a close, but not identical,
346 * match. The only differences allowed are polarities and
347 * porches and syncs, provided the total blanking remains the
348 * same.
350 bool timings_close_match(const timings &t1, const timings &t2)
352 // We don't want to deal with borders, you're on your own
353 // if you are using those.
354 if (t1.hborder || t1.vborder ||
355 t2.hborder || t2.vborder)
356 return false;
357 if (t1.hact != t2.hact || t1.vact != t2.vact ||
358 t1.interlaced != t2.interlaced ||
359 t1.pixclk_khz != t2.pixclk_khz ||
360 t1.hfp + t1.hsync + t1.hbp != t2.hfp + t2.hsync + t2.hbp ||
361 t1.vfp + t1.vsync + t1.vbp != t2.vfp + t2.vsync + t2.vbp)
362 return false;
363 if (t1.hfp == t2.hfp &&
364 t1.hsync == t2.hsync &&
365 t1.hbp == t2.hbp &&
366 t1.pos_pol_hsync == t2.pos_pol_hsync &&
367 t1.vfp == t2.vfp &&
368 t1.vsync == t2.vsync &&
369 t1.vbp == t2.vbp &&
370 t1.pos_pol_vsync == t2.pos_pol_vsync)
371 return false;
372 return true;
375 static void print_modeline(unsigned indent, const struct timings *t, double refresh)
377 unsigned offset = (!t->even_vtotal && t->interlaced) ? 1 : 0;
378 unsigned hfp = t->hborder + t->hfp;
379 unsigned hbp = t->hborder + t->hbp;
380 unsigned vfp = t->vborder + t->vfp;
381 unsigned vbp = t->vborder + t->vbp;
383 printf("%*sModeline \"%ux%u_%.2f%s\" %.3f %u %u %u %u %u %u %u %u %cHSync",
384 indent, "",
385 t->hact, t->vact, refresh,
386 t->interlaced ? "i" : "", t->pixclk_khz / 1000.0,
387 t->hact, t->hact + hfp, t->hact + hfp + t->hsync,
388 t->hact + hfp + t->hsync + hbp,
389 t->vact, t->vact + vfp, t->vact + vfp + t->vsync,
390 t->vact + vfp + t->vsync + vbp + offset,
391 t->pos_pol_hsync ? '+' : '-');
392 if (!t->no_pol_vsync)
393 printf(" %cVSync", t->pos_pol_vsync ? '+' : '-');
394 if (t->interlaced)
395 printf(" Interlace");
396 printf("\n");
399 static void print_fbmode(unsigned indent, const struct timings *t,
400 double refresh, double hor_freq_khz)
402 printf("%*smode \"%ux%u-%u%s\"\n",
403 indent, "",
404 t->hact, t->vact,
405 (unsigned)(0.5 + (t->interlaced ? refresh / 2.0 : refresh)),
406 t->interlaced ? "-lace" : "");
407 printf("%*s# D: %.2f MHz, H: %.3f kHz, V: %.2f Hz\n",
408 indent + 8, "",
409 t->pixclk_khz / 1000.0, hor_freq_khz, refresh);
410 printf("%*sgeometry %u %u %u %u 32\n",
411 indent + 8, "",
412 t->hact, t->vact, t->hact, t->vact);
413 unsigned mult = t->interlaced ? 2 : 1;
414 unsigned offset = !t->even_vtotal && t->interlaced;
415 unsigned hfp = t->hborder + t->hfp;
416 unsigned hbp = t->hborder + t->hbp;
417 unsigned vfp = t->vborder + t->vfp;
418 unsigned vbp = t->vborder + t->vbp;
419 printf("%*stimings %llu %d %d %d %u %u %u\n",
420 indent + 8, "",
421 (unsigned long long)(1000000000.0 / (double)(t->pixclk_khz) + 0.5),
422 hbp, hfp, mult * vbp, mult * vfp + offset, t->hsync, mult * t->vsync);
423 if (t->interlaced)
424 printf("%*slaced true\n", indent + 8, "");
425 if (t->pos_pol_hsync)
426 printf("%*shsync high\n", indent + 8, "");
427 if (t->pos_pol_vsync)
428 printf("%*svsync high\n", indent + 8, "");
429 printf("%*sendmode\n", indent, "");
432 static void print_v4l2_timing(const struct timings *t,
433 double refresh, const char *type)
435 printf("\t#define V4L2_DV_BT_%uX%u%c%u_%02u { \\\n",
436 t->hact, t->vact, t->interlaced ? 'I' : 'P',
437 (unsigned)refresh, (unsigned)(0.5 + 100.0 * (refresh - (unsigned)refresh)));
438 printf("\t\t.type = V4L2_DV_BT_656_1120, \\\n");
439 printf("\t\tV4L2_INIT_BT_TIMINGS(%u, %u, %u, ",
440 t->hact, t->vact, t->interlaced);
441 if (!t->pos_pol_hsync && !t->pos_pol_vsync)
442 printf("0, \\\n");
443 else if (t->pos_pol_hsync && t->pos_pol_vsync)
444 printf("\\\n\t\t\tV4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \\\n");
445 else if (t->pos_pol_hsync)
446 printf("V4L2_DV_HSYNC_POS_POL, \\\n");
447 else
448 printf("V4L2_DV_VSYNC_POS_POL, \\\n");
449 unsigned hfp = t->hborder + t->hfp;
450 unsigned hbp = t->hborder + t->hbp;
451 unsigned vfp = t->vborder + t->vfp;
452 unsigned vbp = t->vborder + t->vbp;
453 printf("\t\t\t%lluULL, %d, %u, %d, %u, %u, %d, %u, %u, %d, \\\n",
454 t->pixclk_khz * 1000ULL, hfp, t->hsync, hbp,
455 vfp, t->vsync, vbp,
456 t->interlaced ? vfp : 0,
457 t->interlaced ? t->vsync : 0,
458 t->interlaced ? vbp + !t->even_vtotal : 0);
460 std::string flags;
461 unsigned num_flags = 0;
462 unsigned vic = 0;
463 unsigned hdmi_vic = 0;
464 const char *std = "0";
466 if (t->interlaced && !t->even_vtotal)
467 or_str(flags, "V4L2_DV_FL_HALF_LINE", num_flags);
468 if (!memcmp(type, "VIC", 3)) {
469 or_str(flags, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags);
470 or_str(flags, "V4L2_DV_FL_IS_CE_VIDEO", num_flags);
471 vic = strtoul(type + 4, 0, 0);
473 if (!memcmp(type, "HDMI VIC", 8)) {
474 or_str(flags, "V4L2_DV_FL_HAS_HDMI_VIC", num_flags);
475 or_str(flags, "V4L2_DV_FL_IS_CE_VIDEO", num_flags);
476 hdmi_vic = strtoul(type + 9, 0, 0);
477 vic = hdmi_vic_to_vic(hdmi_vic);
478 if (vic)
479 or_str(flags, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags);
481 if (vic && (fmod(refresh, 6)) == 0.0)
482 or_str(flags, "V4L2_DV_FL_CAN_REDUCE_FPS", num_flags);
483 if (t->rb)
484 or_str(flags, "V4L2_DV_FL_REDUCED_BLANKING", num_flags);
485 if (t->hratio && t->vratio)
486 or_str(flags, "V4L2_DV_FL_HAS_PICTURE_ASPECT", num_flags);
488 if (!memcmp(type, "VIC", 3) || !memcmp(type, "HDMI VIC", 8))
489 std = "V4L2_DV_BT_STD_CEA861";
490 else if (!memcmp(type, "DMT", 3))
491 std = "V4L2_DV_BT_STD_DMT";
492 else if (!memcmp(type, "CVT", 3))
493 std = "V4L2_DV_BT_STD_CVT";
494 else if (!memcmp(type, "GTF", 3))
495 std = "V4L2_DV_BT_STD_GTF";
496 printf("\t\t\t%s, \\\n", std);
497 printf("\t\t\t%s, \\\n", flags.empty() ? "0" : flags.c_str());
498 printf("\t\t\t{ %u, %u }, %u, %u) \\\n",
499 t->hratio, t->vratio, vic, hdmi_vic);
500 printf("\t}\n");
503 static void print_detailed_timing(unsigned indent, const struct timings *t)
505 printf("%*sHfront %4d Hsync %3u Hback %4d Hpol %s",
506 indent, "",
507 t->hfp, t->hsync, t->hbp, t->pos_pol_hsync ? "P" : "N");
508 if (t->hborder)
509 printf(" Hborder %u", t->hborder);
510 printf("\n");
512 printf("%*sVfront %4u Vsync %3u Vback %4d",
513 indent, "", t->vfp, t->vsync, t->vbp);
514 if (!t->no_pol_vsync)
515 printf(" Vpol %s", t->pos_pol_vsync ? "P" : "N");
516 if (t->vborder)
517 printf(" Vborder %u", t->vborder);
518 if (t->even_vtotal) {
519 printf(" Both Fields");
520 } else if (t->interlaced) {
521 printf(" Vfront +0.5 Odd Field\n");
522 printf("%*sVfront %4d Vsync %3u Vback %4d",
523 indent, "", t->vfp, t->vsync, t->vbp);
524 if (!t->no_pol_vsync)
525 printf(" Vpol %s", t->pos_pol_vsync ? "P" : "N");
526 if (t->vborder)
527 printf(" Vborder %u", t->vborder);
528 printf(" Vback +0.5 Even Field");
530 printf("\n");
533 bool edid_state::print_timings(const char *prefix, const struct timings *t,
534 const char *type, const char *flags,
535 bool detailed, bool do_checks, unsigned ntsc)
537 if (!t) {
538 // Should not happen
539 if (do_checks)
540 fail("Unknown video timings.\n");
541 return false;
544 if (detailed && options[OptShortTimings])
545 detailed = false;
546 if (options[OptLongTimings])
547 detailed = true;
549 unsigned vact = t->vact;
550 unsigned hbl = t->hfp + t->hsync + t->hbp + 2 * t->hborder;
551 unsigned vbl = t->vfp + t->vsync + t->vbp + 2 * t->vborder;
552 unsigned htotal = t->hact + hbl;
553 double hor_freq_khz = htotal ? (double)t->pixclk_khz / htotal : 0;
555 if (t->interlaced)
556 vact /= 2;
558 double out_hor_freq_khz = hor_freq_khz;
559 if (t->ycbcr420)
560 hor_freq_khz /= 2;
562 double vtotal = vact + vbl;
564 bool ok = true;
566 if (!t->hact || !hbl || !t->hfp || !t->hsync ||
567 !vact || !vbl || (!t->vfp && !t->interlaced && !t->even_vtotal) || !t->vsync) {
568 if (do_checks)
569 fail("0 values in the video timing:\n"
570 " Horizontal Active/Blanking %u/%u\n"
571 " Horizontal Frontporch/Sync Width %u/%u\n"
572 " Vertical Active/Blanking %u/%u\n"
573 " Vertical Frontporch/Sync Width %u/%u\n",
574 t->hact, hbl, t->hfp, t->hsync, vact, vbl, t->vfp, t->vsync);
575 ok = false;
578 if (t->even_vtotal)
579 vtotal = vact + t->vfp + t->vsync + t->vbp;
580 else if (t->interlaced)
581 vtotal = vact + t->vfp + t->vsync + t->vbp + 0.5;
583 double refresh = t->pixclk_khz * 1000.0 / (htotal * vtotal);
584 double pixclk = t->pixclk_khz * 1000.0;
585 if (((ntsc > 1 && options[OptNTSC]) || ntsc == 1) && fmod(refresh, 6.0) == 0) {
586 const double ntsc_fact = 1000.0 / 1001.0;
587 pixclk *= ntsc_fact;
588 refresh *= ntsc_fact;
589 out_hor_freq_khz *= ntsc_fact;
592 std::string s;
593 unsigned rb = t->rb & ~RB_ALT;
594 if (rb) {
595 bool alt = t->rb & RB_ALT;
596 s = "RB";
597 if (rb == RB_CVT_V2)
598 s += std::string("v2") + (alt ? ",video-optimized" : "");
599 else if (rb == RB_CVT_V3)
600 s += std::string("v3") + (alt ? ",h-blank-160" : "");
602 add_str(s, flags);
603 if (t->hsize_mm || t->vsize_mm)
604 add_str(s, std::to_string(t->hsize_mm) + " mm x " + std::to_string(t->vsize_mm) + " mm");
605 if (t->hsize_mm > dtd_max_hsize_mm)
606 dtd_max_hsize_mm = t->hsize_mm;
607 if (t->vsize_mm > dtd_max_vsize_mm)
608 dtd_max_vsize_mm = t->vsize_mm;
609 if (!s.empty())
610 s = " (" + s + ")";
611 unsigned pixclk_khz = t->pixclk_khz / (t->ycbcr420 ? 2 : 1);
613 char buf[10];
615 sprintf(buf, "%u%s", t->vact, t->interlaced ? "i" : "");
616 printf("%s%s: %5ux%-5s %10.6f Hz %3u:%-3u %8.3f kHz %13.6f MHz%s\n",
617 prefix, type,
618 t->hact, buf,
619 refresh,
620 t->hratio, t->vratio,
621 out_hor_freq_khz,
622 pixclk / 1000000.0,
623 s.c_str());
625 unsigned len = strlen(prefix) + 2;
627 if (!t->ycbcr420 && detailed && options[OptXModeLineTimings])
628 print_modeline(len, t, refresh);
629 else if (!t->ycbcr420 && detailed && options[OptFBModeTimings])
630 print_fbmode(len, t, refresh, hor_freq_khz);
631 else if (!t->ycbcr420 && detailed && options[OptV4L2Timings])
632 print_v4l2_timing(t, refresh, type);
633 else if (detailed)
634 print_detailed_timing(len + strlen(type) + 6, t);
636 if (!do_checks)
637 return ok;
639 if (!memcmp(type, "DTD", 3)) {
640 unsigned vic, dmt;
641 const timings *vic_t = cta_close_match_to_vic(*t, vic);
643 // We report this even if there is no CTA block since it
644 // is still likely that the actual VIC timings were intended.
645 if (vic_t)
646 warn("DTD is similar but not identical to VIC %u.\n", vic);
648 if (cta_matches_vic(*t, vic) && has_cta &&
649 !cta.preparsed_has_vic[0][vic]) {
650 warn("DTD is identical to VIC %u, which is not present in the CTA Ext Block.\n", vic);
652 if (cta.preparsed_max_vic_pixclk_khz && t->pixclk_khz > 340000 &&
653 t->pixclk_khz > cta.preparsed_max_vic_pixclk_khz)
654 cta.warn_about_hdmi_2x_dtd = true;
657 const timings *dmt_t = close_match_to_dmt(*t, dmt);
658 if (!vic_t && dmt_t)
659 warn("DTD is similar but not identical to DMT 0x%02x.\n", dmt);
662 if (refresh) {
663 min_vert_freq_hz = min(min_vert_freq_hz, refresh);
664 max_vert_freq_hz = max(max_vert_freq_hz, refresh);
666 if (hor_freq_khz) {
667 min_hor_freq_hz = min(min_hor_freq_hz, hor_freq_khz * 1000.0);
668 max_hor_freq_hz = max(max_hor_freq_hz, hor_freq_khz * 1000.0);
669 max_pixclk_khz = max(max_pixclk_khz, pixclk_khz);
670 if (t->pos_pol_hsync && !t->pos_pol_vsync && t->vsync == 3)
671 base.max_pos_neg_hor_freq_khz = hor_freq_khz;
674 if (t->ycbcr420 && t->pixclk_khz < 590000)
675 warn_once("Some YCbCr 4:2:0 timings are invalid for HDMI 2.1 (which requires an RGB timings pixel rate >= 590 MHz).\n");
676 if (t->hfp <= 0)
677 fail("0 or negative horizontal front porch.\n");
678 if (t->hbp <= 0)
679 fail("0 or negative horizontal back porch.\n");
680 if (t->vbp <= 0)
681 fail("0 or negative vertical back porch.\n");
682 if (!base.max_display_width_mm && !base.max_display_height_mm) {
683 /* this is valid */
684 } else if (!t->hsize_mm && !t->vsize_mm) {
685 /* this is valid */
686 } else if (t->hsize_mm > base.max_display_width_mm + 9 ||
687 t->vsize_mm > base.max_display_height_mm + 9) {
688 fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
689 t->hsize_mm, t->vsize_mm, base.max_display_width_mm, base.max_display_height_mm);
690 } else if (t->hsize_mm < base.max_display_width_mm - 9 &&
691 t->vsize_mm < base.max_display_height_mm - 9) {
692 fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
693 t->hsize_mm, t->vsize_mm, base.max_display_width_mm, base.max_display_height_mm);
695 return ok;
698 std::string containerid2s(const unsigned char *x)
700 char buf[40];
702 sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
703 x[0], x[1], x[2], x[3],
704 x[4], x[5],
705 x[6], x[7],
706 x[8], x[9],
707 x[10], x[11], x[12], x[13], x[14], x[15]);
708 return buf;
711 std::string utohex(unsigned char x)
713 char buf[10];
715 sprintf(buf, "0x%02hhx", x);
716 return buf;
719 const char *oui_name(unsigned oui, unsigned *ouinum)
721 unsigned ouinumscratch;
722 if (!ouinum) ouinum = &ouinumscratch;
723 const char *name;
724 switch (oui) {
725 #define oneoui(c,k,n) case c: *ouinum = kOUI_##k; name = n; break;
726 #include "oui.h"
727 default: *ouinum = 0; name = NULL; break;
729 return name;
732 void edid_state::data_block_oui(std::string block_name, const unsigned char *x,
733 unsigned length, unsigned *ouinum, bool ignorezeros, bool do_ascii, bool big_endian)
735 std::string buf;
736 char ascii[4];
737 unsigned oui;
738 const char *ouiname = NULL;
739 bool matched_reverse = false;
740 bool matched_ascii = false;
741 bool valid_ascii = false;
743 if (big_endian)
744 oui = ((length > 0 ? x[0] : 0) << 16) + ((length > 1 ? x[1] : 0) << 8) + (length > 2 ? x[2] : 0);
745 else
746 oui = ((length > 2 ? x[2] : 0) << 16) + ((length > 1 ? x[1] : 0) << 8) + (length > 0 ? x[0] : 0);
748 buf = ouitohex(oui);
749 if (length < 3) {
750 sprintf(ascii, "?"); // some characters are null
751 if (ouinum) *ouinum = 0; // doesn't match a known OUI
752 } else {
753 valid_ascii = (x[0] >= 'A' && x[1] >= 'A' && x[2] >= 'A' && x[0] <= 'Z' && x[1] <= 'Z' && x[2] <= 'Z');
754 sprintf(ascii, "%c%c%c", x[0], x[1], x[2]);
756 ouiname = oui_name(oui, ouinum);
757 if (!ouiname) {
758 big_endian = !big_endian;
759 unsigned reversedoui = ((oui & 0xff) << 16) + (oui & 0x00ff00) + (oui >> 16);
760 ouiname = oui_name(reversedoui, ouinum);
761 if (ouiname) {
762 oui = reversedoui;
763 buf = ouitohex(oui);
764 matched_reverse = true;
765 } else if (do_ascii && valid_ascii) {
766 unsigned asciioui = (x[0] << 24) + (x[1] << 16) + (x[2] << 8);
767 ouiname = oui_name(asciioui, ouinum);
768 if (ouiname) {
769 matched_ascii = true;
775 std::string name;
776 if (ouiname) {
777 if (matched_ascii)
778 name = block_name + " (" + ouiname + ")" + ", PNP ID '" + ascii + "'";
779 else
780 name = block_name + " (" + ouiname + ")" + ", OUI " + buf;
781 } else if (do_ascii && valid_ascii) {
782 name = block_name + ", PNP ID '" + ascii + "'";
783 } else {
784 name = block_name + ", OUI " + buf;
786 // assign string to data_block before outputting errors
787 data_block = name;
789 if (oui || !ignorezeros) {
790 printf(" %s:\n", data_block.c_str());
791 if (length < 3)
792 fail("Data block length (%d) is not enough to contain an OUI.\n", length);
793 else if (ouiname) {
794 if (do_ascii && !valid_ascii)
795 warn("Expected PNP ID but found OUI.\n");
796 if (matched_reverse)
797 fail("Endian-ness (%s) of OUI is different than expected (%s).\n", big_endian ? "be" : "le", big_endian ? "le" : "be");
799 else {
800 if (valid_ascii)
801 warn("Unknown OUI %s (possible PNP %s).\n", buf.c_str(), ascii);
802 else
803 warn("Unknown OUI %s.\n", buf.c_str());
808 std::string ouitohex(unsigned oui)
810 char buf[32];
812 sprintf(buf, "%02X-%02X-%02X", (oui >> 16) & 0xff, (oui >> 8) & 0xff, oui & 0xff);
813 return buf;
816 bool memchk(const unsigned char *x, unsigned len, unsigned char v)
818 for (unsigned i = 0; i < len; i++)
819 if (x[i] != v)
820 return false;
821 return true;
824 void hex_block(const char *prefix, const unsigned char *x,
825 unsigned length, bool show_ascii, unsigned step)
827 unsigned i, j;
829 for (i = 0; i < length; i += step) {
830 unsigned len = min(step, length - i);
832 printf("%s", prefix);
833 for (j = 0; j < len; j++)
834 printf("%s%02x", j ? " " : "", x[i + j]);
836 if (show_ascii) {
837 for (j = len; j < step; j++)
838 printf(" ");
839 printf(" '");
840 for (j = 0; j < len; j++)
841 printf("%c", x[i + j] >= ' ' && x[i + j] <= '~' ? x[i + j] : '.');
842 printf("'");
844 printf("\n");
848 static bool edid_add_byte(const char *s, bool two_digits = true)
850 char buf[3];
852 if (state.edid_size == sizeof(edid))
853 return false;
854 buf[0] = s[0];
855 buf[1] = two_digits ? s[1] : 0;
856 buf[2] = 0;
857 edid[state.edid_size++] = strtoul(buf, NULL, 16);
858 return true;
861 static bool extract_edid_quantumdata(const char *start)
863 /* Parse QuantumData 980 EDID files */
864 do {
865 start = strstr(start, ">");
866 if (!start)
867 return false;
868 start++;
869 for (unsigned i = 0; start[i] && start[i + 1] && i < 256; i += 2)
870 if (!edid_add_byte(start + i))
871 return false;
872 start = strstr(start, "<BLOCK");
873 } while (start);
874 return state.edid_size;
877 static const char *ignore_chars = ",:;";
879 static bool extract_edid_hex(const char *s, bool require_two_digits = true)
881 for (; *s; s++) {
882 if (isspace(*s) || strchr(ignore_chars, *s))
883 continue;
885 if (*s == '0' && tolower(s[1]) == 'x') {
886 s++;
887 continue;
890 /* Read one or two hex digits from the log */
891 if (!isxdigit(s[0])) {
892 if (state.edid_size && state.edid_size % 128 == 0)
893 break;
894 return false;
896 if (require_two_digits && !isxdigit(s[1])) {
897 odd_hex_digits = true;
898 return false;
900 if (!edid_add_byte(s, isxdigit(s[1])))
901 return false;
902 if (isxdigit(s[1]))
903 s++;
905 return state.edid_size;
908 static bool extract_edid_xrandr(const char *start)
910 static const char indentation1[] = " ";
911 static const char indentation2[] = "\t\t";
912 /* Used to detect that we've gone past the EDID property */
913 static const char half_indentation1[] = " ";
914 static const char half_indentation2[] = "\t";
915 const char *indentation;
916 const char *s;
918 for (;;) {
919 unsigned j;
921 /* Get the next start of the line of EDID hex, assuming spaces for indentation */
922 s = strstr(start, indentation = indentation1);
923 /* Did we skip the start of another property? */
924 if (s && s > strstr(start, half_indentation1))
925 break;
927 /* If we failed, retry assuming tabs for indentation */
928 if (!s) {
929 s = strstr(start, indentation = indentation2);
930 /* Did we skip the start of another property? */
931 if (s && s > strstr(start, half_indentation2))
932 break;
935 if (!s)
936 break;
938 start = s + strlen(indentation);
940 for (j = 0; j < 16; j++, start += 2) {
941 /* Read a %02x from the log */
942 if (!isxdigit(start[0]) || !isxdigit(start[1])) {
943 if (j)
944 break;
945 return false;
947 if (!edid_add_byte(start))
948 return false;
951 return state.edid_size;
954 static bool extract_edid_xorg(const char *start)
956 bool find_first_num = true;
958 for (; *start; start++) {
959 if (find_first_num) {
960 const char *s;
962 /* skip ahead to the : */
963 s = strstr(start, ": \t");
964 if (!s)
965 s = strstr(start, ": ");
966 if (!s)
967 break;
968 start = s;
969 /* and find the first number */
970 while (!isxdigit(start[1]))
971 start++;
972 find_first_num = false;
973 continue;
974 } else {
975 /* Read a %02x from the log */
976 if (!isxdigit(*start)) {
977 find_first_num = true;
978 continue;
980 if (!edid_add_byte(start))
981 return false;
982 start++;
985 return state.edid_size;
988 static bool extract_edid(int fd, FILE *error)
990 std::vector<char> edid_data;
991 char buf[EDID_PAGE_SIZE];
993 for (;;) {
994 ssize_t i = read(fd, buf, sizeof(buf));
996 if (i < 0)
997 return false;
998 if (i == 0)
999 break;
1000 edid_data.insert(edid_data.end(), buf, buf + i);
1003 if (edid_data.empty()) {
1004 state.edid_size = 0;
1005 return false;
1007 // Ensure it is safely terminated by a 0 char
1008 edid_data.push_back('\0');
1010 const char *data = &edid_data[0];
1011 const char *start;
1013 /* Look for edid-decode output */
1014 start = strstr(data, "EDID (hex):");
1015 if (!start)
1016 start = strstr(data, "edid-decode (hex):");
1017 if (start)
1018 return extract_edid_hex(strchr(start, ':'));
1020 /* Look for C-array */
1021 start = strstr(data, "unsigned char edid[] = {");
1022 if (start)
1023 return extract_edid_hex(strchr(start, '{') + 1, false);
1025 /* Look for QuantumData EDID output */
1026 start = strstr(data, "<BLOCK");
1027 if (start)
1028 return extract_edid_quantumdata(start);
1030 /* Look for xrandr --verbose output (lines of 16 hex bytes) */
1031 start = strstr(data, "EDID_DATA:");
1032 if (!start)
1033 start = strstr(data, "EDID:");
1034 if (start)
1035 return extract_edid_xrandr(start);
1037 /* Look for an EDID in an Xorg.0.log file */
1038 start = strstr(data, "EDID (in hex):");
1039 if (start)
1040 start = strstr(start, "(II)");
1041 if (start)
1042 return extract_edid_xorg(start);
1044 unsigned i;
1046 /* Is the EDID provided in hex? */
1047 for (i = 0; i < 32 && (isspace(data[i]) || strchr(ignore_chars, data[i]) ||
1048 tolower(data[i]) == 'x' || isxdigit(data[i])); i++);
1050 if (i == 32)
1051 return extract_edid_hex(data);
1053 // Drop the extra '\0' byte since we now assume binary data
1054 edid_data.pop_back();
1056 /* Assume binary */
1057 if (edid_data.size() > sizeof(edid)) {
1058 fprintf(error, "Binary EDID length %zu is greater than %zu.\n",
1059 edid_data.size(), sizeof(edid));
1060 return false;
1062 memcpy(edid, data, edid_data.size());
1063 state.edid_size = edid_data.size();
1064 return true;
1067 static int edid_from_file(const char *from_file, FILE *error)
1069 #ifdef O_BINARY
1070 // Windows compatibility
1071 int flags = O_RDONLY | O_BINARY;
1072 #else
1073 int flags = O_RDONLY;
1074 #endif
1075 int fd;
1077 if (!strcmp(from_file, "-")) {
1078 from_file = "stdin";
1079 fd = 0;
1080 } else if ((fd = open(from_file, flags)) == -1) {
1081 perror(from_file);
1082 return -1;
1085 odd_hex_digits = false;
1086 if (!extract_edid(fd, error)) {
1087 if (!state.edid_size) {
1088 fprintf(error, "EDID of '%s' was empty.\n", from_file);
1089 return -1;
1091 fprintf(error, "EDID extract of '%s' failed: ", from_file);
1092 if (odd_hex_digits)
1093 fprintf(error, "odd number of hexadecimal digits.\n");
1094 else
1095 fprintf(error, "unknown format.\n");
1096 return -1;
1098 if (state.edid_size % EDID_PAGE_SIZE) {
1099 fprintf(error, "EDID length %u is not a multiple of %u.\n",
1100 state.edid_size, EDID_PAGE_SIZE);
1101 return -1;
1103 state.num_blocks = state.edid_size / EDID_PAGE_SIZE;
1104 if (fd != 0)
1105 close(fd);
1107 if (memcmp(edid, "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", 8)) {
1108 fprintf(error, "No EDID header found in '%s'.\n", from_file);
1109 return -1;
1111 return 0;
1114 /* generic extension code */
1116 std::string block_name(unsigned char block)
1118 char buf[10];
1120 switch (block) {
1121 case 0x00: return "Base EDID";
1122 case 0x02: return "CTA-861 Extension Block";
1123 case 0x10: return "Video Timing Extension Block";
1124 case 0x20: return "EDID 2.0 Extension Block";
1125 case 0x40: return "Display Information Extension Block";
1126 case 0x50: return "Localized String Extension Block";
1127 case 0x60: return "Microdisplay Interface Extension Block";
1128 case 0x70: return "DisplayID Extension Block";
1129 case 0xf0: return "Block Map Extension Block";
1130 case 0xff: return "Manufacturer-Specific Extension Block";
1131 default:
1132 sprintf(buf, " 0x%02x", block);
1133 return std::string("Unknown EDID Extension Block") + buf;
1137 void edid_state::parse_block_map(const unsigned char *x)
1139 unsigned last_valid_block_tag = 0;
1140 bool fail_once = false;
1141 unsigned offset = 1;
1142 unsigned i;
1144 if (block_nr == 1)
1145 block_map.saw_block_1 = true;
1146 else if (!block_map.saw_block_1)
1147 fail("No EDID Block Map Extension found in block 1.\n");
1148 else if (block_nr == 128)
1149 block_map.saw_block_128 = true;
1151 if (block_nr > 1)
1152 offset = 128;
1154 for (i = 1; i < 127; i++) {
1155 unsigned block = offset + i;
1157 if (x[i]) {
1158 last_valid_block_tag++;
1159 if (i != last_valid_block_tag && !fail_once) {
1160 fail("Valid block tags are not consecutive.\n");
1161 fail_once = true;
1163 printf(" Block %3u: %s\n", block, block_name(x[i]).c_str());
1164 if (block >= num_blocks) {
1165 if (!fail_once)
1166 fail("Invalid block number %u.\n", block);
1167 fail_once = true;
1168 } else if (x[i] != edid[block * EDID_PAGE_SIZE]) {
1169 fail("Block %u tag mismatch: expected 0x%02x, but got 0x%02x.\n",
1170 block, edid[block * EDID_PAGE_SIZE], x[i]);
1172 } else if (block < num_blocks) {
1173 fail("Block %u tag mismatch: expected 0x%02x, but got 0x00.\n",
1174 block, edid[block * EDID_PAGE_SIZE]);
1179 void edid_state::preparse_extension(unsigned char *x)
1181 switch (x[0]) {
1182 case 0x02:
1183 has_cta = true;
1184 preparse_cta_block(x);
1185 break;
1186 case 0x50:
1187 preparse_ls_ext_block(x);
1188 break;
1189 case 0x70:
1190 has_dispid = true;
1191 preparse_displayid_block(x);
1192 break;
1196 void edid_state::parse_extension(const unsigned char *x)
1198 block = block_name(x[0]);
1199 data_block.clear();
1200 unused_bytes = 0;
1202 printf("\n");
1203 if (block_nr && x[0] == 0)
1204 block = "Unknown EDID Extension Block 0x00";
1205 printf("Block %u, %s:\n", block_nr, block.c_str());
1207 switch (x[0]) {
1208 case 0x02:
1209 parse_cta_block(x);
1210 break;
1211 case 0x10:
1212 parse_vtb_ext_block(x);
1213 break;
1214 case 0x20:
1215 fail("Deprecated extension block for EDID 2.0, do not use.\n");
1216 break;
1217 case 0x40:
1218 parse_di_ext_block(x);
1219 break;
1220 case 0x50:
1221 parse_ls_ext_block(x);
1222 break;
1223 case 0x70:
1224 parse_displayid_block(x);
1225 break;
1226 case 0xf0:
1227 parse_block_map(x);
1228 if (block_nr != 1 && block_nr != 128)
1229 fail("Must be used in block 1 and 128.\n");
1230 break;
1231 default:
1232 hex_block(" ", x, EDID_PAGE_SIZE);
1233 fail("Unknown Extension Block.\n");
1234 break;
1237 data_block.clear();
1238 do_checksum("", x, EDID_PAGE_SIZE, EDID_PAGE_SIZE - 1, unused_bytes);
1241 void edid_state::print_preferred_timings()
1243 if (base.preferred_timing.is_valid()) {
1244 printf("\n----------------\n");
1245 printf("\nPreferred Video Timing if only Block 0 is parsed:\n");
1246 print_timings(" ", base.preferred_timing, true, false);
1249 if (!cta.preferred_timings.empty()) {
1250 printf("\n----------------\n");
1251 printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
1252 cta.preferred_timings.size() > 1 ? "s" : "");
1253 for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
1254 iter != cta.preferred_timings.end(); ++iter)
1255 print_timings(" ", *iter, true, false);
1258 if (!cta.preferred_timings_vfpdb.empty()) {
1259 printf("\n----------------\n");
1260 printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed with VFPDB support:\n",
1261 cta.preferred_timings_vfpdb.size() > 1 ? "s" : "");
1262 for (vec_timings_ext::iterator iter = cta.preferred_timings_vfpdb.begin();
1263 iter != cta.preferred_timings_vfpdb.end(); ++iter)
1264 print_timings(" ", *iter, true, false);
1267 if (!dispid.preferred_timings.empty()) {
1268 printf("\n----------------\n");
1269 printf("\nPreferred Video Timing%s if Block 0 and DisplayID Blocks are parsed:\n",
1270 dispid.preferred_timings.size() > 1 ? "s" : "");
1271 for (vec_timings_ext::iterator iter = dispid.preferred_timings.begin();
1272 iter != dispid.preferred_timings.end(); ++iter)
1273 print_timings(" ", *iter, true, false);
1277 void edid_state::print_native_res()
1279 typedef std::pair<unsigned, unsigned> resolution;
1280 typedef std::set<resolution> resolution_set;
1281 resolution_set native_prog, native_int, native_nvrdb;
1282 unsigned native_width = 0, native_height = 0;
1283 unsigned native_width_int = 0, native_height_int = 0;
1285 // Note: it is also a mismatch if Block 0 does not define a
1286 // native resolution, but other blocks do.
1287 bool native_mismatch = false;
1288 bool native_int_mismatch = false;
1290 if (base.preferred_timing.is_valid() && base.preferred_is_also_native) {
1291 if (base.preferred_timing.t.interlaced) {
1292 native_width_int = base.preferred_timing.t.hact;
1293 native_height_int = base.preferred_timing.t.vact;
1294 } else {
1295 native_width = base.preferred_timing.t.hact;
1296 native_height = base.preferred_timing.t.vact;
1300 if (!native_width && dispid.native_width) {
1301 native_width = dispid.native_width;
1302 native_height = dispid.native_height;
1303 native_mismatch = true;
1304 } else if (dispid.native_width && native_width &&
1305 (dispid.native_width != native_width ||
1306 dispid.native_height != native_height)) {
1307 native_mismatch = true;
1310 for (vec_timings_ext::iterator iter = cta.native_timings.begin();
1311 iter != cta.native_timings.end(); ++iter) {
1312 if (iter->t.interlaced) {
1313 native_int.insert(std::pair<unsigned, unsigned>(iter->t.hact, iter->t.vact));
1314 if (!native_width_int) {
1315 native_width_int = iter->t.hact;
1316 native_height_int = iter->t.vact;
1317 native_int_mismatch = true;
1318 } else if (native_width_int &&
1319 (iter->t.hact != native_width_int ||
1320 iter->t.vact != native_height_int)) {
1321 native_int_mismatch = true;
1323 } else {
1324 native_prog.insert(std::pair<unsigned, unsigned>(iter->t.hact, iter->t.vact));
1325 if (!native_width) {
1326 native_width = iter->t.hact;
1327 native_height = iter->t.vact;
1328 native_mismatch = true;
1329 } else if (native_width &&
1330 (iter->t.hact != native_width ||
1331 iter->t.vact != native_height)) {
1332 native_mismatch = true;
1337 for (vec_timings_ext::iterator iter = cta.native_timing_nvrdb.begin();
1338 iter != cta.native_timing_nvrdb.end(); ++iter) {
1339 if (iter->t.interlaced) {
1340 fail("Interlaced native timing in NVRDB.\n");
1341 } else {
1342 native_nvrdb.insert(std::pair<unsigned, unsigned>(iter->t.hact, iter->t.vact));
1343 if (!native_width) {
1344 native_width = iter->t.hact;
1345 native_height = iter->t.vact;
1346 native_mismatch = true;
1347 } else if (native_width &&
1348 (iter->t.hact != native_width ||
1349 iter->t.vact != native_height)) {
1350 native_mismatch = true;
1355 if (diagonal) {
1356 if (image_width) {
1357 double w = image_width;
1358 double h = image_height;
1359 double d = sqrt(w * w + h * h) / 254.0;
1361 if (fabs(diagonal - d) >= 0.1)
1362 warn("Specified diagonal is %.1f\", calculated diagonal is %.1f\".\n",
1363 diagonal, d);
1365 if (native_width) {
1366 double w = native_width;
1367 double h = native_height;
1368 double d = diagonal * 254.0;
1369 double c = sqrt((d * d) / (w * w + h * h));
1371 w *= c;
1372 h *= c;
1374 if (image_width) {
1375 printf("\n----------------\n");
1376 printf("\nCalculated image size for a diagonal of %.1f\" is %.1fx%.1fmm.\n",
1377 diagonal, w / 10.0, h / 10.0);
1379 if (fabs((double)image_width - w) >= 100.0 ||
1380 fabs((double)image_height - h) >= 100.0)
1381 warn("Calculated image size is %.1fx%.1fmm, EDID image size is %.1fx%.1fmm.\n",
1382 w / 10.0, h / 10.0,
1383 image_width / 10.0, image_height / 10.0);
1384 } else {
1385 warn("No image size was specified, but it is calculated as %.1fx%.1fmm.\n",
1386 w / 10.0, h / 10.0);
1391 if (!options[OptNativeResolution])
1392 return;
1394 if (native_width == 0 && native_width_int == 0) {
1395 printf("\n----------------\n");
1396 printf("\nNo Native Video Resolution was defined.\n");
1397 return;
1400 if ((native_width || native_width_int) &&
1401 !native_mismatch && !native_int_mismatch) {
1402 printf("\n----------------\n");
1403 printf("\nNative Video Resolution%s:\n",
1404 native_width && native_width_int ? "s" : "");
1405 if (native_width)
1406 printf(" %ux%u\n", native_width, native_height);
1407 if (native_width_int)
1408 printf(" %ux%ui\n", native_width_int, native_height_int);
1409 return;
1412 if (base.preferred_timing.is_valid() && base.preferred_is_also_native) {
1413 printf("\n----------------\n");
1414 printf("\nNative Video Resolution if only Block 0 is parsed:\n");
1415 printf(" %ux%u%s\n",
1416 base.preferred_timing.t.hact, base.preferred_timing.t.vact,
1417 base.preferred_timing.t.interlaced ? "i" : "");
1420 if (!cta.native_timings.empty()) {
1421 printf("\n----------------\n");
1422 printf("\nNative Video Resolution%s if Block 0 and CTA-861 Blocks are parsed:\n",
1423 native_prog.size() + native_int.size() > 1 ? "s" : "");
1424 for (resolution_set::iterator iter = native_prog.begin();
1425 iter != native_prog.end(); ++iter)
1426 printf(" %ux%u\n", iter->first, iter->second);
1427 for (resolution_set::iterator iter = native_int.begin();
1428 iter != native_int.end(); ++iter)
1429 printf(" %ux%ui\n", iter->first, iter->second);
1432 if (!cta.native_timing_nvrdb.empty()) {
1433 printf("\n----------------\n");
1434 printf("\nNative Video Resolution if Block 0 and CTA-861 Blocks are parsed with NVRDB support:\n");
1435 for (resolution_set::iterator iter = native_nvrdb.begin();
1436 iter != native_nvrdb.end(); ++iter)
1437 printf(" %ux%u\n", iter->first, iter->second);
1440 if (dispid.native_width) {
1441 printf("\n----------------\n");
1442 printf("\nNative Video Resolution if the DisplayID Blocks are parsed:\n");
1443 printf(" %ux%u\n", dispid.native_width, dispid.native_height);
1447 int edid_state::parse_edid()
1449 hide_serial_numbers = options[OptHideSerialNumbers];
1450 replace_unique_ids = options[OptReplaceUniqueIDs];
1452 preparse_base_block(edid);
1453 if (replace_unique_ids)
1454 replace_checksum(edid, EDID_PAGE_SIZE);
1456 for (unsigned i = 1; i < num_blocks; i++)
1457 preparse_extension(edid + i * EDID_PAGE_SIZE);
1459 if (options[OptPhysicalAddress]) {
1460 printf("%x.%x.%x.%x\n",
1461 (cta.preparsed_phys_addr >> 12) & 0xf,
1462 (cta.preparsed_phys_addr >> 8) & 0xf,
1463 (cta.preparsed_phys_addr >> 4) & 0xf,
1464 cta.preparsed_phys_addr & 0xf);
1465 return 0;
1468 if (!options[OptSkipHexDump]) {
1469 printf("edid-decode (hex):\n\n");
1470 for (unsigned i = 0; i < num_blocks; i++) {
1471 hex_block("", edid + i * EDID_PAGE_SIZE, EDID_PAGE_SIZE, false);
1472 if (i == num_blocks - 1 && options[OptOnlyHexDump])
1473 return 0;
1474 printf("\n");
1476 printf("----------------\n\n");
1479 block = block_name(0x00);
1480 printf("Block %u, %s:\n", block_nr, block.c_str());
1481 parse_base_block(edid);
1483 for (unsigned i = 1; i < num_blocks; i++) {
1484 block_nr++;
1485 printf("\n----------------\n");
1486 parse_extension(edid + i * EDID_PAGE_SIZE);
1489 block = "";
1490 block_nr = EDID_MAX_BLOCKS;
1492 if (cta.has_svrs)
1493 cta_resolve_svrs();
1495 if (options[OptPreferredTimings])
1496 print_preferred_timings();
1498 print_native_res();
1500 if (!options[OptCheck] && !options[OptCheckInline])
1501 return 0;
1503 check_base_block(edid);
1504 if (has_cta)
1505 check_cta_blocks();
1506 if (has_dispid)
1507 check_displayid_blocks();
1509 printf("\n----------------\n");
1511 if (!options[OptSkipSHA] && strlen(STRING(SHA))) {
1512 printf("\nedid-decode SHA: %s %s\n", STRING(SHA), STRING(DATE));
1515 if (options[OptCheck]) {
1516 if (warnings)
1517 show_msgs(true);
1518 if (failures)
1519 show_msgs(false);
1521 printf("\nEDID conformity: %s\n", failures ? "FAIL" : "PASS");
1522 return failures ? -2 : 0;
1525 #ifndef __EMSCRIPTEN__
1527 static unsigned char crc_calc(const unsigned char *b)
1529 unsigned char sum = 0;
1530 unsigned i;
1532 for (i = 0; i < 127; i++)
1533 sum += b[i];
1534 return 256 - sum;
1537 static int crc_ok(const unsigned char *b)
1539 return crc_calc(b) == b[127];
1542 static void hexdumpedid(FILE *f, const unsigned char *edid, unsigned size)
1544 unsigned b, i, j;
1546 for (b = 0; b < size / 128; b++) {
1547 const unsigned char *buf = edid + 128 * b;
1549 if (b)
1550 fprintf(f, "\n");
1551 for (i = 0; i < 128; i += 0x10) {
1552 fprintf(f, "%02x", buf[i]);
1553 for (j = 1; j < 0x10; j++) {
1554 fprintf(f, " %02x", buf[i + j]);
1556 fprintf(f, "\n");
1558 if (!crc_ok(buf))
1559 fprintf(f, "Block %u has a checksum error (should be 0x%02x).\n",
1560 b, crc_calc(buf));
1564 static void carraydumpedid(FILE *f, const unsigned char *edid, unsigned size)
1566 unsigned b, i, j;
1568 fprintf(f, "const unsigned char edid[] = {\n");
1569 for (b = 0; b < size / 128; b++) {
1570 const unsigned char *buf = edid + 128 * b;
1572 if (b)
1573 fprintf(f, "\n");
1574 for (i = 0; i < 128; i += 8) {
1575 fprintf(f, "\t0x%02x,", buf[i]);
1576 for (j = 1; j < 8; j++) {
1577 fprintf(f, " 0x%02x,", buf[i + j]);
1579 fprintf(f, "\n");
1581 if (!crc_ok(buf))
1582 fprintf(f, "\t/* Block %u has a checksum error (should be 0x%02x). */\n",
1583 b, crc_calc(buf));
1585 fprintf(f, "};\n");
1588 // This format can be read by the QuantumData EDID editor
1589 static void xmldumpedid(FILE *f, const unsigned char *edid, unsigned size)
1591 fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
1592 fprintf(f, "<DATAOBJ>\n");
1593 fprintf(f, " <HEADER TYPE=\"DID\" VERSION=\"1.0\"/>\n");
1594 fprintf(f, " <DATA>\n");
1595 for (unsigned b = 0; b < size / 128; b++) {
1596 const unsigned char *buf = edid + 128 * b;
1598 fprintf(f, " <BLOCK%u>", b);
1599 for (unsigned i = 0; i < 128; i++)
1600 fprintf(f, "%02X", buf[i]);
1601 fprintf(f, "</BLOCK%u>\n", b);
1603 fprintf(f, " </DATA>\n");
1604 fprintf(f, "</DATAOBJ>\n");
1607 static int edid_to_file(const char *to_file, enum output_format out_fmt)
1609 FILE *out;
1611 if (!strcmp(to_file, "-")) {
1612 to_file = "stdout";
1613 out = stdout;
1614 } else if ((out = fopen(to_file, "w")) == NULL) {
1615 perror(to_file);
1616 return -1;
1618 if (out_fmt == OUT_FMT_DEFAULT)
1619 out_fmt = out == stdout ? OUT_FMT_HEX : OUT_FMT_RAW;
1621 switch (out_fmt) {
1622 default:
1623 case OUT_FMT_HEX:
1624 hexdumpedid(out, edid, state.edid_size);
1625 break;
1626 case OUT_FMT_RAW:
1627 fwrite(edid, state.edid_size, 1, out);
1628 break;
1629 case OUT_FMT_CARRAY:
1630 carraydumpedid(out, edid, state.edid_size);
1631 break;
1632 case OUT_FMT_XML:
1633 xmldumpedid(out, edid, state.edid_size);
1634 break;
1637 if (out != stdout)
1638 fclose(out);
1639 return 0;
1642 enum cvt_opts {
1643 CVT_WIDTH = 0,
1644 CVT_HEIGHT,
1645 CVT_FPS,
1646 CVT_INTERLACED,
1647 CVT_OVERSCAN,
1648 CVT_RB,
1649 CVT_ALT,
1650 CVT_RB_H_BLANK,
1651 CVT_RB_V_BLANK,
1652 CVT_EARLY_VSYNC,
1655 static int parse_cvt_subopt(char **subopt_str, double *value)
1657 int opt;
1658 char *opt_str;
1660 static const char * const subopt_list[] = {
1661 "w",
1662 "h",
1663 "fps",
1664 "interlaced",
1665 "overscan",
1666 "rb",
1667 "alt",
1668 "hblank",
1669 "vblank",
1670 "early-vsync",
1671 nullptr
1674 opt = getsubopt(subopt_str, (char* const*) subopt_list, &opt_str);
1676 if (opt == -1) {
1677 fprintf(stderr, "Invalid suboptions specified.\n");
1678 usage();
1679 std::exit(EXIT_FAILURE);
1681 if (opt_str == nullptr && opt != CVT_INTERLACED && opt != CVT_ALT &&
1682 opt != CVT_OVERSCAN && opt != CVT_EARLY_VSYNC) {
1683 fprintf(stderr, "No value given to suboption <%s>.\n",
1684 subopt_list[opt]);
1685 usage();
1686 std::exit(EXIT_FAILURE);
1689 if (opt_str)
1690 *value = strtod(opt_str, nullptr);
1691 return opt;
1694 static void parse_cvt(char *optarg)
1696 unsigned w = 0, h = 0;
1697 double fps = 0;
1698 unsigned rb = RB_NONE;
1699 unsigned rb_h_blank = 0;
1700 unsigned rb_v_blank = 460;
1701 bool interlaced = false;
1702 bool alt = false;
1703 bool overscan = false;
1704 bool early_vsync = false;
1706 while (*optarg != '\0') {
1707 int opt;
1708 double opt_val;
1710 opt = parse_cvt_subopt(&optarg, &opt_val);
1712 switch (opt) {
1713 case CVT_WIDTH:
1714 w = round(opt_val);
1715 break;
1716 case CVT_HEIGHT:
1717 h = round(opt_val);
1718 break;
1719 case CVT_FPS:
1720 fps = opt_val;
1721 break;
1722 case CVT_RB:
1723 rb = opt_val;
1724 break;
1725 case CVT_OVERSCAN:
1726 overscan = true;
1727 break;
1728 case CVT_INTERLACED:
1729 interlaced = opt_val;
1730 break;
1731 case CVT_ALT:
1732 alt = opt_val;
1733 break;
1734 case CVT_RB_H_BLANK:
1735 rb_h_blank = opt_val;
1736 break;
1737 case CVT_RB_V_BLANK:
1738 rb_v_blank = opt_val;
1739 if (rb_v_blank < 460) {
1740 fprintf(stderr, "vblank must be >= 460, set to 460.\n");
1741 rb_v_blank = 460;
1742 } else if (rb_v_blank > 705) {
1743 fprintf(stderr, "warning: vblank values > 705 might not be supported by RBv3 compliant sources.\n");
1745 break;
1746 case CVT_EARLY_VSYNC:
1747 early_vsync = true;
1748 break;
1749 default:
1750 break;
1754 if (!w || !h || !fps) {
1755 fprintf(stderr, "Missing width, height and/or fps.\n");
1756 usage();
1757 std::exit(EXIT_FAILURE);
1759 if (interlaced)
1760 fps /= 2;
1761 timings t = state.calc_cvt_mode(w, h, fps, rb, interlaced, overscan, alt,
1762 rb_h_blank, rb_v_blank, early_vsync);
1763 state.print_timings("", &t, "CVT", "", true, false);
1766 struct gtf_parsed_data {
1767 unsigned w, h;
1768 double freq;
1769 double C, M, K, J;
1770 bool overscan;
1771 bool interlaced;
1772 bool secondary;
1773 bool params_from_edid;
1774 enum gtf_ip_parm ip_parm;
1777 enum gtf_opts {
1778 GTF_WIDTH = 0,
1779 GTF_HEIGHT,
1780 GTF_FPS,
1781 GTF_HORFREQ,
1782 GTF_PIXCLK,
1783 GTF_INTERLACED,
1784 GTF_OVERSCAN,
1785 GTF_SECONDARY,
1786 GTF_C2,
1787 GTF_M,
1788 GTF_K,
1789 GTF_J2,
1792 static int parse_gtf_subopt(char **subopt_str, double *value)
1794 int opt;
1795 char *opt_str;
1797 static const char * const subopt_list[] = {
1798 "w",
1799 "h",
1800 "fps",
1801 "horfreq",
1802 "pixclk",
1803 "interlaced",
1804 "overscan",
1805 "secondary",
1806 "C",
1807 "M",
1808 "K",
1809 "J",
1810 nullptr
1813 opt = getsubopt(subopt_str, (char * const *)subopt_list, &opt_str);
1815 if (opt == -1) {
1816 fprintf(stderr, "Invalid suboptions specified.\n");
1817 usage();
1818 std::exit(EXIT_FAILURE);
1820 if (opt_str == nullptr && opt != GTF_INTERLACED && opt != GTF_OVERSCAN &&
1821 opt != GTF_SECONDARY) {
1822 fprintf(stderr, "No value given to suboption <%s>.\n",
1823 subopt_list[opt]);
1824 usage();
1825 std::exit(EXIT_FAILURE);
1828 if (opt == GTF_C2 || opt == GTF_J2)
1829 *value = round(2.0 * strtod(opt_str, nullptr));
1830 else if (opt_str)
1831 *value = strtod(opt_str, nullptr);
1832 return opt;
1835 static void parse_gtf(char *optarg, gtf_parsed_data &data)
1837 memset(&data, 0, sizeof(data));
1838 data.params_from_edid = true;
1839 data.C = 40;
1840 data.M = 600;
1841 data.K = 128;
1842 data.J = 20;
1844 while (*optarg != '\0') {
1845 int opt;
1846 double opt_val;
1848 opt = parse_gtf_subopt(&optarg, &opt_val);
1850 switch (opt) {
1851 case GTF_WIDTH:
1852 data.w = round(opt_val);
1853 break;
1854 case GTF_HEIGHT:
1855 data.h = round(opt_val);
1856 break;
1857 case GTF_FPS:
1858 data.freq = opt_val;
1859 data.ip_parm = gtf_ip_vert_freq;
1860 break;
1861 case GTF_HORFREQ:
1862 data.freq = opt_val;
1863 data.ip_parm = gtf_ip_hor_freq;
1864 break;
1865 case GTF_PIXCLK:
1866 data.freq = opt_val;
1867 data.ip_parm = gtf_ip_clk_freq;
1868 break;
1869 case GTF_INTERLACED:
1870 data.interlaced = true;
1871 break;
1872 case GTF_OVERSCAN:
1873 data.overscan = true;
1874 break;
1875 case GTF_SECONDARY:
1876 data.secondary = true;
1877 break;
1878 case GTF_C2:
1879 data.C = opt_val / 2.0;
1880 data.params_from_edid = false;
1881 break;
1882 case GTF_M:
1883 data.M = round(opt_val);
1884 data.params_from_edid = false;
1885 break;
1886 case GTF_K:
1887 data.K = round(opt_val);
1888 data.params_from_edid = false;
1889 break;
1890 case GTF_J2:
1891 data.J = opt_val / 2.0;
1892 data.params_from_edid = false;
1893 break;
1894 default:
1895 break;
1899 if (!data.w || !data.h) {
1900 fprintf(stderr, "Missing width and/or height.\n");
1901 usage();
1902 std::exit(EXIT_FAILURE);
1904 if (!data.freq) {
1905 fprintf(stderr, "One of fps, horfreq or pixclk must be given.\n");
1906 usage();
1907 std::exit(EXIT_FAILURE);
1909 if (!data.secondary)
1910 data.params_from_edid = false;
1911 if (data.interlaced && data.ip_parm == gtf_ip_vert_freq)
1912 data.freq /= 2;
1915 static void show_gtf(gtf_parsed_data &data)
1917 timings t;
1919 t = state.calc_gtf_mode(data.w, data.h, data.freq, data.interlaced,
1920 data.ip_parm, data.overscan, data.secondary,
1921 data.C, data.M, data.K, data.J);
1922 calc_ratio(&t);
1923 state.print_timings("", &t, "GTF", "", true, false);
1926 enum ovt_opts {
1927 OVT_RID,
1928 OVT_WIDTH,
1929 OVT_HEIGHT,
1930 OVT_FPS,
1933 static int parse_ovt_subopt(char **subopt_str, unsigned *value)
1935 int opt;
1936 char *opt_str;
1938 static const char * const subopt_list[] = {
1939 "rid",
1940 "w",
1941 "h",
1942 "fps",
1943 nullptr
1946 opt = getsubopt(subopt_str, (char* const*) subopt_list, &opt_str);
1948 if (opt == -1) {
1949 fprintf(stderr, "Invalid suboptions specified.\n");
1950 usage();
1951 std::exit(EXIT_FAILURE);
1953 if (opt_str == nullptr) {
1954 fprintf(stderr, "No value given to suboption <%s>.\n",
1955 subopt_list[opt]);
1956 usage();
1957 std::exit(EXIT_FAILURE);
1960 if (opt_str)
1961 *value = strtoul(opt_str, NULL, 0);
1962 return opt;
1965 static void parse_ovt(char *optarg)
1967 unsigned rid = 0;
1968 unsigned w = 0, h = 0;
1969 unsigned fps = 0;
1971 while (*optarg != '\0') {
1972 int opt;
1973 unsigned opt_val;
1975 opt = parse_ovt_subopt(&optarg, &opt_val);
1977 switch (opt) {
1978 case OVT_RID:
1979 rid = opt_val;
1980 break;
1981 case OVT_WIDTH:
1982 w = opt_val;
1983 break;
1984 case OVT_HEIGHT:
1985 h = opt_val;
1986 break;
1987 case OVT_FPS:
1988 fps = opt_val;
1989 break;
1990 default:
1991 break;
1995 if ((!rid && (!w || !h)) || !fps) {
1996 fprintf(stderr, "Missing rid, width, height and/or fps.\n");
1997 usage();
1998 std::exit(EXIT_FAILURE);
2000 unsigned hratio = 0, vratio = 0;
2001 if (rid) {
2002 const cta_rid *r = find_rid(rid);
2004 if (r) {
2005 w = r->hact;
2006 h = r->vact;
2007 hratio = r->hratio;
2008 vratio = r->vratio;
2011 timings t = state.calc_ovt_mode(w, h, hratio, vratio, fps);
2012 state.print_timings("", &t, "OVT", "", true, false);
2015 int main(int argc, char **argv)
2017 char short_options[26 * 2 * 2 + 1];
2018 enum output_format out_fmt = OUT_FMT_DEFAULT;
2019 gtf_parsed_data gtf_data;
2020 unsigned list_rid = 0;
2021 int ret;
2023 while (1) {
2024 int option_index = 0;
2025 unsigned idx = 0;
2026 unsigned i, val;
2027 const timings *t;
2028 char buf[16];
2030 for (i = 0; long_options[i].name; i++) {
2031 if (!isalpha(long_options[i].val))
2032 continue;
2033 short_options[idx++] = long_options[i].val;
2034 if (long_options[i].has_arg == required_argument)
2035 short_options[idx++] = ':';
2037 short_options[idx] = 0;
2038 int ch = getopt_long(argc, argv, short_options,
2039 long_options, &option_index);
2040 if (ch == -1)
2041 break;
2043 options[ch] = 1;
2044 switch (ch) {
2045 case OptHelp:
2046 usage();
2047 return -1;
2048 case OptOutputFormat:
2049 if (!strcmp(optarg, "hex")) {
2050 out_fmt = OUT_FMT_HEX;
2051 } else if (!strcmp(optarg, "raw")) {
2052 out_fmt = OUT_FMT_RAW;
2053 } else if (!strcmp(optarg, "carray")) {
2054 out_fmt = OUT_FMT_CARRAY;
2055 } else if (!strcmp(optarg, "xml")) {
2056 out_fmt = OUT_FMT_XML;
2057 } else {
2058 usage();
2059 exit(1);
2061 break;
2062 case OptDiag:
2063 state.diagonal = strtod(optarg, NULL);
2064 break;
2065 case OptSTD: {
2066 unsigned char byte1, byte2 = 0;
2067 char *endptr;
2069 byte1 = strtoul(optarg, &endptr, 0);
2070 if (*endptr == ',')
2071 byte2 = strtoul(endptr + 1, NULL, 0);
2072 state.print_standard_timing("", byte1, byte2, false, true);
2073 break;
2075 case OptDMT:
2076 val = strtoul(optarg, NULL, 0);
2077 t = find_dmt_id(val);
2078 if (t) {
2079 sprintf(buf, "DMT 0x%02x", val);
2080 state.print_timings("", t, buf, "", true, false);
2081 } else {
2082 fprintf(stderr, "Unknown DMT code 0x%02x.\n", val);
2084 break;
2085 case OptVIC:
2086 val = strtoul(optarg, NULL, 0);
2087 t = find_vic_id(val);
2088 if (t) {
2089 sprintf(buf, "VIC %3u", val);
2090 state.print_timings("", t, buf, "", true, false);
2091 } else {
2092 fprintf(stderr, "Unknown VIC code %u.\n", val);
2094 break;
2095 case OptHDMIVIC:
2096 val = strtoul(optarg, NULL, 0);
2097 t = find_hdmi_vic_id(val);
2098 if (t) {
2099 sprintf(buf, "HDMI VIC %u", val);
2100 state.print_timings("", t, buf, "", true, false);
2101 } else {
2102 fprintf(stderr, "Unknown HDMI VIC code %u.\n", val);
2104 break;
2105 case OptCVT:
2106 parse_cvt(optarg);
2107 break;
2108 case OptGTF:
2109 parse_gtf(optarg, gtf_data);
2110 break;
2111 case OptOVT:
2112 parse_ovt(optarg);
2113 break;
2114 case OptListRIDTimings:
2115 list_rid = strtoul(optarg, NULL, 0);
2116 break;
2117 case ':':
2118 fprintf(stderr, "Option '%s' requires a value.\n",
2119 argv[optind]);
2120 usage();
2121 return -1;
2122 case '?':
2123 fprintf(stderr, "Unknown argument '%s'.\n",
2124 argv[optind]);
2125 usage();
2126 return -1;
2129 if (optind == argc && options[OptVersion]) {
2130 if (strlen(STRING(SHA)))
2131 printf("edid-decode SHA: %s %s\n", STRING(SHA), STRING(DATE));
2132 else
2133 printf("edid-decode SHA: not available\n");
2134 return 0;
2137 if (options[OptListEstTimings])
2138 state.list_established_timings();
2139 if (options[OptListDMTs])
2140 state.list_dmts();
2141 if (options[OptListVICs])
2142 state.cta_list_vics();
2143 if (options[OptListHDMIVICs])
2144 state.cta_list_hdmi_vics();
2145 if (options[OptListRIDs])
2146 state.cta_list_rids();
2147 if (options[OptListRIDTimings])
2148 state.cta_list_rid_timings(list_rid);
2150 if (options[OptListEstTimings] || options[OptListDMTs] ||
2151 options[OptListVICs] || options[OptListHDMIVICs] ||
2152 options[OptListRIDs] || options[OptListRIDTimings])
2153 return 0;
2155 if (options[OptCVT] || options[OptDMT] || options[OptVIC] ||
2156 options[OptHDMIVIC] || options[OptSTD] || options[OptOVT])
2157 return 0;
2159 if (options[OptGTF] && (!gtf_data.params_from_edid || optind == argc)) {
2160 show_gtf(gtf_data);
2161 return 0;
2164 if (optind == argc)
2165 ret = edid_from_file("-", stdout);
2166 else
2167 ret = edid_from_file(argv[optind], argv[optind + 1] ? stderr : stdout);
2169 if (ret && options[OptPhysicalAddress]) {
2170 printf("f.f.f.f\n");
2171 return 0;
2173 if (optind < argc - 1)
2174 return ret ? ret : edid_to_file(argv[optind + 1], out_fmt);
2176 if (options[OptGTF]) {
2177 timings t;
2179 state.preparse_base_block(edid);
2181 t = state.calc_gtf_mode(gtf_data.w, gtf_data.h, gtf_data.freq,
2182 gtf_data.interlaced, gtf_data.ip_parm,
2183 gtf_data.overscan);
2184 unsigned hbl = t.hfp + t.hsync + t.hbp;
2185 unsigned htotal = t.hact + hbl;
2186 double hor_freq_khz = htotal ? (double)t.pixclk_khz / htotal : 0;
2188 if (state.base.supports_sec_gtf &&
2189 hor_freq_khz >= state.base.sec_gtf_start_freq) {
2190 t = state.calc_gtf_mode(gtf_data.w, gtf_data.h, gtf_data.freq,
2191 gtf_data.interlaced, gtf_data.ip_parm,
2192 gtf_data.overscan, true,
2193 state.base.C, state.base.M,
2194 state.base.K, state.base.J);
2196 calc_ratio(&t);
2197 if (t.hfp <= 0)
2198 state.print_timings("", &t, "GTF", "INVALID: Hfront <= 0", true, false);
2199 else
2200 state.print_timings("", &t, "GTF", "", true, false);
2201 return 0;
2204 return ret ? ret : state.parse_edid();
2207 #else
2210 * The surrounding JavaScript implementation will call this function
2211 * each time it wants to decode an EDID. So this should reset all the
2212 * state and start over.
2214 extern "C" int parse_edid(const char *input)
2216 for (unsigned i = 0; i < EDID_MAX_BLOCKS + 1; i++) {
2217 s_msgs[i][0].clear();
2218 s_msgs[i][1].clear();
2220 options[OptCheck] = 1;
2221 options[OptPreferredTimings] = 1;
2222 options[OptNativeResolution] = 1;
2223 state = edid_state();
2224 int ret = edid_from_file(input, stderr);
2225 return ret ? ret : state.parse_edid();
2228 #endif