1 // SPDX-License-Identifier: MIT
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>
19 #include "edid-decode.h"
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
;
39 * Please keep in alphabetical order of the short option.
40 * That makes it easier to see which options are still free.
46 OptFBModeTimings
= 'F',
51 OptNativeResolution
= 'n',
53 OptOutputFormat
= 'o',
54 OptPreferredTimings
= 'p',
55 OptPhysicalAddress
= 'P',
57 OptShortTimings
= 'S',
60 OptXModeLineTimings
= 'X',
85 static char options
[OptLast
];
87 #ifndef __EMSCRIPTEN__
88 static struct option long_options
[] = {
89 { "help", no_argument
, 0, OptHelp
},
90 { "output-format", required_argument
, 0, OptOutputFormat
},
91 { "native-resolution", no_argument
, 0, OptNativeResolution
},
92 { "preferred-timings", no_argument
, 0, OptPreferredTimings
},
93 { "physical-address", no_argument
, 0, OptPhysicalAddress
},
94 { "skip-hex-dump", no_argument
, 0, OptSkipHexDump
},
95 { "only-hex-dump", no_argument
, 0, OptOnlyHexDump
},
96 { "skip-sha", no_argument
, 0, OptSkipSHA
},
97 { "hide-serial-numbers", no_argument
, 0, OptHideSerialNumbers
},
98 { "replace-unique-ids", no_argument
, 0, OptReplaceUniqueIDs
},
99 { "utf8", no_argument
, 0, OptUTF8
},
100 { "version", no_argument
, 0, OptVersion
},
101 { "check-inline", no_argument
, 0, OptCheckInline
},
102 { "check", no_argument
, 0, OptCheck
},
103 { "short-timings", no_argument
, 0, OptShortTimings
},
104 { "long-timings", no_argument
, 0, OptLongTimings
},
105 { "ntsc", no_argument
, 0, OptNTSC
},
106 { "xmodeline", no_argument
, 0, OptXModeLineTimings
},
107 { "fbmode", no_argument
, 0, OptFBModeTimings
},
108 { "v4l2-timings", no_argument
, 0, OptV4L2Timings
},
109 { "diagonal", required_argument
, 0, OptDiag
},
110 { "i2c-adapter", required_argument
, 0, OptI2CAdapter
},
111 { "i2c-edid", no_argument
, 0, OptI2CEDID
},
112 { "i2c-hdcp", no_argument
, 0, OptI2CHDCP
},
113 { "i2c-hdcp-ri", required_argument
, 0, OptI2CHDCPRi
},
114 { "std", required_argument
, 0, OptSTD
},
115 { "dmt", required_argument
, 0, OptDMT
},
116 { "vic", required_argument
, 0, OptVIC
},
117 { "hdmi-vic", required_argument
, 0, OptHDMIVIC
},
118 { "cvt", required_argument
, 0, OptCVT
},
119 { "gtf", required_argument
, 0, OptGTF
},
120 { "ovt", required_argument
, 0, OptOVT
},
121 { "list-established-timings", no_argument
, 0, OptListEstTimings
},
122 { "list-dmts", no_argument
, 0, OptListDMTs
},
123 { "list-vics", no_argument
, 0, OptListVICs
},
124 { "list-hdmi-vics", no_argument
, 0, OptListHDMIVICs
},
125 { "list-rid-timings", required_argument
, 0, OptListRIDTimings
},
126 { "list-rids", no_argument
, 0, OptListRIDs
},
127 { "infoframe", required_argument
, 0, OptInfoFrame
},
131 static void usage(void)
133 printf("Usage: edid-decode <options> [in [out]]\n"
134 " [in] EDID file to parse. Read from standard input if none given\n"
135 " and --infoframe was not used, or if the input filename is '-'.\n"
136 " [out] Output the read EDID to this file. Write to standard output\n"
137 " if the output filename is '-'.\n"
139 " -o, --output-format <fmt>\n"
140 " If [out] is specified, then write the EDID in this format.\n"
141 " <fmt> is one of:\n"
142 " hex: hex numbers in ascii text (default for stdout)\n"
143 " raw: binary data (default unless writing to stdout)\n"
144 " carray: c-program struct\n"
146 " -c, --check Check if the EDID conforms to the standards, failures and\n"
147 " warnings are reported at the end.\n"
148 " -C, --check-inline Check if the EDID conforms to the standards, failures and\n"
149 " warnings are reported inline.\n"
150 " -n, --native-resolution Report the native resolution.\n"
151 " -p, --preferred-timings Report the preferred timings.\n"
152 " -P, --physical-address Only report the CEC physical address.\n"
153 " -S, --short-timings Report all video timings in a short format.\n"
154 " -L, --long-timings Report all video timings in a long format.\n"
155 " -N, --ntsc Report the video timings suitable for NTSC-based video.\n"
156 " -X, --xmodeline Report all long video timings in Xorg.conf format.\n"
157 " -F, --fbmode Report all long video timings in fb.modes format.\n"
158 " -V, --v4l2-timings Report all long video timings in v4l2-dv-timings.h format.\n"
159 " -s, --skip-hex-dump Skip the initial hex dump of the EDID.\n"
160 " -H, --only-hex-dump Only output the hex dump of the EDID.\n"
161 " --skip-sha Skip the SHA report.\n"
162 " --hide-serial-numbers Hide serial numbers with '...'.\n"
163 " --replace-unique-ids Replace unique IDs (serial numbers, dates, Container IDs) with fixed values.\n"
164 " -u, --utf8 Convert strings in EDIDs to UTF-8.\n"
165 " --version Show the edid-decode version (SHA).\n"
166 " --diagonal <inches> Set the display's diagonal in inches.\n"
167 " -a, --i2c-adapter <num> Use /dev/i2c-<num> to access the DDC lines.\n"
168 " --i2c-edid Read the EDID from the DDC lines.\n"
169 " --i2c-hdcp Read the HDCP from the DDC lines.\n"
170 " --i2c-hdcp-ri=<t> Read and print the HDCP Ri information every <t> seconds.\n"
171 " --std <byte1>,<byte2> Show the standard timing represented by these two bytes.\n"
172 " --dmt <dmt> Show the timings for the DMT with the given DMT ID.\n"
173 " --vic <vic> Show the timings for this VIC.\n"
174 " --hdmi-vic <hdmivic> Show the timings for this HDMI VIC.\n"
175 " --cvt w=<width>,h=<height>,fps=<fps>[,rb=<rb>][,interlaced][,overscan][,alt][,hblank=<hblank>][,vblank=<vblank>][,early-vsync]\n"
176 " Calculate the CVT timings for the given format.\n"
177 " <fps> is frames per second for progressive timings,\n"
178 " or fields per second for interlaced timings.\n"
179 " <rb> can be 0 (no reduced blanking, default), or\n"
180 " 1-3 for the reduced blanking version.\n"
181 " If 'interlaced' is given, then this is an interlaced format.\n"
182 " If 'overscan' is given, then this is an overscanned format.\n"
183 " If 'alt' is given and <rb>=2, then report the timings\n"
184 " optimized for video: 1000 / 1001 * <fps>.\n"
185 " If 'alt' is given and <rb>=3, then the horizontal blanking\n"
186 " is 160 instead of 80 pixels.\n"
187 " If 'hblank' is given and <rb>=3, then the horizontal blanking\n"
188 " is <hblank> pixels (range of 80-200), overriding 'alt'.\n"
189 " If 'vblank' is given and <rb>=3, then the vertical blanking\n"
190 " time is <vblank> microseconds (range of 460-705 or 300-440).\n"
191 " If 'early-vsync' is given and <rb=3>, then select early vsync.\n"
192 " --gtf w=<width>,h=<height>[,fps=<fps>][,horfreq=<horfreq>][,pixclk=<pixclk>][,interlaced]\n"
193 " [,overscan][,secondary][,C=<c>][,M=<m>][,K=<k>][,J=<j>]\n"
194 " Calculate the GTF timings for the given format.\n"
195 " <fps> is frames per second for progressive timings,\n"
196 " or fields per second for interlaced timings.\n"
197 " <horfreq> is the horizontal frequency in kHz.\n"
198 " <pixclk> is the pixel clock frequency in MHz.\n"
199 " Only one of fps, horfreq or pixclk must be given.\n"
200 " If 'interlaced' is given, then this is an interlaced format.\n"
201 " If 'overscan' is given, then this is an overscanned format.\n"
202 " If 'secondary' is given, then the secondary GTF is used for\n"
203 " reduced blanking, where <c>, <m>, <k> and <j> are parameters\n"
204 " for the secondary curve.\n"
205 " --ovt (rid=<rid>|w=<width>,h=<height>),fps=<fps>\n"
206 " Calculate the OVT timings for the given format.\n"
207 " Either specify a RID or explicitly specify width and height.\n"
208 " --list-established-timings List all known Established Timings.\n"
209 " --list-dmts List all known DMTs.\n"
210 " --list-vics List all known VICs.\n"
211 " --list-hdmi-vics List all known HDMI VICs.\n"
212 " --list-rids List all known RIDs.\n"
213 " --list-rid-timings <rid> List all timings for RID <rid> or all known RIDs if <rid> is 0.\n"
214 " -I, --infoframe <file> Parse the InfoFrame from <file> that was sent to this display.\n"
215 " This option can be specified multiple times for different InfoFrame files.\n"
216 " -h, --help Display this help message.\n");
220 static std::string s_msgs
[EDID_MAX_BLOCKS
+ 1][2];
222 void msg(bool is_warn
, const char *fmt
, ...)
228 vsprintf(buf
, fmt
, ap
);
235 if (state
.data_block
.empty())
236 s_msgs
[state
.block_nr
][is_warn
] += std::string(" ") + buf
;
238 s_msgs
[state
.block_nr
][is_warn
] += " " + state
.data_block
+ ": " + buf
;
240 if (options
[OptCheckInline
])
241 printf("%s: %s", is_warn
? "WARN" : "FAIL", buf
);
244 static void show_msgs(bool is_warn
)
246 printf("\n%s:\n\n", is_warn
? "Warnings" : "Failures");
247 for (unsigned i
= 0; i
< state
.num_blocks
; i
++) {
248 if (s_msgs
[i
][is_warn
].empty())
250 printf("Block %u, %s:\n%s",
251 i
, block_name(edid
[i
* EDID_PAGE_SIZE
]).c_str(),
252 s_msgs
[i
][is_warn
].c_str());
254 if (s_msgs
[EDID_MAX_BLOCKS
][is_warn
].empty())
257 s_msgs
[EDID_MAX_BLOCKS
][is_warn
].c_str());
261 void replace_checksum(unsigned char *x
, size_t len
)
263 unsigned char sum
= 0;
266 for (i
= 0; i
< len
- 1; i
++)
268 x
[len
- 1] = -sum
& 0xff;
271 void do_checksum(const char *prefix
, const unsigned char *x
, size_t len
, size_t checksum_pos
,
272 unsigned unused_bytes
)
274 unsigned char check
= x
[checksum_pos
];
275 unsigned char sum
= 0;
278 for (i
= 0; i
< len
; i
++) {
279 if (i
!= checksum_pos
)
283 printf("%sChecksum: 0x%02hhx", prefix
, check
);
284 if ((unsigned char)(check
+ sum
) != 0) {
285 printf(" (should be 0x%02x)", -sum
& 0xff);
286 fail("Invalid checksum 0x%02x (should be 0x%02x).\n",
290 printf(" Unused space in Extension Block: %u byte%s",
291 unused_bytes
, unused_bytes
> 1 ? "s" : "");
295 unsigned gcd(unsigned a
, unsigned b
)
306 void calc_ratio(struct timings
*t
)
308 unsigned d
= gcd(t
->hact
, t
->vact
);
311 t
->hratio
= t
->vratio
= 0;
314 t
->hratio
= t
->hact
/ d
;
315 t
->vratio
= t
->vact
/ d
;
317 if (t
->hratio
== 8 && t
->vratio
== 5) {
323 unsigned calc_fps(const struct timings
*t
)
325 unsigned vact
= t
->vact
;
326 unsigned vbl
= t
->vfp
+ t
->vsync
+ t
->vbp
+ 2 * t
->vborder
;
327 unsigned hbl
= t
->hfp
+ t
->hsync
+ t
->hbp
+ 2 * t
->hborder
;
328 unsigned htotal
= t
->hact
+ hbl
;
333 double vtotal
= vact
+ vbl
;
336 vtotal
= vact
+ t
->vfp
+ t
->vsync
+ t
->vbp
;
337 else if (t
->interlaced
)
338 vtotal
= vact
+ t
->vfp
+ t
->vsync
+ t
->vbp
+ 0.5;
340 return t
->pixclk_khz
* 1000.0 / (htotal
* vtotal
);
343 std::string
edid_state::dtd_type(unsigned cnt
)
345 unsigned len
= std::to_string(cta
.preparsed_total_dtds
).length();
347 sprintf(buf
, "DTD %*u", len
, cnt
);
351 bool match_timings(const timings
&t1
, const timings
&t2
)
353 if (t1
.hact
!= t2
.hact
||
354 t1
.vact
!= t2
.vact
||
356 t1
.interlaced
!= t2
.interlaced
||
359 t1
.hsync
!= t2
.hsync
||
360 t1
.pos_pol_hsync
!= t2
.pos_pol_hsync
||
361 t1
.hratio
!= t2
.hratio
||
364 t1
.vsync
!= t2
.vsync
||
365 t1
.pos_pol_vsync
!= t2
.pos_pol_vsync
||
366 t1
.vratio
!= t2
.vratio
||
367 t1
.pixclk_khz
!= t2
.pixclk_khz
)
372 static void or_str(std::string
&s
, const std::string
&flag
, unsigned &num_flags
)
376 else if (num_flags
% 2 == 0)
377 s
= s
+ " | \\\n\t\t" + flag
;
379 s
= s
+ " | " + flag
;
384 * Return true if the timings are a close, but not identical,
385 * match. The only differences allowed are polarities and
386 * porches and syncs, provided the total blanking remains the
389 bool timings_close_match(const timings
&t1
, const timings
&t2
)
391 // We don't want to deal with borders, you're on your own
392 // if you are using those.
393 if (t1
.hborder
|| t1
.vborder
||
394 t2
.hborder
|| t2
.vborder
)
396 if (t1
.hact
!= t2
.hact
|| t1
.vact
!= t2
.vact
||
397 t1
.interlaced
!= t2
.interlaced
||
398 t1
.pixclk_khz
!= t2
.pixclk_khz
||
399 t1
.hfp
+ t1
.hsync
+ t1
.hbp
!= t2
.hfp
+ t2
.hsync
+ t2
.hbp
||
400 t1
.vfp
+ t1
.vsync
+ t1
.vbp
!= t2
.vfp
+ t2
.vsync
+ t2
.vbp
)
402 if (t1
.hfp
== t2
.hfp
&&
403 t1
.hsync
== t2
.hsync
&&
405 t1
.pos_pol_hsync
== t2
.pos_pol_hsync
&&
407 t1
.vsync
== t2
.vsync
&&
409 t1
.pos_pol_vsync
== t2
.pos_pol_vsync
)
414 static void print_modeline(unsigned indent
, const struct timings
*t
, double refresh
)
416 unsigned offset
= (!t
->even_vtotal
&& t
->interlaced
) ? 1 : 0;
417 unsigned hfp
= t
->hborder
+ t
->hfp
;
418 unsigned hbp
= t
->hborder
+ t
->hbp
;
419 unsigned vfp
= t
->vborder
+ t
->vfp
;
420 unsigned vbp
= t
->vborder
+ t
->vbp
;
422 printf("%*sModeline \"%ux%u_%.2f%s\" %.3f %u %u %u %u %u %u %u %u %cHSync",
424 t
->hact
, t
->vact
, refresh
,
425 t
->interlaced
? "i" : "", t
->pixclk_khz
/ 1000.0,
426 t
->hact
, t
->hact
+ hfp
, t
->hact
+ hfp
+ t
->hsync
,
427 t
->hact
+ hfp
+ t
->hsync
+ hbp
,
428 t
->vact
, t
->vact
+ vfp
, t
->vact
+ vfp
+ t
->vsync
,
429 t
->vact
+ vfp
+ t
->vsync
+ vbp
+ offset
,
430 t
->pos_pol_hsync
? '+' : '-');
431 if (!t
->no_pol_vsync
)
432 printf(" %cVSync", t
->pos_pol_vsync
? '+' : '-');
434 printf(" Interlace");
438 static void print_fbmode(unsigned indent
, const struct timings
*t
,
439 double refresh
, double hor_freq_khz
)
441 printf("%*smode \"%ux%u-%u%s\"\n",
444 (unsigned)(0.5 + (t
->interlaced
? refresh
/ 2.0 : refresh
)),
445 t
->interlaced
? "-lace" : "");
446 printf("%*s# D: %.2f MHz, H: %.3f kHz, V: %.2f Hz\n",
448 t
->pixclk_khz
/ 1000.0, hor_freq_khz
, refresh
);
449 printf("%*sgeometry %u %u %u %u 32\n",
451 t
->hact
, t
->vact
, t
->hact
, t
->vact
);
452 unsigned mult
= t
->interlaced
? 2 : 1;
453 unsigned offset
= !t
->even_vtotal
&& t
->interlaced
;
454 unsigned hfp
= t
->hborder
+ t
->hfp
;
455 unsigned hbp
= t
->hborder
+ t
->hbp
;
456 unsigned vfp
= t
->vborder
+ t
->vfp
;
457 unsigned vbp
= t
->vborder
+ t
->vbp
;
458 printf("%*stimings %llu %d %d %d %u %u %u\n",
460 (unsigned long long)(1000000000.0 / (double)(t
->pixclk_khz
) + 0.5),
461 hbp
, hfp
, mult
* vbp
, mult
* vfp
+ offset
, t
->hsync
, mult
* t
->vsync
);
463 printf("%*slaced true\n", indent
+ 8, "");
464 if (t
->pos_pol_hsync
)
465 printf("%*shsync high\n", indent
+ 8, "");
466 if (t
->pos_pol_vsync
)
467 printf("%*svsync high\n", indent
+ 8, "");
468 printf("%*sendmode\n", indent
, "");
471 static void print_v4l2_timing(const struct timings
*t
,
472 double refresh
, const char *type
)
474 printf("\t#define V4L2_DV_BT_%uX%u%c%u_%02u { \\\n",
475 t
->hact
, t
->vact
, t
->interlaced
? 'I' : 'P',
476 (unsigned)refresh
, (unsigned)(0.5 + 100.0 * (refresh
- (unsigned)refresh
)));
477 printf("\t\t.type = V4L2_DV_BT_656_1120, \\\n");
478 printf("\t\tV4L2_INIT_BT_TIMINGS(%u, %u, %u, ",
479 t
->hact
, t
->vact
, t
->interlaced
);
480 if (!t
->pos_pol_hsync
&& !t
->pos_pol_vsync
)
482 else if (t
->pos_pol_hsync
&& t
->pos_pol_vsync
)
483 printf("\\\n\t\t\tV4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \\\n");
484 else if (t
->pos_pol_hsync
)
485 printf("V4L2_DV_HSYNC_POS_POL, \\\n");
487 printf("V4L2_DV_VSYNC_POS_POL, \\\n");
488 unsigned hfp
= t
->hborder
+ t
->hfp
;
489 unsigned hbp
= t
->hborder
+ t
->hbp
;
490 unsigned vfp
= t
->vborder
+ t
->vfp
;
491 unsigned vbp
= t
->vborder
+ t
->vbp
;
492 printf("\t\t\t%lluULL, %d, %u, %d, %u, %u, %d, %u, %u, %d, \\\n",
493 t
->pixclk_khz
* 1000ULL, hfp
, t
->hsync
, hbp
,
495 t
->interlaced
? vfp
: 0,
496 t
->interlaced
? t
->vsync
: 0,
497 t
->interlaced
? vbp
+ !t
->even_vtotal
: 0);
500 unsigned num_flags
= 0;
502 unsigned hdmi_vic
= 0;
503 const char *std
= "0";
505 if (t
->interlaced
&& !t
->even_vtotal
)
506 or_str(flags
, "V4L2_DV_FL_HALF_LINE", num_flags
);
507 if (!memcmp(type
, "VIC", 3)) {
508 or_str(flags
, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags
);
509 or_str(flags
, "V4L2_DV_FL_IS_CE_VIDEO", num_flags
);
510 vic
= strtoul(type
+ 4, 0, 0);
512 if (!memcmp(type
, "HDMI VIC", 8)) {
513 or_str(flags
, "V4L2_DV_FL_HAS_HDMI_VIC", num_flags
);
514 or_str(flags
, "V4L2_DV_FL_IS_CE_VIDEO", num_flags
);
515 hdmi_vic
= strtoul(type
+ 9, 0, 0);
516 vic
= hdmi_vic_to_vic(hdmi_vic
);
518 or_str(flags
, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags
);
520 if (vic
&& (fmod(refresh
, 6)) == 0.0)
521 or_str(flags
, "V4L2_DV_FL_CAN_REDUCE_FPS", num_flags
);
523 or_str(flags
, "V4L2_DV_FL_REDUCED_BLANKING", num_flags
);
524 if (t
->hratio
&& t
->vratio
)
525 or_str(flags
, "V4L2_DV_FL_HAS_PICTURE_ASPECT", num_flags
);
527 if (!memcmp(type
, "VIC", 3) || !memcmp(type
, "HDMI VIC", 8))
528 std
= "V4L2_DV_BT_STD_CEA861";
529 else if (!memcmp(type
, "DMT", 3))
530 std
= "V4L2_DV_BT_STD_DMT";
531 else if (!memcmp(type
, "CVT", 3))
532 std
= "V4L2_DV_BT_STD_CVT";
533 else if (!memcmp(type
, "GTF", 3))
534 std
= "V4L2_DV_BT_STD_GTF";
535 printf("\t\t\t%s, \\\n", std
);
536 printf("\t\t\t%s, \\\n", flags
.empty() ? "0" : flags
.c_str());
537 printf("\t\t\t{ %u, %u }, %u, %u) \\\n",
538 t
->hratio
, t
->vratio
, vic
, hdmi_vic
);
542 static void print_detailed_timing(unsigned indent
, const struct timings
*t
)
544 printf("%*sHfront %4d Hsync %3u Hback %4d Hpol %s",
546 t
->hfp
, t
->hsync
, t
->hbp
, t
->pos_pol_hsync
? "P" : "N");
548 printf(" Hborder %u", t
->hborder
);
551 printf("%*sVfront %4u Vsync %3u Vback %4d",
552 indent
, "", t
->vfp
, t
->vsync
, t
->vbp
);
553 if (!t
->no_pol_vsync
)
554 printf(" Vpol %s", t
->pos_pol_vsync
? "P" : "N");
556 printf(" Vborder %u", t
->vborder
);
557 if (t
->even_vtotal
) {
558 printf(" Both Fields");
559 } else if (t
->interlaced
) {
560 printf(" Vfront +0.5 Odd Field\n");
561 printf("%*sVfront %4d Vsync %3u Vback %4d",
562 indent
, "", t
->vfp
, t
->vsync
, t
->vbp
);
563 if (!t
->no_pol_vsync
)
564 printf(" Vpol %s", t
->pos_pol_vsync
? "P" : "N");
566 printf(" Vborder %u", t
->vborder
);
567 printf(" Vback +0.5 Even Field");
572 bool edid_state::print_timings(const char *prefix
, const struct timings
*t
,
573 const char *type
, const char *flags
,
574 bool detailed
, bool do_checks
, unsigned ntsc
)
579 fail("Unknown video timings.\n");
583 if (detailed
&& options
[OptShortTimings
])
585 if (options
[OptLongTimings
])
588 unsigned vact
= t
->vact
;
589 unsigned hbl
= t
->hfp
+ t
->hsync
+ t
->hbp
+ 2 * t
->hborder
;
590 unsigned vbl
= t
->vfp
+ t
->vsync
+ t
->vbp
+ 2 * t
->vborder
;
591 unsigned htotal
= t
->hact
+ hbl
;
592 double hor_freq_khz
= htotal
? (double)t
->pixclk_khz
/ htotal
: 0;
597 double out_hor_freq_khz
= hor_freq_khz
;
601 double vtotal
= vact
+ vbl
;
605 if (!t
->hact
|| !hbl
|| !t
->hfp
|| !t
->hsync
||
606 !vact
|| !vbl
|| (!t
->vfp
&& !t
->interlaced
&& !t
->even_vtotal
) || !t
->vsync
) {
608 fail("0 values in the video timing:\n"
609 " Horizontal Active/Blanking %u/%u\n"
610 " Horizontal Frontporch/Sync Width %u/%u\n"
611 " Vertical Active/Blanking %u/%u\n"
612 " Vertical Frontporch/Sync Width %u/%u\n",
613 t
->hact
, hbl
, t
->hfp
, t
->hsync
, vact
, vbl
, t
->vfp
, t
->vsync
);
618 vtotal
= vact
+ t
->vfp
+ t
->vsync
+ t
->vbp
;
619 else if (t
->interlaced
)
620 vtotal
= vact
+ t
->vfp
+ t
->vsync
+ t
->vbp
+ 0.5;
622 double refresh
= t
->pixclk_khz
* 1000.0 / (htotal
* vtotal
);
623 double pixclk
= t
->pixclk_khz
* 1000.0;
624 if (((ntsc
> 1 && options
[OptNTSC
]) || ntsc
== 1) && fmod(refresh
, 6.0) == 0) {
625 const double ntsc_fact
= 1000.0 / 1001.0;
627 refresh
*= ntsc_fact
;
628 out_hor_freq_khz
*= ntsc_fact
;
632 unsigned rb
= t
->rb
& ~RB_ALT
;
634 bool alt
= t
->rb
& RB_ALT
;
637 s
+= std::string("v2") + (alt
? ",video-optimized" : "");
638 else if (rb
== RB_CVT_V3
)
639 s
+= std::string("v3") + (alt
? ",h-blank-160" : "");
642 if (t
->hsize_mm
|| t
->vsize_mm
)
643 add_str(s
, std::to_string(t
->hsize_mm
) + " mm x " + std::to_string(t
->vsize_mm
) + " mm");
644 if (t
->hsize_mm
> dtd_max_hsize_mm
)
645 dtd_max_hsize_mm
= t
->hsize_mm
;
646 if (t
->vsize_mm
> dtd_max_vsize_mm
)
647 dtd_max_vsize_mm
= t
->vsize_mm
;
650 unsigned pixclk_khz
= t
->pixclk_khz
/ (t
->ycbcr420
? 2 : 1);
654 sprintf(buf
, "%u%s", t
->vact
, t
->interlaced
? "i" : "");
655 printf("%s%s: %5ux%-5s %10.6f Hz %3u:%-3u %8.3f kHz %13.6f MHz%s\n",
659 t
->hratio
, t
->vratio
,
664 unsigned len
= strlen(prefix
) + 2;
666 if (!t
->ycbcr420
&& detailed
&& options
[OptXModeLineTimings
])
667 print_modeline(len
, t
, refresh
);
668 else if (!t
->ycbcr420
&& detailed
&& options
[OptFBModeTimings
])
669 print_fbmode(len
, t
, refresh
, hor_freq_khz
);
670 else if (!t
->ycbcr420
&& detailed
&& options
[OptV4L2Timings
])
671 print_v4l2_timing(t
, refresh
, type
);
673 print_detailed_timing(len
+ strlen(type
) + 6, t
);
678 if (!memcmp(type
, "DTD", 3)) {
680 const timings
*vic_t
= cta_close_match_to_vic(*t
, vic
);
682 // We report this even if there is no CTA block since it
683 // is still likely that the actual VIC timings were intended.
685 warn("DTD is similar but not identical to VIC %u.\n", vic
);
687 if (cta_matches_vic(*t
, vic
) && has_cta
&&
688 !cta
.preparsed_has_vic
[0][vic
]) {
689 warn("DTD is identical to VIC %u, which is not present in the CTA Ext Block.\n", vic
);
691 if (cta
.preparsed_max_vic_pixclk_khz
&& t
->pixclk_khz
> 340000 &&
692 t
->pixclk_khz
> cta
.preparsed_max_vic_pixclk_khz
)
693 cta
.warn_about_hdmi_2x_dtd
= true;
696 const timings
*dmt_t
= close_match_to_dmt(*t
, dmt
);
698 warn("DTD is similar but not identical to DMT 0x%02x.\n", dmt
);
702 min_vert_freq_hz
= min(min_vert_freq_hz
, refresh
);
703 max_vert_freq_hz
= max(max_vert_freq_hz
, refresh
);
706 min_hor_freq_hz
= min(min_hor_freq_hz
, hor_freq_khz
* 1000.0);
707 max_hor_freq_hz
= max(max_hor_freq_hz
, hor_freq_khz
* 1000.0);
708 max_pixclk_khz
= max(max_pixclk_khz
, pixclk_khz
);
709 if (t
->pos_pol_hsync
&& !t
->pos_pol_vsync
&& t
->vsync
== 3)
710 base
.max_pos_neg_hor_freq_khz
= hor_freq_khz
;
713 if (t
->ycbcr420
&& t
->pixclk_khz
< 590000)
714 warn_once("Some YCbCr 4:2:0 timings are invalid for HDMI 2.1 (which requires an RGB timings pixel rate >= 590 MHz).\n");
716 fail("0 or negative horizontal front porch.\n");
718 fail("0 or negative horizontal back porch.\n");
720 fail("0 or negative vertical back porch.\n");
721 if (!base
.max_display_width_mm
&& !base
.max_display_height_mm
) {
723 } else if (!t
->hsize_mm
&& !t
->vsize_mm
) {
725 } else if (t
->hsize_mm
> base
.max_display_width_mm
+ 9 ||
726 t
->vsize_mm
> base
.max_display_height_mm
+ 9) {
727 fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
728 t
->hsize_mm
, t
->vsize_mm
, base
.max_display_width_mm
, base
.max_display_height_mm
);
729 } else if (t
->hsize_mm
< base
.max_display_width_mm
- 9 &&
730 t
->vsize_mm
< base
.max_display_height_mm
- 9) {
731 fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
732 t
->hsize_mm
, t
->vsize_mm
, base
.max_display_width_mm
, base
.max_display_height_mm
);
737 std::string
containerid2s(const unsigned char *x
)
741 sprintf(buf
, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
742 x
[0], x
[1], x
[2], x
[3],
746 x
[10], x
[11], x
[12], x
[13], x
[14], x
[15]);
750 std::string
utohex(unsigned char x
)
754 sprintf(buf
, "0x%02hhx", x
);
758 const char *oui_name(unsigned oui
, unsigned *ouinum
)
760 unsigned ouinumscratch
;
761 if (!ouinum
) ouinum
= &ouinumscratch
;
764 #define oneoui(c,k,n) case c: *ouinum = kOUI_##k; name = n; break;
766 default: *ouinum
= 0; name
= NULL
; break;
771 void edid_state::data_block_oui(std::string block_name
, const unsigned char *x
,
772 unsigned length
, unsigned *ouinum
, bool ignorezeros
, bool do_ascii
, bool big_endian
,
778 const char *ouiname
= NULL
;
779 bool matched_reverse
= false;
780 bool matched_ascii
= false;
781 bool valid_ascii
= false;
784 oui
= ((length
> 0 ? x
[0] : 0) << 16) + ((length
> 1 ? x
[1] : 0) << 8) + (length
> 2 ? x
[2] : 0);
786 oui
= ((length
> 2 ? x
[2] : 0) << 16) + ((length
> 1 ? x
[1] : 0) << 8) + (length
> 0 ? x
[0] : 0);
790 sprintf(ascii
, "?"); // some characters are null
791 if (ouinum
) *ouinum
= 0; // doesn't match a known OUI
793 valid_ascii
= (x
[0] >= 'A' && x
[1] >= 'A' && x
[2] >= 'A' && x
[0] <= 'Z' && x
[1] <= 'Z' && x
[2] <= 'Z');
794 sprintf(ascii
, "%c%c%c", x
[0], x
[1], x
[2]);
796 ouiname
= oui_name(oui
, ouinum
);
798 big_endian
= !big_endian
;
799 unsigned reversedoui
= ((oui
& 0xff) << 16) + (oui
& 0x00ff00) + (oui
>> 16);
800 ouiname
= oui_name(reversedoui
, ouinum
);
804 matched_reverse
= true;
805 } else if (do_ascii
&& valid_ascii
) {
806 unsigned asciioui
= (x
[0] << 24) + (x
[1] << 16) + (x
[2] << 8);
807 ouiname
= oui_name(asciioui
, ouinum
);
809 matched_ascii
= true;
818 name
= block_name
+ " (" + ouiname
+ ")" + ", PNP ID '" + ascii
+ "'";
820 name
= block_name
+ " (" + ouiname
+ ")" + ", OUI " + buf
;
821 } else if (do_ascii
&& valid_ascii
) {
822 name
= block_name
+ ", PNP ID '" + ascii
+ "'";
824 name
= block_name
+ ", OUI " + buf
;
826 // assign string to data_block before outputting errors
829 if (oui
|| !ignorezeros
) {
831 printf(" %s:\n", data_block
.c_str());
833 fail("Data block length (%d) is not enough to contain an OUI.\n", length
);
835 if (do_ascii
&& !valid_ascii
)
836 warn("Expected PNP ID but found OUI.\n");
838 fail("Endian-ness (%s) of OUI is different than expected (%s).\n", big_endian
? "be" : "le", big_endian
? "le" : "be");
842 warn("Unknown OUI %s (possible PNP %s).\n", buf
.c_str(), ascii
);
844 warn("Unknown OUI %s.\n", buf
.c_str());
849 std::string
ouitohex(unsigned oui
)
853 sprintf(buf
, "%02X-%02X-%02X", (oui
>> 16) & 0xff, (oui
>> 8) & 0xff, oui
& 0xff);
857 bool memchk(const unsigned char *x
, unsigned len
, unsigned char v
)
859 for (unsigned i
= 0; i
< len
; i
++)
865 void hex_block(const char *prefix
, const unsigned char *x
,
866 unsigned length
, bool show_ascii
, unsigned step
)
870 for (i
= 0; i
< length
; i
+= step
) {
871 unsigned len
= min(step
, length
- i
);
873 printf("%s", prefix
);
874 for (j
= 0; j
< len
; j
++)
875 printf("%s%02x", j
? " " : "", x
[i
+ j
]);
878 for (j
= len
; j
< step
; j
++)
881 for (j
= 0; j
< len
; j
++)
882 printf("%c", x
[i
+ j
] >= ' ' && x
[i
+ j
] <= '~' ? x
[i
+ j
] : '.');
889 static bool edid_add_byte(const char *s
, bool two_digits
= true)
893 if (state
.edid_size
== sizeof(edid
))
896 buf
[1] = two_digits
? s
[1] : 0;
898 edid
[state
.edid_size
++] = strtoul(buf
, NULL
, 16);
902 static bool extract_edid_quantumdata(const char *start
)
904 /* Parse QuantumData 980 EDID files */
906 start
= strstr(start
, ">");
910 for (unsigned i
= 0; start
[i
] && start
[i
+ 1] && i
< 256; i
+= 2)
911 if (!edid_add_byte(start
+ i
))
913 start
= strstr(start
, "<BLOCK");
915 return state
.edid_size
;
918 static const char *ignore_chars
= ",:;";
920 static bool extract_edid_hex(const char *s
, bool require_two_digits
= true)
923 if (isspace(*s
) || strchr(ignore_chars
, *s
))
926 if (*s
== '0' && tolower(s
[1]) == 'x') {
931 /* Read one or two hex digits from the log */
932 if (!isxdigit(s
[0])) {
933 if (state
.edid_size
&& state
.edid_size
% 128 == 0)
937 if (require_two_digits
&& !isxdigit(s
[1])) {
938 odd_hex_digits
= true;
941 if (!edid_add_byte(s
, isxdigit(s
[1])))
946 return state
.edid_size
;
949 static bool extract_edid_xrandr(const char *start
)
951 static const char indentation1
[] = " ";
952 static const char indentation2
[] = "\t\t";
953 /* Used to detect that we've gone past the EDID property */
954 static const char half_indentation1
[] = " ";
955 static const char half_indentation2
[] = "\t";
956 const char *indentation
;
962 /* Get the next start of the line of EDID hex, assuming spaces for indentation */
963 s
= strstr(start
, indentation
= indentation1
);
964 /* Did we skip the start of another property? */
965 if (s
&& s
> strstr(start
, half_indentation1
))
968 /* If we failed, retry assuming tabs for indentation */
970 s
= strstr(start
, indentation
= indentation2
);
971 /* Did we skip the start of another property? */
972 if (s
&& s
> strstr(start
, half_indentation2
))
979 start
= s
+ strlen(indentation
);
981 for (j
= 0; j
< 16; j
++, start
+= 2) {
982 /* Read a %02x from the log */
983 if (!isxdigit(start
[0]) || !isxdigit(start
[1])) {
988 if (!edid_add_byte(start
))
992 return state
.edid_size
;
995 static bool extract_edid_xorg(const char *start
)
997 bool find_first_num
= true;
999 for (; *start
; start
++) {
1000 if (find_first_num
) {
1003 /* skip ahead to the : */
1004 s
= strstr(start
, ": \t");
1006 s
= strstr(start
, ": ");
1010 /* and find the first number */
1011 while (!isxdigit(start
[1]))
1013 find_first_num
= false;
1016 /* Read a %02x from the log */
1017 if (!isxdigit(*start
)) {
1018 find_first_num
= true;
1021 if (!edid_add_byte(start
))
1026 return state
.edid_size
;
1029 static bool extract_edid(int fd
, FILE *error
)
1031 std::vector
<char> edid_data
;
1032 char buf
[EDID_PAGE_SIZE
];
1035 ssize_t i
= read(fd
, buf
, sizeof(buf
));
1041 edid_data
.insert(edid_data
.end(), buf
, buf
+ i
);
1044 if (edid_data
.empty()) {
1045 state
.edid_size
= 0;
1048 // Ensure it is safely terminated by a 0 char
1049 edid_data
.push_back('\0');
1051 const char *data
= &edid_data
[0];
1054 /* Look for edid-decode output */
1055 start
= strstr(data
, "EDID (hex):");
1057 start
= strstr(data
, "edid-decode (hex):");
1059 return extract_edid_hex(strchr(start
, ':'));
1061 /* Look for C-array */
1062 start
= strstr(data
, "unsigned char edid[] = {");
1064 return extract_edid_hex(strchr(start
, '{') + 1, false);
1066 /* Look for QuantumData EDID output */
1067 start
= strstr(data
, "<BLOCK");
1069 return extract_edid_quantumdata(start
);
1071 /* Look for xrandr --verbose output (lines of 16 hex bytes) */
1072 start
= strstr(data
, "EDID_DATA:");
1074 start
= strstr(data
, "EDID:");
1076 return extract_edid_xrandr(start
);
1078 /* Look for an EDID in an Xorg.0.log file */
1079 start
= strstr(data
, "EDID (in hex):");
1081 start
= strstr(start
, "(II)");
1083 return extract_edid_xorg(start
);
1087 /* Is the EDID provided in hex? */
1088 for (i
= 0; i
< 32 && (isspace(data
[i
]) || strchr(ignore_chars
, data
[i
]) ||
1089 tolower(data
[i
]) == 'x' || isxdigit(data
[i
])); i
++);
1092 return extract_edid_hex(data
);
1094 // Drop the extra '\0' byte since we now assume binary data
1095 edid_data
.pop_back();
1098 if (edid_data
.size() > sizeof(edid
)) {
1099 fprintf(error
, "Binary EDID length %zu is greater than %zu.\n",
1100 edid_data
.size(), sizeof(edid
));
1103 memcpy(edid
, data
, edid_data
.size());
1104 state
.edid_size
= edid_data
.size();
1108 static int edid_from_file(const char *from_file
, FILE *error
)
1111 // Windows compatibility
1112 int flags
= O_RDONLY
| O_BINARY
;
1114 int flags
= O_RDONLY
;
1118 if (!strcmp(from_file
, "-")) {
1119 from_file
= "stdin";
1121 } else if ((fd
= open(from_file
, flags
)) == -1) {
1126 odd_hex_digits
= false;
1127 if (!extract_edid(fd
, error
)) {
1128 if (!state
.edid_size
) {
1129 fprintf(error
, "EDID of '%s' was empty.\n", from_file
);
1132 fprintf(error
, "EDID extract of '%s' failed: ", from_file
);
1134 fprintf(error
, "odd number of hexadecimal digits.\n");
1136 fprintf(error
, "unknown format.\n");
1139 if (state
.edid_size
% EDID_PAGE_SIZE
) {
1140 fprintf(error
, "EDID length %u is not a multiple of %u.\n",
1141 state
.edid_size
, EDID_PAGE_SIZE
);
1144 state
.num_blocks
= state
.edid_size
/ EDID_PAGE_SIZE
;
1148 if (memcmp(edid
, "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", 8)) {
1149 fprintf(error
, "No EDID header found in '%s'.\n", from_file
);
1155 /* generic extension code */
1157 std::string
block_name(unsigned char block
)
1162 case 0x00: return "Base EDID";
1163 case 0x02: return "CTA-861 Extension Block";
1164 case 0x10: return "Video Timing Extension Block";
1165 case 0x20: return "EDID 2.0 Extension Block";
1166 case 0x40: return "Display Information Extension Block";
1167 case 0x50: return "Localized String Extension Block";
1168 case 0x60: return "Microdisplay Interface Extension Block";
1169 case 0x70: return "DisplayID Extension Block";
1170 case 0xf0: return "Block Map Extension Block";
1171 case 0xff: return "Manufacturer-Specific Extension Block";
1173 sprintf(buf
, " 0x%02x", block
);
1174 return std::string("Unknown EDID Extension Block") + buf
;
1178 void edid_state::parse_block_map(const unsigned char *x
)
1180 unsigned last_valid_block_tag
= 0;
1181 bool fail_once
= false;
1182 unsigned offset
= 1;
1186 block_map
.saw_block_1
= true;
1187 else if (!block_map
.saw_block_1
)
1188 fail("No EDID Block Map Extension found in block 1.\n");
1189 else if (block_nr
== 128)
1190 block_map
.saw_block_128
= true;
1195 for (i
= 1; i
< 127; i
++) {
1196 unsigned block
= offset
+ i
;
1199 last_valid_block_tag
++;
1200 if (i
!= last_valid_block_tag
&& !fail_once
) {
1201 fail("Valid block tags are not consecutive.\n");
1204 printf(" Block %3u: %s\n", block
, block_name(x
[i
]).c_str());
1205 if (block
>= num_blocks
) {
1207 fail("Invalid block number %u.\n", block
);
1209 } else if (x
[i
] != edid
[block
* EDID_PAGE_SIZE
]) {
1210 fail("Block %u tag mismatch: expected 0x%02x, but got 0x%02x.\n",
1211 block
, edid
[block
* EDID_PAGE_SIZE
], x
[i
]);
1213 } else if (block
< num_blocks
) {
1214 fail("Block %u tag mismatch: expected 0x%02x, but got 0x00.\n",
1215 block
, edid
[block
* EDID_PAGE_SIZE
]);
1220 void edid_state::preparse_extension(unsigned char *x
)
1225 preparse_cta_block(x
);
1228 preparse_ls_ext_block(x
);
1232 preparse_displayid_block(x
);
1237 void edid_state::parse_extension(const unsigned char *x
)
1239 block
= block_name(x
[0]);
1244 if (block_nr
&& x
[0] == 0)
1245 block
= "Unknown EDID Extension Block 0x00";
1246 printf("Block %u, %s:\n", block_nr
, block
.c_str());
1253 parse_vtb_ext_block(x
);
1256 fail("Deprecated extension block for EDID 2.0, do not use.\n");
1259 parse_di_ext_block(x
);
1262 parse_ls_ext_block(x
);
1265 parse_displayid_block(x
);
1269 if (block_nr
!= 1 && block_nr
!= 128)
1270 fail("Must be used in block 1 and 128.\n");
1273 hex_block(" ", x
, EDID_PAGE_SIZE
);
1274 fail("Unknown Extension Block.\n");
1279 do_checksum("", x
, EDID_PAGE_SIZE
, EDID_PAGE_SIZE
- 1, unused_bytes
);
1282 void edid_state::print_preferred_timings()
1284 if (base
.preferred_timing
.is_valid()) {
1285 printf("\n----------------\n");
1286 printf("\nPreferred Video Timing if only Block 0 is parsed:\n");
1287 print_timings(" ", base
.preferred_timing
, true, false);
1290 if (!cta
.preferred_timings
.empty()) {
1291 printf("\n----------------\n");
1292 printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
1293 cta
.preferred_timings
.size() > 1 ? "s" : "");
1294 for (vec_timings_ext::iterator iter
= cta
.preferred_timings
.begin();
1295 iter
!= cta
.preferred_timings
.end(); ++iter
)
1296 print_timings(" ", *iter
, true, false);
1299 if (!cta
.preferred_timings_vfpdb
.empty()) {
1300 printf("\n----------------\n");
1301 printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed with VFPDB support:\n",
1302 cta
.preferred_timings_vfpdb
.size() > 1 ? "s" : "");
1303 for (vec_timings_ext::iterator iter
= cta
.preferred_timings_vfpdb
.begin();
1304 iter
!= cta
.preferred_timings_vfpdb
.end(); ++iter
)
1305 print_timings(" ", *iter
, true, false);
1308 if (!dispid
.preferred_timings
.empty()) {
1309 printf("\n----------------\n");
1310 printf("\nPreferred Video Timing%s if Block 0 and DisplayID Blocks are parsed:\n",
1311 dispid
.preferred_timings
.size() > 1 ? "s" : "");
1312 for (vec_timings_ext::iterator iter
= dispid
.preferred_timings
.begin();
1313 iter
!= dispid
.preferred_timings
.end(); ++iter
)
1314 print_timings(" ", *iter
, true, false);
1318 void edid_state::print_native_res()
1320 typedef std::pair
<unsigned, unsigned> resolution
;
1321 typedef std::set
<resolution
> resolution_set
;
1322 resolution_set native_prog
, native_int
, native_nvrdb
;
1323 unsigned native_width
= 0, native_height
= 0;
1324 unsigned native_width_int
= 0, native_height_int
= 0;
1326 // Note: it is also a mismatch if Block 0 does not define a
1327 // native resolution, but other blocks do.
1328 bool native_mismatch
= false;
1329 bool native_int_mismatch
= false;
1331 if (base
.preferred_timing
.is_valid() && base
.preferred_is_also_native
) {
1332 if (base
.preferred_timing
.t
.interlaced
) {
1333 native_width_int
= base
.preferred_timing
.t
.hact
;
1334 native_height_int
= base
.preferred_timing
.t
.vact
;
1336 native_width
= base
.preferred_timing
.t
.hact
;
1337 native_height
= base
.preferred_timing
.t
.vact
;
1341 if (!native_width
&& dispid
.native_width
) {
1342 native_width
= dispid
.native_width
;
1343 native_height
= dispid
.native_height
;
1344 native_mismatch
= true;
1345 } else if (dispid
.native_width
&& native_width
&&
1346 (dispid
.native_width
!= native_width
||
1347 dispid
.native_height
!= native_height
)) {
1348 native_mismatch
= true;
1351 for (vec_timings_ext::iterator iter
= cta
.native_timings
.begin();
1352 iter
!= cta
.native_timings
.end(); ++iter
) {
1353 if (iter
->t
.interlaced
) {
1354 native_int
.insert(std::pair
<unsigned, unsigned>(iter
->t
.hact
, iter
->t
.vact
));
1355 if (!native_width_int
) {
1356 native_width_int
= iter
->t
.hact
;
1357 native_height_int
= iter
->t
.vact
;
1358 native_int_mismatch
= true;
1359 } else if (native_width_int
&&
1360 (iter
->t
.hact
!= native_width_int
||
1361 iter
->t
.vact
!= native_height_int
)) {
1362 native_int_mismatch
= true;
1365 native_prog
.insert(std::pair
<unsigned, unsigned>(iter
->t
.hact
, iter
->t
.vact
));
1366 if (!native_width
) {
1367 native_width
= iter
->t
.hact
;
1368 native_height
= iter
->t
.vact
;
1369 native_mismatch
= true;
1370 } else if (native_width
&&
1371 (iter
->t
.hact
!= native_width
||
1372 iter
->t
.vact
!= native_height
)) {
1373 native_mismatch
= true;
1378 for (vec_timings_ext::iterator iter
= cta
.native_timing_nvrdb
.begin();
1379 iter
!= cta
.native_timing_nvrdb
.end(); ++iter
) {
1380 if (iter
->t
.interlaced
) {
1381 fail("Interlaced native timing in NVRDB.\n");
1383 native_nvrdb
.insert(std::pair
<unsigned, unsigned>(iter
->t
.hact
, iter
->t
.vact
));
1384 if (!native_width
) {
1385 native_width
= iter
->t
.hact
;
1386 native_height
= iter
->t
.vact
;
1387 native_mismatch
= true;
1388 } else if (native_width
&&
1389 (iter
->t
.hact
!= native_width
||
1390 iter
->t
.vact
!= native_height
)) {
1391 native_mismatch
= true;
1398 double w
= image_width
;
1399 double h
= image_height
;
1400 double d
= sqrt(w
* w
+ h
* h
) / 254.0;
1402 if (fabs(diagonal
- d
) >= 0.1)
1403 warn("Specified diagonal is %.1f\", calculated diagonal is %.1f\".\n",
1407 double w
= native_width
;
1408 double h
= native_height
;
1409 double d
= diagonal
* 254.0;
1410 double c
= sqrt((d
* d
) / (w
* w
+ h
* h
));
1416 printf("\n----------------\n");
1417 printf("\nCalculated image size for a diagonal of %.1f\" is %.1fx%.1fmm.\n",
1418 diagonal
, w
/ 10.0, h
/ 10.0);
1420 if (fabs((double)image_width
- w
) >= 100.0 ||
1421 fabs((double)image_height
- h
) >= 100.0)
1422 warn("Calculated image size is %.1fx%.1fmm, EDID image size is %.1fx%.1fmm.\n",
1424 image_width
/ 10.0, image_height
/ 10.0);
1426 warn("No image size was specified, but it is calculated as %.1fx%.1fmm.\n",
1427 w
/ 10.0, h
/ 10.0);
1432 if (!options
[OptNativeResolution
])
1435 if (native_width
== 0 && native_width_int
== 0) {
1436 printf("\n----------------\n");
1437 printf("\nNo Native Video Resolution was defined.\n");
1441 if ((native_width
|| native_width_int
) &&
1442 !native_mismatch
&& !native_int_mismatch
) {
1443 printf("\n----------------\n");
1444 printf("\nNative Video Resolution%s:\n",
1445 native_width
&& native_width_int
? "s" : "");
1447 printf(" %ux%u\n", native_width
, native_height
);
1448 if (native_width_int
)
1449 printf(" %ux%ui\n", native_width_int
, native_height_int
);
1453 if (base
.preferred_timing
.is_valid() && base
.preferred_is_also_native
) {
1454 printf("\n----------------\n");
1455 printf("\nNative Video Resolution if only Block 0 is parsed:\n");
1456 printf(" %ux%u%s\n",
1457 base
.preferred_timing
.t
.hact
, base
.preferred_timing
.t
.vact
,
1458 base
.preferred_timing
.t
.interlaced
? "i" : "");
1461 if (!cta
.native_timings
.empty()) {
1462 printf("\n----------------\n");
1463 printf("\nNative Video Resolution%s if Block 0 and CTA-861 Blocks are parsed:\n",
1464 native_prog
.size() + native_int
.size() > 1 ? "s" : "");
1465 for (resolution_set::iterator iter
= native_prog
.begin();
1466 iter
!= native_prog
.end(); ++iter
)
1467 printf(" %ux%u\n", iter
->first
, iter
->second
);
1468 for (resolution_set::iterator iter
= native_int
.begin();
1469 iter
!= native_int
.end(); ++iter
)
1470 printf(" %ux%ui\n", iter
->first
, iter
->second
);
1473 if (!cta
.native_timing_nvrdb
.empty()) {
1474 printf("\n----------------\n");
1475 printf("\nNative Video Resolution if Block 0 and CTA-861 Blocks are parsed with NVRDB support:\n");
1476 for (resolution_set::iterator iter
= native_nvrdb
.begin();
1477 iter
!= native_nvrdb
.end(); ++iter
)
1478 printf(" %ux%u\n", iter
->first
, iter
->second
);
1481 if (dispid
.native_width
) {
1482 printf("\n----------------\n");
1483 printf("\nNative Video Resolution if the DisplayID Blocks are parsed:\n");
1484 printf(" %ux%u\n", dispid
.native_width
, dispid
.native_height
);
1488 int edid_state::parse_edid()
1490 hide_serial_numbers
= options
[OptHideSerialNumbers
];
1491 replace_unique_ids
= options
[OptReplaceUniqueIDs
];
1493 to_utf8
= options
[OptUTF8
];
1495 preparse_base_block(edid
);
1496 if (replace_unique_ids
)
1497 replace_checksum(edid
, EDID_PAGE_SIZE
);
1499 for (unsigned i
= 1; i
< num_blocks
; i
++)
1500 preparse_extension(edid
+ i
* EDID_PAGE_SIZE
);
1502 if (options
[OptPhysicalAddress
]) {
1503 printf("%x.%x.%x.%x\n",
1504 (cta
.preparsed_phys_addr
>> 12) & 0xf,
1505 (cta
.preparsed_phys_addr
>> 8) & 0xf,
1506 (cta
.preparsed_phys_addr
>> 4) & 0xf,
1507 cta
.preparsed_phys_addr
& 0xf);
1511 if (!options
[OptSkipHexDump
]) {
1512 printf("edid-decode (hex):\n\n");
1513 for (unsigned i
= 0; i
< num_blocks
; i
++) {
1514 hex_block("", edid
+ i
* EDID_PAGE_SIZE
, EDID_PAGE_SIZE
, false);
1515 if (i
== num_blocks
- 1 && options
[OptOnlyHexDump
])
1519 printf("----------------\n\n");
1522 block
= block_name(0x00);
1523 printf("Block %u, %s:\n", block_nr
, block
.c_str());
1524 parse_base_block(edid
);
1526 for (unsigned i
= 1; i
< num_blocks
; i
++) {
1528 printf("\n----------------\n");
1529 parse_extension(edid
+ i
* EDID_PAGE_SIZE
);
1533 block_nr
= EDID_MAX_BLOCKS
;
1538 if (options
[OptPreferredTimings
])
1539 print_preferred_timings();
1543 if (!options
[OptCheck
] && !options
[OptCheckInline
])
1546 check_base_block(edid
);
1550 check_displayid_blocks();
1552 printf("\n----------------\n");
1554 if (!options
[OptSkipSHA
] && strlen(STRING(SHA
))) {
1555 options
[OptSkipSHA
] = 1;
1556 printf("\nedid-decode SHA: %s %s\n", STRING(SHA
), STRING(DATE
));
1559 if (options
[OptCheck
]) {
1565 printf("\nEDID conformity: %s\n", failures
? "FAIL" : "PASS");
1566 return failures
? -2 : 0;
1569 /* InfoFrame parsing */
1571 static unsigned char infoframe
[32];
1572 static unsigned if_size
;
1574 static bool if_add_byte(const char *s
)
1578 if (if_size
== sizeof(infoframe
))
1583 infoframe
[if_size
++] = strtoul(buf
, NULL
, 16);
1587 static bool extract_if_hex(const char *s
)
1593 /* Read one or two hex digits from the log */
1594 if (!isxdigit(s
[0]))
1597 if (!isxdigit(s
[1])) {
1598 odd_hex_digits
= true;
1601 if (!if_add_byte(s
))
1608 static bool extract_if(int fd
)
1610 std::vector
<char> if_data
;
1614 ssize_t i
= read(fd
, buf
, sizeof(buf
));
1620 if_data
.insert(if_data
.end(), buf
, buf
+ i
);
1623 if (if_data
.empty()) {
1627 // Ensure it is safely terminated by a 0 char
1628 if_data
.push_back('\0');
1630 const char *data
= &if_data
[0];
1633 /* Look for edid-decode output */
1634 start
= strstr(data
, "edid-decode InfoFrame (hex):");
1636 return extract_if_hex(strchr(start
, ':') + 1);
1638 // Drop the extra '\0' byte since we now assume binary data
1641 if_size
= if_data
.size();
1644 if (if_size
> sizeof(infoframe
)) {
1645 fprintf(stderr
, "Binary InfoFrame length %u is greater than %zu.\n",
1646 if_size
, sizeof(infoframe
));
1649 memcpy(infoframe
, data
, if_size
);
1653 static int if_from_file(const char *from_file
)
1656 // Windows compatibility
1657 int flags
= O_RDONLY
| O_BINARY
;
1659 int flags
= O_RDONLY
;
1663 memset(infoframe
, 0, sizeof(infoframe
));
1666 if ((fd
= open(from_file
, flags
)) == -1) {
1671 odd_hex_digits
= false;
1672 if (!extract_if(fd
)) {
1674 fprintf(stderr
, "InfoFrame of '%s' was empty.\n", from_file
);
1677 fprintf(stderr
, "InfoFrame extraction of '%s' failed: ", from_file
);
1679 fprintf(stderr
, "odd number of hexadecimal digits.\n");
1681 fprintf(stderr
, "unknown format.\n");
1689 static void show_if_msgs(bool is_warn
)
1691 printf("\n%s:\n\n", is_warn
? "Warnings" : "Failures");
1692 if (s_msgs
[0][is_warn
].empty())
1694 printf("InfoFrame:\n%s",
1695 s_msgs
[0][is_warn
].c_str());
1698 int edid_state::parse_if(const std::string
&fname
)
1700 int ret
= if_from_file(fname
.c_str());
1701 unsigned min_size
= 4;
1702 bool is_hdmi
= false;
1708 state
.data_block
.clear();
1710 if (!options
[OptSkipHexDump
]) {
1711 printf("edid-decode InfoFrame (hex):\n\n");
1712 hex_block("", infoframe
, if_size
, false);
1713 if (options
[OptOnlyHexDump
])
1715 printf("\n----------------\n\n");
1718 if (infoframe
[0] >= 0x80) {
1723 if (if_size
< min_size
) {
1724 fail("InfoFrame is too small to parse.\n");
1729 do_checksum("HDMI InfoFrame ", infoframe
, if_size
, 3);
1731 memcpy(infoframe
+ 3, infoframe
+ 4, if_size
- 4);
1732 infoframe
[0] &= 0x7f;
1736 switch (infoframe
[0]) {
1738 parse_if_vendor(infoframe
, if_size
);
1741 parse_if_avi(infoframe
, if_size
);
1744 parse_if_spd(infoframe
, if_size
);
1747 parse_if_audio(infoframe
, if_size
);
1750 parse_if_mpeg_source(infoframe
, if_size
);
1753 parse_if_ntsc_vbi(infoframe
, if_size
);
1756 parse_if_drm(infoframe
, if_size
);
1759 if (infoframe
[0] <= 0x1f)
1760 fail("Reserved InfoFrame type %hhx.\n", infoframe
[0]);
1762 fail("Forbidden InfoFrame type %hhx.\n", infoframe
[0]);
1766 if (!options
[OptCheck
] && !options
[OptCheckInline
])
1769 printf("\n----------------\n");
1771 if (!options
[OptSkipSHA
] && strlen(STRING(SHA
))) {
1772 options
[OptSkipSHA
] = 1;
1773 printf("\nedid-decode SHA: %s %s\n", STRING(SHA
), STRING(DATE
));
1776 if (options
[OptCheck
]) {
1780 show_if_msgs(false);
1783 printf("\n%s conformity: %s\n",
1784 state
.data_block
.empty() ? "InfoFrame" : state
.data_block
.c_str(),
1785 failures
? "FAIL" : "PASS");
1786 return failures
? -2 : 0;
1789 #ifndef __EMSCRIPTEN__
1791 static unsigned char crc_calc(const unsigned char *b
)
1793 unsigned char sum
= 0;
1796 for (i
= 0; i
< 127; i
++)
1801 static int crc_ok(const unsigned char *b
)
1803 return crc_calc(b
) == b
[127];
1806 static void hexdumpedid(FILE *f
, const unsigned char *edid
, unsigned size
)
1810 for (b
= 0; b
< size
/ 128; b
++) {
1811 const unsigned char *buf
= edid
+ 128 * b
;
1815 for (i
= 0; i
< 128; i
+= 0x10) {
1816 fprintf(f
, "%02x", buf
[i
]);
1817 for (j
= 1; j
< 0x10; j
++) {
1818 fprintf(f
, " %02x", buf
[i
+ j
]);
1823 fprintf(f
, "Block %u has a checksum error (should be 0x%02x).\n",
1828 static void carraydumpedid(FILE *f
, const unsigned char *edid
, unsigned size
)
1832 fprintf(f
, "const unsigned char edid[] = {\n");
1833 for (b
= 0; b
< size
/ 128; b
++) {
1834 const unsigned char *buf
= edid
+ 128 * b
;
1838 for (i
= 0; i
< 128; i
+= 8) {
1839 fprintf(f
, "\t0x%02x,", buf
[i
]);
1840 for (j
= 1; j
< 8; j
++) {
1841 fprintf(f
, " 0x%02x,", buf
[i
+ j
]);
1846 fprintf(f
, "\t/* Block %u has a checksum error (should be 0x%02x). */\n",
1852 // This format can be read by the QuantumData EDID editor
1853 static void xmldumpedid(FILE *f
, const unsigned char *edid
, unsigned size
)
1855 fprintf(f
, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
1856 fprintf(f
, "<DATAOBJ>\n");
1857 fprintf(f
, " <HEADER TYPE=\"DID\" VERSION=\"1.0\"/>\n");
1858 fprintf(f
, " <DATA>\n");
1859 for (unsigned b
= 0; b
< size
/ 128; b
++) {
1860 const unsigned char *buf
= edid
+ 128 * b
;
1862 fprintf(f
, " <BLOCK%u>", b
);
1863 for (unsigned i
= 0; i
< 128; i
++)
1864 fprintf(f
, "%02X", buf
[i
]);
1865 fprintf(f
, "</BLOCK%u>\n", b
);
1867 fprintf(f
, " </DATA>\n");
1868 fprintf(f
, "</DATAOBJ>\n");
1871 static int edid_to_file(const char *to_file
, enum output_format out_fmt
)
1875 if (!strcmp(to_file
, "-")) {
1878 } else if ((out
= fopen(to_file
, "w")) == NULL
) {
1882 if (out_fmt
== OUT_FMT_DEFAULT
)
1883 out_fmt
= out
== stdout
? OUT_FMT_HEX
: OUT_FMT_RAW
;
1888 hexdumpedid(out
, edid
, state
.edid_size
);
1891 fwrite(edid
, state
.edid_size
, 1, out
);
1893 case OUT_FMT_CARRAY
:
1894 carraydumpedid(out
, edid
, state
.edid_size
);
1897 xmldumpedid(out
, edid
, state
.edid_size
);
1919 static int parse_cvt_subopt(char **subopt_str
, double *value
)
1924 static const char * const subopt_list
[] = {
1938 opt
= getsubopt(subopt_str
, (char* const*) subopt_list
, &opt_str
);
1941 fprintf(stderr
, "Invalid suboptions specified.\n");
1943 std::exit(EXIT_FAILURE
);
1945 if (opt_str
== nullptr && opt
!= CVT_INTERLACED
&& opt
!= CVT_ALT
&&
1946 opt
!= CVT_OVERSCAN
&& opt
!= CVT_EARLY_VSYNC
) {
1947 fprintf(stderr
, "No value given to suboption <%s>.\n",
1950 std::exit(EXIT_FAILURE
);
1954 *value
= strtod(opt_str
, nullptr);
1958 static void parse_cvt(char *optarg
)
1960 unsigned w
= 0, h
= 0;
1962 unsigned rb
= RB_NONE
;
1963 unsigned rb_h_blank
= 0;
1964 unsigned rb_v_blank
= 460;
1965 bool interlaced
= false;
1967 bool overscan
= false;
1968 bool early_vsync
= false;
1970 while (*optarg
!= '\0') {
1974 opt
= parse_cvt_subopt(&optarg
, &opt_val
);
1992 case CVT_INTERLACED
:
1993 interlaced
= opt_val
;
1998 case CVT_RB_H_BLANK
:
1999 rb_h_blank
= opt_val
;
2001 case CVT_RB_V_BLANK
:
2002 rb_v_blank
= opt_val
;
2003 if (rb_v_blank
< 460) {
2004 fprintf(stderr
, "vblank must be >= 460, set to 460.\n");
2006 } else if (rb_v_blank
> 705) {
2007 fprintf(stderr
, "warning: vblank values > 705 might not be supported by RBv3 compliant sources.\n");
2010 case CVT_EARLY_VSYNC
:
2018 if (!w
|| !h
|| !fps
) {
2019 fprintf(stderr
, "Missing width, height and/or fps.\n");
2021 std::exit(EXIT_FAILURE
);
2025 timings t
= state
.calc_cvt_mode(w
, h
, fps
, rb
, interlaced
, overscan
, alt
,
2026 rb_h_blank
, rb_v_blank
, early_vsync
);
2027 state
.print_timings("", &t
, "CVT", "", true, false);
2030 struct gtf_parsed_data
{
2037 bool params_from_edid
;
2038 enum gtf_ip_parm ip_parm
;
2056 static int parse_gtf_subopt(char **subopt_str
, double *value
)
2061 static const char * const subopt_list
[] = {
2077 opt
= getsubopt(subopt_str
, (char * const *)subopt_list
, &opt_str
);
2080 fprintf(stderr
, "Invalid suboptions specified.\n");
2082 std::exit(EXIT_FAILURE
);
2084 if (opt_str
== nullptr && opt
!= GTF_INTERLACED
&& opt
!= GTF_OVERSCAN
&&
2085 opt
!= GTF_SECONDARY
) {
2086 fprintf(stderr
, "No value given to suboption <%s>.\n",
2089 std::exit(EXIT_FAILURE
);
2092 if (opt
== GTF_C2
|| opt
== GTF_J2
)
2093 *value
= round(2.0 * strtod(opt_str
, nullptr));
2095 *value
= strtod(opt_str
, nullptr);
2099 static void parse_gtf(char *optarg
, gtf_parsed_data
&data
)
2101 memset(&data
, 0, sizeof(data
));
2102 data
.params_from_edid
= true;
2108 while (*optarg
!= '\0') {
2112 opt
= parse_gtf_subopt(&optarg
, &opt_val
);
2116 data
.w
= round(opt_val
);
2119 data
.h
= round(opt_val
);
2122 data
.freq
= opt_val
;
2123 data
.ip_parm
= gtf_ip_vert_freq
;
2126 data
.freq
= opt_val
;
2127 data
.ip_parm
= gtf_ip_hor_freq
;
2130 data
.freq
= opt_val
;
2131 data
.ip_parm
= gtf_ip_clk_freq
;
2133 case GTF_INTERLACED
:
2134 data
.interlaced
= true;
2137 data
.overscan
= true;
2140 data
.secondary
= true;
2143 data
.C
= opt_val
/ 2.0;
2144 data
.params_from_edid
= false;
2147 data
.M
= round(opt_val
);
2148 data
.params_from_edid
= false;
2151 data
.K
= round(opt_val
);
2152 data
.params_from_edid
= false;
2155 data
.J
= opt_val
/ 2.0;
2156 data
.params_from_edid
= false;
2163 if (!data
.w
|| !data
.h
) {
2164 fprintf(stderr
, "Missing width and/or height.\n");
2166 std::exit(EXIT_FAILURE
);
2169 fprintf(stderr
, "One of fps, horfreq or pixclk must be given.\n");
2171 std::exit(EXIT_FAILURE
);
2173 if (!data
.secondary
)
2174 data
.params_from_edid
= false;
2175 if (data
.interlaced
&& data
.ip_parm
== gtf_ip_vert_freq
)
2179 static void show_gtf(gtf_parsed_data
&data
)
2183 t
= state
.calc_gtf_mode(data
.w
, data
.h
, data
.freq
, data
.interlaced
,
2184 data
.ip_parm
, data
.overscan
, data
.secondary
,
2185 data
.C
, data
.M
, data
.K
, data
.J
);
2187 state
.print_timings("", &t
, "GTF", "", true, false);
2197 static int parse_ovt_subopt(char **subopt_str
, unsigned *value
)
2202 static const char * const subopt_list
[] = {
2210 opt
= getsubopt(subopt_str
, (char* const*) subopt_list
, &opt_str
);
2213 fprintf(stderr
, "Invalid suboptions specified.\n");
2215 std::exit(EXIT_FAILURE
);
2217 if (opt_str
== nullptr) {
2218 fprintf(stderr
, "No value given to suboption <%s>.\n",
2221 std::exit(EXIT_FAILURE
);
2225 *value
= strtoul(opt_str
, NULL
, 0);
2229 static void parse_ovt(char *optarg
)
2232 unsigned w
= 0, h
= 0;
2235 while (*optarg
!= '\0') {
2239 opt
= parse_ovt_subopt(&optarg
, &opt_val
);
2259 if ((!rid
&& (!w
|| !h
)) || !fps
) {
2260 fprintf(stderr
, "Missing rid, width, height and/or fps.\n");
2262 std::exit(EXIT_FAILURE
);
2264 unsigned hratio
= 0, vratio
= 0;
2266 const cta_rid
*r
= find_rid(rid
);
2275 timings t
= state
.calc_ovt_mode(w
, h
, hratio
, vratio
, fps
);
2276 state
.print_timings("", &t
, "OVT", "", true, false);
2279 int main(int argc
, char **argv
)
2281 char short_options
[26 * 2 * 2 + 1];
2282 enum output_format out_fmt
= OUT_FMT_DEFAULT
;
2283 gtf_parsed_data gtf_data
;
2284 unsigned list_rid
= 0;
2285 int adapter_fd
= -1;
2286 double hdcp_ri_sleep
= 0;
2287 std::vector
<std::string
> if_names
;
2291 int option_index
= 0;
2297 for (i
= 0; long_options
[i
].name
; i
++) {
2298 if (!isalpha(long_options
[i
].val
))
2300 short_options
[idx
++] = long_options
[i
].val
;
2301 if (long_options
[i
].has_arg
== required_argument
)
2302 short_options
[idx
++] = ':';
2304 short_options
[idx
] = 0;
2305 int ch
= getopt_long(argc
, argv
, short_options
,
2306 long_options
, &option_index
);
2315 case OptOutputFormat
:
2316 if (!strcmp(optarg
, "hex")) {
2317 out_fmt
= OUT_FMT_HEX
;
2318 } else if (!strcmp(optarg
, "raw")) {
2319 out_fmt
= OUT_FMT_RAW
;
2320 } else if (!strcmp(optarg
, "carray")) {
2321 out_fmt
= OUT_FMT_CARRAY
;
2322 } else if (!strcmp(optarg
, "xml")) {
2323 out_fmt
= OUT_FMT_XML
;
2330 state
.diagonal
= strtod(optarg
, NULL
);
2332 #ifndef __EMSCRIPTEN__
2333 case OptI2CAdapter
: {
2334 unsigned num
= strtoul(optarg
, NULL
, 0);
2336 adapter_fd
= request_i2c_adapter(num
);
2343 hdcp_ri_sleep
= strtod(optarg
, NULL
);
2346 unsigned char byte1
, byte2
= 0;
2349 byte1
= strtoul(optarg
, &endptr
, 0);
2351 byte2
= strtoul(endptr
+ 1, NULL
, 0);
2352 state
.print_standard_timing("", byte1
, byte2
, false, true);
2356 val
= strtoul(optarg
, NULL
, 0);
2357 t
= find_dmt_id(val
);
2359 sprintf(buf
, "DMT 0x%02x", val
);
2360 state
.print_timings("", t
, buf
, "", true, false);
2362 fprintf(stderr
, "Unknown DMT code 0x%02x.\n", val
);
2366 val
= strtoul(optarg
, NULL
, 0);
2367 t
= find_vic_id(val
);
2369 sprintf(buf
, "VIC %3u", val
);
2370 state
.print_timings("", t
, buf
, "", true, false);
2372 fprintf(stderr
, "Unknown VIC code %u.\n", val
);
2376 val
= strtoul(optarg
, NULL
, 0);
2377 t
= find_hdmi_vic_id(val
);
2379 sprintf(buf
, "HDMI VIC %u", val
);
2380 state
.print_timings("", t
, buf
, "", true, false);
2382 fprintf(stderr
, "Unknown HDMI VIC code %u.\n", val
);
2389 parse_gtf(optarg
, gtf_data
);
2394 case OptListRIDTimings
:
2395 list_rid
= strtoul(optarg
, NULL
, 0);
2398 if_names
.push_back(optarg
);
2401 fprintf(stderr
, "Option '%s' requires a value.\n",
2406 fprintf(stderr
, "Unknown argument '%s'.\n",
2412 if (optind
== argc
&& options
[OptVersion
]) {
2413 if (strlen(STRING(SHA
)))
2414 printf("edid-decode SHA: %s %s\n", STRING(SHA
), STRING(DATE
));
2416 printf("edid-decode SHA: not available\n");
2420 if (options
[OptListEstTimings
])
2421 state
.list_established_timings();
2422 if (options
[OptListDMTs
])
2424 if (options
[OptListVICs
])
2425 state
.cta_list_vics();
2426 if (options
[OptListHDMIVICs
])
2427 state
.cta_list_hdmi_vics();
2428 if (options
[OptListRIDs
])
2429 state
.cta_list_rids();
2430 if (options
[OptListRIDTimings
])
2431 state
.cta_list_rid_timings(list_rid
);
2433 if (options
[OptListEstTimings
] || options
[OptListDMTs
] ||
2434 options
[OptListVICs
] || options
[OptListHDMIVICs
] ||
2435 options
[OptListRIDs
] || options
[OptListRIDTimings
])
2438 if (options
[OptCVT
] || options
[OptDMT
] || options
[OptVIC
] ||
2439 options
[OptHDMIVIC
] || options
[OptSTD
] || options
[OptOVT
])
2442 if (options
[OptGTF
] && (!gtf_data
.params_from_edid
|| optind
== argc
)) {
2447 if (optind
== argc
) {
2448 if (adapter_fd
>= 0 && options
[OptI2CEDID
]) {
2449 #ifndef __EMSCRIPTEN__
2450 ret
= read_edid(adapter_fd
, edid
);
2455 state
.edid_size
= ret
* EDID_PAGE_SIZE
;
2456 state
.num_blocks
= ret
;
2459 } else if (adapter_fd
>= 0) {
2460 #ifndef __EMSCRIPTEN__
2461 if (options
[OptI2CHDCP
])
2462 read_hdcp(adapter_fd
);
2463 if (options
[OptI2CHDCPRi
])
2464 read_hdcp_ri(adapter_fd
, hdcp_ri_sleep
);
2469 } else if (options
[OptInfoFrame
] && !options
[OptGTF
]) {
2472 ret
= edid_from_file("-", stdout
);
2475 ret
= edid_from_file(argv
[optind
], argv
[optind
+ 1] ? stderr
: stdout
);
2478 if (ret
&& options
[OptPhysicalAddress
]) {
2479 printf("f.f.f.f\n");
2482 if (optind
< argc
- 1)
2483 return ret
? ret
: edid_to_file(argv
[optind
+ 1], out_fmt
);
2485 if (options
[OptGTF
]) {
2488 state
.preparse_base_block(edid
);
2490 t
= state
.calc_gtf_mode(gtf_data
.w
, gtf_data
.h
, gtf_data
.freq
,
2491 gtf_data
.interlaced
, gtf_data
.ip_parm
,
2493 unsigned hbl
= t
.hfp
+ t
.hsync
+ t
.hbp
;
2494 unsigned htotal
= t
.hact
+ hbl
;
2495 double hor_freq_khz
= htotal
? (double)t
.pixclk_khz
/ htotal
: 0;
2497 if (state
.base
.supports_sec_gtf
&&
2498 hor_freq_khz
>= state
.base
.sec_gtf_start_freq
) {
2499 t
= state
.calc_gtf_mode(gtf_data
.w
, gtf_data
.h
, gtf_data
.freq
,
2500 gtf_data
.interlaced
, gtf_data
.ip_parm
,
2501 gtf_data
.overscan
, true,
2502 state
.base
.C
, state
.base
.M
,
2503 state
.base
.K
, state
.base
.J
);
2507 state
.print_timings("", &t
, "GTF", "INVALID: Hfront <= 0", true, false);
2509 state
.print_timings("", &t
, "GTF", "", true, false);
2513 if (!ret
&& state
.edid_size
)
2514 ret
= state
.parse_edid();
2516 bool show_line
= state
.edid_size
;
2518 for (const auto &n
: if_names
) {
2520 printf("\n================\n\n");
2523 state
.warnings
= state
.failures
= 0;
2524 for (unsigned i
= 0; i
< EDID_MAX_BLOCKS
+ 1; i
++) {
2525 s_msgs
[i
][0].clear();
2526 s_msgs
[i
][1].clear();
2528 int r
= state
.parse_if(n
);
2538 * The surrounding JavaScript implementation will call this function
2539 * each time it wants to decode an EDID. So this should reset all the
2540 * state and start over.
2542 extern "C" int parse_edid(const char *input
)
2544 for (unsigned i
= 0; i
< EDID_MAX_BLOCKS
+ 1; i
++) {
2545 s_msgs
[i
][0].clear();
2546 s_msgs
[i
][1].clear();
2548 options
[OptCheck
] = 1;
2549 options
[OptPreferredTimings
] = 1;
2550 options
[OptNativeResolution
] = 1;
2551 options
[OptSkipSHA
] = 0;
2552 options
[OptUTF8
] = 1;
2553 state
= edid_state();
2554 int ret
= edid_from_file(input
, stderr
);
2555 return ret
? ret
: state
.parse_edid();