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.
45 OptFBModeTimings
= 'F',
49 OptNativeTimings
= 'n',
50 OptOutputFormat
= 'o',
51 OptPreferredTimings
= 'p',
52 OptPhysicalAddress
= 'P',
54 OptShortTimings
= 'S',
56 OptXModeLineTimings
= 'X',
73 static char options
[OptLast
];
75 static struct option long_options
[] = {
76 { "help", no_argument
, 0, OptHelp
},
77 { "output-format", required_argument
, 0, OptOutputFormat
},
78 { "native-timings", no_argument
, 0, OptNativeTimings
},
79 { "preferred-timings", no_argument
, 0, OptPreferredTimings
},
80 { "physical-address", no_argument
, 0, OptPhysicalAddress
},
81 { "skip-hex-dump", no_argument
, 0, OptSkipHexDump
},
82 { "only-hex-dump", no_argument
, 0, OptOnlyHexDump
},
83 { "skip-sha", no_argument
, 0, OptSkipSHA
},
84 { "hide-serial-numbers", no_argument
, 0, OptHideSerialNumbers
},
85 { "version", no_argument
, 0, OptVersion
},
86 { "check-inline", no_argument
, 0, OptCheckInline
},
87 { "check", no_argument
, 0, OptCheck
},
88 { "short-timings", no_argument
, 0, OptShortTimings
},
89 { "long-timings", no_argument
, 0, OptLongTimings
},
90 { "xmodeline", no_argument
, 0, OptXModeLineTimings
},
91 { "fbmode", no_argument
, 0, OptFBModeTimings
},
92 { "v4l2-timings", no_argument
, 0, OptV4L2Timings
},
93 { "std", required_argument
, 0, OptSTD
},
94 { "dmt", required_argument
, 0, OptDMT
},
95 { "vic", required_argument
, 0, OptVIC
},
96 { "hdmi-vic", required_argument
, 0, OptHDMIVIC
},
97 { "cvt", required_argument
, 0, OptCVT
},
98 { "gtf", required_argument
, 0, OptGTF
},
99 { "list-established-timings", no_argument
, 0, OptListEstTimings
},
100 { "list-dmts", no_argument
, 0, OptListDMTs
},
101 { "list-vics", no_argument
, 0, OptListVICs
},
102 { "list-hdmi-vics", no_argument
, 0, OptListHDMIVICs
},
106 static void usage(void)
108 printf("Usage: edid-decode <options> [in [out]]\n"
109 " [in] EDID file to parse. Read from standard input if none given\n"
110 " or if the input filename is '-'.\n"
111 " [out] Output the read EDID to this file. Write to standard output\n"
112 " if the output filename is '-'.\n"
114 " -o, --output-format <fmt>\n"
115 " If [out] is specified, then write the EDID in this format\n"
116 " <fmt> is one of:\n"
117 " hex: hex numbers in ascii text (default for stdout)\n"
118 " raw: binary data (default unless writing to stdout)\n"
119 " carray: c-program struct\n"
121 " -c, --check Check if the EDID conforms to the standards, failures and\n"
122 " warnings are reported at the end.\n"
123 " -C, --check-inline Check if the EDID conforms to the standards, failures and\n"
124 " warnings are reported inline.\n"
125 " -n, --native-timings Report the native timings.\n"
126 " -p, --preferred-timings Report the preferred timings.\n"
127 " -P, --physical-address Only report the CEC physical address.\n"
128 " -S, --short-timings Report all video timings in a short format.\n"
129 " -L, --long-timings Report all video timings in a long format.\n"
130 " -X, --xmodeline Report all long video timings in Xorg.conf format.\n"
131 " -F, --fbmode Report all long video timings in fb.modes format.\n"
132 " -V, --v4l2-timings Report all long video timings in v4l2-dv-timings.h format.\n"
133 " -s, --skip-hex-dump Skip the initial hex dump of the EDID.\n"
134 " -H, --only-hex-dump Only output the hex dump of the EDID.\n"
135 " --skip-sha Skip the SHA report.\n"
136 " --hide-serial-numbers Replace serial numbers with '...'\n"
137 " --version show the edid-decode version (SHA)\n"
138 " --std <byte1>,<byte2> Show the standard timing represented by these two bytes.\n"
139 " --dmt <dmt> Show the timings for the DMT with the given DMT ID.\n"
140 " --vic <vic> Show the timings for this VIC.\n"
141 " --hdmi-vic <hdmivic> Show the timings for this HDMI VIC.\n"
142 " --cvt w=<width>,h=<height>,fps=<fps>[,rb=<rb>][,interlaced][,overscan][,alt][,hblank=<hblank][,add-vblank=<add-vblank>\n"
143 " Calculate the CVT timings for the given format.\n"
144 " <fps> is frames per second for progressive timings,\n"
145 " or fields per second for interlaced timings.\n"
146 " <rb> can be 0 (no reduced blanking, default), or\n"
147 " 1-3 for the reduced blanking version.\n"
148 " If 'interlaced' is given, then this is an interlaced format.\n"
149 " If 'overscan' is given, then this is an overscanned format.\n"
150 " If 'alt' is given and <rb>=2, then report the timings\n"
151 " optimized for video: 1000 / 1001 * <fps>.\n"
152 " If 'alt' is given and <rb>=3, then the horizontal blanking\n"
153 " is 160 instead of 80 pixels.\n"
154 " If 'hblank' is given and <rb>=3, then the horizontal blanking\n"
155 " is <hblank> pixels (range of 80-200), overriding 'alt'.\n"
156 " If 'add-vblank' is given and <rb>=3, then <add-vblank> usecs are\n"
157 " added to the minimum vertical blank time of 460 usecs.\n"
158 " --gtf w=<width>,h=<height>[,fps=<fps>][,horfreq=<horfreq>][,pixclk=<pixclk>][,interlaced]\n"
159 " [,overscan][,secondary][,C=<c>][,M=<m>][,K=<k>][,J=<j>]\n"
160 " Calculate the GTF timings for the given format.\n"
161 " <fps> is frames per second for progressive timings,\n"
162 " or fields per second for interlaced timings.\n"
163 " <horfreq> is the horizontal frequency in kHz.\n"
164 " <pixclk> is the pixel clock frequency in MHz.\n"
165 " Only one of fps, horfreq or pixclk must be given.\n"
166 " If 'interlaced' is given, then this is an interlaced format.\n"
167 " If 'overscan' is given, then this is an overscanned format.\n"
168 " If 'secondary' is given, then the secondary GTF is used for\n"
169 " reduced blanking, where <c>, <m>, <k> and <j> are parameters\n"
170 " for the secondary curve.\n"
171 " --list-established-timings List all known Established Timings.\n"
172 " --list-dmts List all known DMTs.\n"
173 " --list-vics List all known VICs.\n"
174 " --list-hdmi-vics List all known HDMI VICs.\n"
175 " -h, --help Display this help message.\n");
178 static std::string s_msgs
[EDID_MAX_BLOCKS
+ 1][2];
180 void msg(bool is_warn
, const char *fmt
, ...)
186 vsprintf(buf
, fmt
, ap
);
193 if (state
.data_block
.empty())
194 s_msgs
[state
.block_nr
][is_warn
] += std::string(" ") + buf
;
196 s_msgs
[state
.block_nr
][is_warn
] += " " + state
.data_block
+ ": " + buf
;
198 if (options
[OptCheckInline
])
199 printf("%s: %s", is_warn
? "WARN" : "FAIL", buf
);
202 static void show_msgs(bool is_warn
)
204 printf("\n%s:\n\n", is_warn
? "Warnings" : "Failures");
205 for (unsigned i
= 0; i
< state
.num_blocks
; i
++) {
206 if (s_msgs
[i
][is_warn
].empty())
208 printf("Block %u, %s:\n%s",
209 i
, block_name(edid
[i
* EDID_PAGE_SIZE
]).c_str(),
210 s_msgs
[i
][is_warn
].c_str());
212 if (s_msgs
[EDID_MAX_BLOCKS
][is_warn
].empty())
215 s_msgs
[EDID_MAX_BLOCKS
][is_warn
].c_str());
219 void do_checksum(const char *prefix
, const unsigned char *x
, size_t len
)
221 unsigned char check
= x
[len
- 1];
222 unsigned char sum
= 0;
225 printf("%sChecksum: 0x%02hhx", prefix
, check
);
227 for (i
= 0; i
< len
-1; i
++)
230 if ((unsigned char)(check
+ sum
) != 0) {
231 printf(" (should be 0x%02x)\n", -sum
& 0xff);
232 fail("Invalid checksum 0x%02x (should be 0x%02x).\n",
239 static unsigned gcd(unsigned a
, unsigned b
)
250 void calc_ratio(struct timings
*t
)
252 unsigned d
= gcd(t
->hact
, t
->vact
);
255 t
->hratio
= t
->vratio
= 0;
258 t
->hratio
= t
->hact
/ d
;
259 t
->vratio
= t
->vact
/ d
;
262 std::string
edid_state::dtd_type(unsigned cnt
)
264 unsigned len
= std::to_string(cta
.preparsed_total_dtds
).length();
266 sprintf(buf
, "DTD %*u", len
, cnt
);
270 bool edid_state::match_timings(const timings
&t1
, const timings
&t2
)
272 if (t1
.hact
!= t2
.hact
||
273 t1
.vact
!= t2
.vact
||
275 t1
.interlaced
!= t2
.interlaced
||
278 t1
.hsync
!= t2
.hsync
||
279 t1
.pos_pol_hsync
!= t2
.pos_pol_hsync
||
280 t1
.hratio
!= t2
.hratio
||
283 t1
.vsync
!= t2
.vsync
||
284 t1
.pos_pol_vsync
!= t2
.pos_pol_vsync
||
285 t1
.vratio
!= t2
.vratio
||
286 t1
.pixclk_khz
!= t2
.pixclk_khz
)
291 static void or_str(std::string
&s
, const std::string
&flag
, unsigned &num_flags
)
295 else if (num_flags
% 2 == 0)
296 s
= s
+ " | \\\n\t\t" + flag
;
298 s
= s
+ " | " + flag
;
303 * Return true if the timings are a close, but not identical,
304 * match. The only differences allowed are polarities and
305 * porches and syncs, provided the total blanking remains the
308 bool timings_close_match(const timings
&t1
, const timings
&t2
)
310 // We don't want to deal with borders, you're on your own
311 // if you are using those.
312 if (t1
.hborder
|| t1
.vborder
||
313 t2
.hborder
|| t2
.vborder
)
315 if (t1
.hact
!= t2
.hact
|| t1
.vact
!= t2
.vact
||
316 t1
.interlaced
!= t2
.interlaced
||
317 t1
.pixclk_khz
!= t2
.pixclk_khz
||
318 t1
.hfp
+ t1
.hsync
+ t1
.hbp
!= t2
.hfp
+ t2
.hsync
+ t2
.hbp
||
319 t1
.vfp
+ t1
.vsync
+ t1
.vbp
!= t2
.vfp
+ t2
.vsync
+ t2
.vbp
)
321 if (t1
.hfp
== t2
.hfp
&&
322 t1
.hsync
== t2
.hsync
&&
324 t1
.pos_pol_hsync
== t2
.pos_pol_hsync
&&
326 t1
.vsync
== t2
.vsync
&&
328 t1
.pos_pol_vsync
== t2
.pos_pol_vsync
)
333 static void print_modeline(unsigned indent
, const struct timings
*t
, double refresh
)
335 unsigned offset
= (!t
->even_vtotal
&& t
->interlaced
) ? 1 : 0;
336 unsigned hfp
= t
->hborder
+ t
->hfp
;
337 unsigned hbp
= t
->hborder
+ t
->hbp
;
338 unsigned vfp
= t
->vborder
+ t
->vfp
;
339 unsigned vbp
= t
->vborder
+ t
->vbp
;
341 printf("%*sModeline \"%ux%u_%.2f%s\" %.3f %u %u %u %u %u %u %u %u %cHSync",
343 t
->hact
, t
->vact
, refresh
,
344 t
->interlaced
? "i" : "", t
->pixclk_khz
/ 1000.0,
345 t
->hact
, t
->hact
+ hfp
, t
->hact
+ hfp
+ t
->hsync
,
346 t
->hact
+ hfp
+ t
->hsync
+ hbp
,
347 t
->vact
, t
->vact
+ vfp
, t
->vact
+ vfp
+ t
->vsync
,
348 t
->vact
+ vfp
+ t
->vsync
+ vbp
+ offset
,
349 t
->pos_pol_hsync
? '+' : '-');
350 if (!t
->no_pol_vsync
)
351 printf(" %cVSync", t
->pos_pol_vsync
? '+' : '-');
353 printf(" Interlace");
357 static void print_fbmode(unsigned indent
, const struct timings
*t
,
358 double refresh
, double hor_freq_khz
)
360 printf("%*smode \"%ux%u-%u%s\"\n",
363 (unsigned)(0.5 + (t
->interlaced
? refresh
/ 2.0 : refresh
)),
364 t
->interlaced
? "-lace" : "");
365 printf("%*s# D: %.2f MHz, H: %.3f kHz, V: %.2f Hz\n",
367 t
->pixclk_khz
/ 1000.0, hor_freq_khz
, refresh
);
368 printf("%*sgeometry %u %u %u %u 32\n",
370 t
->hact
, t
->vact
, t
->hact
, t
->vact
);
371 unsigned mult
= t
->interlaced
? 2 : 1;
372 unsigned offset
= !t
->even_vtotal
&& t
->interlaced
;
373 unsigned hfp
= t
->hborder
+ t
->hfp
;
374 unsigned hbp
= t
->hborder
+ t
->hbp
;
375 unsigned vfp
= t
->vborder
+ t
->vfp
;
376 unsigned vbp
= t
->vborder
+ t
->vbp
;
377 printf("%*stimings %llu %d %d %d %u %u %u\n",
379 (unsigned long long)(1000000000.0 / (double)(t
->pixclk_khz
) + 0.5),
380 hbp
, hfp
, mult
* vbp
, mult
* vfp
+ offset
, t
->hsync
, mult
* t
->vsync
);
382 printf("%*slaced true\n", indent
+ 8, "");
383 if (t
->pos_pol_hsync
)
384 printf("%*shsync high\n", indent
+ 8, "");
385 if (t
->pos_pol_vsync
)
386 printf("%*svsync high\n", indent
+ 8, "");
387 printf("%*sendmode\n", indent
, "");
390 static void print_v4l2_timing(const struct timings
*t
,
391 double refresh
, const char *type
)
393 printf("\t#define V4L2_DV_BT_%uX%u%c%u_%02u { \\\n",
394 t
->hact
, t
->vact
, t
->interlaced
? 'I' : 'P',
395 (unsigned)refresh
, (unsigned)(0.5 + 100.0 * (refresh
- (unsigned)refresh
)));
396 printf("\t\t.type = V4L2_DV_BT_656_1120, \\\n");
397 printf("\t\tV4L2_INIT_BT_TIMINGS(%u, %u, %u, ",
398 t
->hact
, t
->vact
, t
->interlaced
);
399 if (!t
->pos_pol_hsync
&& !t
->pos_pol_vsync
)
401 else if (t
->pos_pol_hsync
&& t
->pos_pol_vsync
)
402 printf("\\\n\t\t\tV4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \\\n");
403 else if (t
->pos_pol_hsync
)
404 printf("V4L2_DV_HSYNC_POS_POL, \\\n");
406 printf("V4L2_DV_VSYNC_POS_POL, \\\n");
407 unsigned hfp
= t
->hborder
+ t
->hfp
;
408 unsigned hbp
= t
->hborder
+ t
->hbp
;
409 unsigned vfp
= t
->vborder
+ t
->vfp
;
410 unsigned vbp
= t
->vborder
+ t
->vbp
;
411 printf("\t\t\t%lluULL, %d, %u, %d, %u, %u, %d, %u, %u, %d, \\\n",
412 t
->pixclk_khz
* 1000ULL, hfp
, t
->hsync
, hbp
,
414 t
->interlaced
? vfp
: 0,
415 t
->interlaced
? t
->vsync
: 0,
416 t
->interlaced
? vbp
+ !t
->even_vtotal
: 0);
419 unsigned num_flags
= 0;
421 unsigned hdmi_vic
= 0;
422 const char *std
= "0";
424 if (t
->interlaced
&& !t
->even_vtotal
)
425 or_str(flags
, "V4L2_DV_FL_HALF_LINE", num_flags
);
426 if (!memcmp(type
, "VIC", 3)) {
427 or_str(flags
, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags
);
428 or_str(flags
, "V4L2_DV_FL_IS_CE_VIDEO", num_flags
);
429 vic
= strtoul(type
+ 4, 0, 0);
431 if (!memcmp(type
, "HDMI VIC", 8)) {
432 or_str(flags
, "V4L2_DV_FL_HAS_HDMI_VIC", num_flags
);
433 or_str(flags
, "V4L2_DV_FL_IS_CE_VIDEO", num_flags
);
434 hdmi_vic
= strtoul(type
+ 9, 0, 0);
435 vic
= hdmi_vic_to_vic(hdmi_vic
);
437 or_str(flags
, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags
);
439 if (vic
&& (fmod(refresh
, 6)) == 0.0)
440 or_str(flags
, "V4L2_DV_FL_CAN_REDUCE_FPS", num_flags
);
442 or_str(flags
, "V4L2_DV_FL_REDUCED_BLANKING", num_flags
);
443 if (t
->hratio
&& t
->vratio
)
444 or_str(flags
, "V4L2_DV_FL_HAS_PICTURE_ASPECT", num_flags
);
446 if (!memcmp(type
, "VIC", 3) || !memcmp(type
, "HDMI VIC", 8))
447 std
= "V4L2_DV_BT_STD_CEA861";
448 else if (!memcmp(type
, "DMT", 3))
449 std
= "V4L2_DV_BT_STD_DMT";
450 else if (!memcmp(type
, "CVT", 3))
451 std
= "V4L2_DV_BT_STD_CVT";
452 else if (!memcmp(type
, "GTF", 3))
453 std
= "V4L2_DV_BT_STD_GTF";
454 printf("\t\t\t%s, \\\n", std
);
455 printf("\t\t\t%s, \\\n", flags
.empty() ? "0" : flags
.c_str());
456 printf("\t\t\t{ %u, %u }, %u, %u) \\\n",
457 t
->hratio
, t
->vratio
, vic
, hdmi_vic
);
461 static void print_detailed_timing(unsigned indent
, const struct timings
*t
)
463 printf("%*sHfront %4d Hsync %3u Hback %3d Hpol %s",
465 t
->hfp
, t
->hsync
, t
->hbp
, t
->pos_pol_hsync
? "P" : "N");
467 printf(" Hborder %u", t
->hborder
);
470 printf("%*sVfront %4u Vsync %3u Vback %3d",
471 indent
, "", t
->vfp
, t
->vsync
, t
->vbp
);
472 if (!t
->no_pol_vsync
)
473 printf(" Vpol %s", t
->pos_pol_vsync
? "P" : "N");
475 printf(" Vborder %u", t
->vborder
);
476 if (t
->even_vtotal
) {
477 printf(" Both Fields");
478 } else if (t
->interlaced
) {
479 printf(" Vfront +0.5 Odd Field\n");
480 printf("%*sVfront %4d Vsync %3u Vback %3d",
481 indent
, "", t
->vfp
, t
->vsync
, t
->vbp
);
482 if (!t
->no_pol_vsync
)
483 printf(" Vpol %s", t
->pos_pol_vsync
? "P" : "N");
485 printf(" Vborder %u", t
->vborder
);
486 printf(" Vback +0.5 Even Field");
491 bool edid_state::print_timings(const char *prefix
, const struct timings
*t
,
492 const char *type
, const char *flags
,
493 bool detailed
, bool do_checks
)
498 fail("Unknown video timings.\n");
502 if (detailed
&& options
[OptShortTimings
])
504 if (options
[OptLongTimings
])
507 unsigned vact
= t
->vact
;
508 unsigned hbl
= t
->hfp
+ t
->hsync
+ t
->hbp
+ 2 * t
->hborder
;
509 unsigned vbl
= t
->vfp
+ t
->vsync
+ t
->vbp
+ 2 * t
->vborder
;
510 unsigned htotal
= t
->hact
+ hbl
;
511 double hor_freq_khz
= htotal
? (double)t
->pixclk_khz
/ htotal
: 0;
516 double out_hor_freq_khz
= hor_freq_khz
;
520 double vtotal
= vact
+ vbl
;
524 if (!t
->hact
|| !hbl
|| !t
->hfp
|| !t
->hsync
||
525 !vact
|| !vbl
|| (!t
->vfp
&& !t
->interlaced
&& !t
->even_vtotal
) || !t
->vsync
) {
527 fail("0 values in the video timing:\n"
528 " Horizontal Active/Blanking %u/%u\n"
529 " Horizontal Frontporch/Sync Width %u/%u\n"
530 " Vertical Active/Blanking %u/%u\n"
531 " Vertical Frontporch/Sync Width %u/%u\n",
532 t
->hact
, hbl
, t
->hfp
, t
->hsync
, vact
, vbl
, t
->vfp
, t
->vsync
);
537 vtotal
= vact
+ t
->vfp
+ t
->vsync
+ t
->vbp
;
538 else if (t
->interlaced
)
539 vtotal
= vact
+ t
->vfp
+ t
->vsync
+ t
->vbp
+ 0.5;
541 double refresh
= (double)t
->pixclk_khz
* 1000.0 / (htotal
* vtotal
);
544 unsigned rb
= t
->rb
& ~RB_ALT
;
546 bool alt
= t
->rb
& RB_ALT
;
549 s
+= std::string("v2") + (alt
? ",video-optimized" : "");
550 else if (rb
== RB_CVT_V3
)
551 s
+= std::string("v3") + (alt
? ",h-blank-160" : "");
554 if (t
->hsize_mm
|| t
->vsize_mm
)
555 add_str(s
, std::to_string(t
->hsize_mm
) + " mm x " + std::to_string(t
->vsize_mm
) + " mm");
556 if (t
->hsize_mm
> dtd_max_hsize_mm
)
557 dtd_max_hsize_mm
= t
->hsize_mm
;
558 if (t
->vsize_mm
> dtd_max_vsize_mm
)
559 dtd_max_vsize_mm
= t
->vsize_mm
;
562 unsigned out_pixclk_khz
= t
->pixclk_khz
;
563 unsigned pixclk_khz
= t
->pixclk_khz
/ (t
->ycbcr420
? 2 : 1);
567 sprintf(buf
, "%u%s", t
->vact
, t
->interlaced
? "i" : "");
568 printf("%s%s: %5ux%-5s %7.3f Hz %3u:%-3u %7.3f kHz %8.3f MHz%s\n",
572 t
->hratio
, t
->vratio
,
574 out_pixclk_khz
/ 1000.0,
577 unsigned len
= strlen(prefix
) + 2;
579 if (!t
->ycbcr420
&& detailed
&& options
[OptXModeLineTimings
])
580 print_modeline(len
, t
, refresh
);
581 else if (!t
->ycbcr420
&& detailed
&& options
[OptFBModeTimings
])
582 print_fbmode(len
, t
, refresh
, hor_freq_khz
);
583 else if (!t
->ycbcr420
&& detailed
&& options
[OptV4L2Timings
])
584 print_v4l2_timing(t
, refresh
, type
);
586 print_detailed_timing(len
+ strlen(type
) + 6, t
);
591 if (!memcmp(type
, "DTD", 3)) {
593 const timings
*vic_t
= cta_close_match_to_vic(*t
, vic
);
596 warn("DTD is similar but not identical to VIC %u.\n", vic
);
598 const timings
*dmt_t
= close_match_to_dmt(*t
, dmt
);
600 warn("DTD is similar but not identical to DMT 0x%02x.\n", dmt
);
604 min_vert_freq_hz
= min(min_vert_freq_hz
, refresh
);
605 max_vert_freq_hz
= max(max_vert_freq_hz
, refresh
);
608 min_hor_freq_hz
= min(min_hor_freq_hz
, hor_freq_khz
* 1000.0);
609 max_hor_freq_hz
= max(max_hor_freq_hz
, hor_freq_khz
* 1000.0);
610 max_pixclk_khz
= max(max_pixclk_khz
, pixclk_khz
);
611 if (t
->pos_pol_hsync
&& !t
->pos_pol_vsync
&& t
->vsync
== 3)
612 base
.max_pos_neg_hor_freq_khz
= hor_freq_khz
;
615 if (t
->ycbcr420
&& t
->pixclk_khz
< 590000)
616 warn_once("Some YCbCr 4:2:0 timings are invalid for HDMI (which requires an RGB timings pixel rate >= 590 MHz).\n");
618 fail("0 or negative horizontal front porch.\n");
620 fail("0 or negative horizontal back porch.\n");
622 fail("0 or negative vertical back porch.\n");
623 if (!base
.max_display_width_mm
&& !base
.max_display_height_mm
) {
625 } else if (!t
->hsize_mm
&& !t
->vsize_mm
) {
627 } else if (t
->hsize_mm
> base
.max_display_width_mm
+ 9 ||
628 t
->vsize_mm
> base
.max_display_height_mm
+ 9) {
629 fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
630 t
->hsize_mm
, t
->vsize_mm
, base
.max_display_width_mm
, base
.max_display_height_mm
);
631 } else if (t
->hsize_mm
< base
.max_display_width_mm
- 9 &&
632 t
->vsize_mm
< base
.max_display_height_mm
- 9) {
633 fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
634 t
->hsize_mm
, t
->vsize_mm
, base
.max_display_width_mm
, base
.max_display_height_mm
);
639 std::string
containerid2s(const unsigned char *x
)
643 sprintf(buf
, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
644 x
[0], x
[1], x
[2], x
[3],
648 x
[10], x
[11], x
[12], x
[13], x
[14], x
[15]);
652 std::string
utohex(unsigned char x
)
656 sprintf(buf
, "0x%02hhx", x
);
660 const char *oui_name(unsigned oui
, unsigned *ouinum
)
662 unsigned ouinumscratch
;
663 if (!ouinum
) ouinum
= &ouinumscratch
;
666 #define oneoui(c,k,n) case c: *ouinum = kOUI_##k; name = n; break;
668 default: *ouinum
= 0; name
= NULL
; break;
673 void edid_state::data_block_oui(std::string block_name
, const unsigned char *x
,
674 unsigned length
, unsigned *ouinum
, bool ignorezeros
, bool do_ascii
, bool big_endian
)
679 const char *ouiname
= NULL
;
680 bool matched_reverse
= false;
681 bool matched_ascii
= false;
682 bool valid_ascii
= false;
685 oui
= ((length
> 0 ? x
[0] : 0) << 16) + ((length
> 1 ? x
[1] : 0) << 8) + (length
> 2 ? x
[2] : 0);
687 oui
= ((length
> 2 ? x
[2] : 0) << 16) + ((length
> 1 ? x
[1] : 0) << 8) + (length
> 0 ? x
[0] : 0);
691 sprintf(ascii
, "?"); // some characters are null
692 if (ouinum
) *ouinum
= 0; // doesn't match a known OUI
694 valid_ascii
= (x
[0] >= 'A' && x
[1] >= 'A' && x
[2] >= 'A' && x
[0] <= 'Z' && x
[1] <= 'Z' && x
[2] <= 'Z');
695 sprintf(ascii
, "%c%c%c", x
[0], x
[1], x
[2]);
697 ouiname
= oui_name(oui
, ouinum
);
699 big_endian
= !big_endian
;
700 unsigned reversedoui
= ((oui
& 0xff) << 16) + (oui
& 0x00ff00) + (oui
>> 16);
701 ouiname
= oui_name(reversedoui
, ouinum
);
705 matched_reverse
= true;
706 } else if (do_ascii
&& valid_ascii
) {
707 unsigned asciioui
= (x
[0] << 24) + (x
[1] << 16) + (x
[2] << 8);
708 ouiname
= oui_name(asciioui
, ouinum
);
711 matched_ascii
= do_ascii
&& valid_ascii
&& ouiname
!= NULL
;
717 name
= block_name
+ " (" + ouiname
+ ")" + ", PNP ID '" + ascii
+ "'";
719 name
= block_name
+ " (" + ouiname
+ ")" + ", OUI " + buf
;
720 } else if (do_ascii
&& valid_ascii
) {
721 name
= block_name
+ ", PNP ID '" + ascii
+ "'";
723 name
= block_name
+ ", OUI " + buf
;
725 // assign string to data_block before outputting errors
728 if (oui
|| !ignorezeros
) {
729 printf(" %s:\n", data_block
.c_str());
731 fail("Data block length (%d) is not enough to contain an OUI.\n", length
);
733 if (do_ascii
&& !valid_ascii
)
734 warn("Expected PNP ID but found OUI.\n");
736 fail("Endian-ness (%s) of OUI is different than expected (%s).\n", big_endian
? "be" : "le", big_endian
? "le" : "be");
740 warn("Unknown OUI %s (possible PNP %s).\n", buf
.c_str(), ascii
);
742 warn("Unknown OUI %s.\n", buf
.c_str());
747 std::string
ouitohex(unsigned oui
)
751 sprintf(buf
, "%02X-%02X-%02X", (oui
>> 16) & 0xff, (oui
>> 8) & 0xff, oui
& 0xff);
755 bool memchk(const unsigned char *x
, unsigned len
, unsigned char v
)
757 for (unsigned i
= 0; i
< len
; i
++)
763 void hex_block(const char *prefix
, const unsigned char *x
,
764 unsigned length
, bool show_ascii
, unsigned step
)
768 for (i
= 0; i
< length
; i
+= step
) {
769 unsigned len
= min(step
, length
- i
);
771 printf("%s", prefix
);
772 for (j
= 0; j
< len
; j
++)
773 printf("%s%02x", j
? " " : "", x
[i
+ j
]);
776 for (j
= len
; j
< step
; j
++)
779 for (j
= 0; j
< len
; j
++)
780 printf("%c", x
[i
+ j
] >= ' ' && x
[i
+ j
] <= '~' ? x
[i
+ j
] : '.');
787 static bool edid_add_byte(const char *s
, bool two_digits
= true)
791 if (state
.edid_size
== sizeof(edid
))
794 buf
[1] = two_digits
? s
[1] : 0;
796 edid
[state
.edid_size
++] = strtoul(buf
, NULL
, 16);
800 static bool extract_edid_quantumdata(const char *start
)
802 /* Parse QuantumData 980 EDID files */
804 start
= strstr(start
, ">");
808 for (unsigned i
= 0; start
[i
] && start
[i
+ 1] && i
< 256; i
+= 2)
809 if (!edid_add_byte(start
+ i
))
811 start
= strstr(start
, "<BLOCK");
813 return state
.edid_size
;
816 static const char *ignore_chars
= ",:;";
818 static bool extract_edid_hex(const char *s
, bool require_two_digits
= true)
821 if (isspace(*s
) || strchr(ignore_chars
, *s
))
824 if (*s
== '0' && tolower(s
[1]) == 'x') {
829 /* Read one or two hex digits from the log */
830 if (!isxdigit(s
[0])) {
831 if (state
.edid_size
&& state
.edid_size
% 128 == 0)
835 if (require_two_digits
&& !isxdigit(s
[1])) {
836 odd_hex_digits
= true;
839 if (!edid_add_byte(s
, isxdigit(s
[1])))
844 return state
.edid_size
;
847 static bool extract_edid_xrandr(const char *start
)
849 static const char indentation1
[] = " ";
850 static const char indentation2
[] = "\t\t";
851 /* Used to detect that we've gone past the EDID property */
852 static const char half_indentation1
[] = " ";
853 static const char half_indentation2
[] = "\t";
854 const char *indentation
;
860 /* Get the next start of the line of EDID hex, assuming spaces for indentation */
861 s
= strstr(start
, indentation
= indentation1
);
862 /* Did we skip the start of another property? */
863 if (s
&& s
> strstr(start
, half_indentation1
))
866 /* If we failed, retry assuming tabs for indentation */
868 s
= strstr(start
, indentation
= indentation2
);
869 /* Did we skip the start of another property? */
870 if (s
&& s
> strstr(start
, half_indentation2
))
877 start
= s
+ strlen(indentation
);
879 for (j
= 0; j
< 16; j
++, start
+= 2) {
880 /* Read a %02x from the log */
881 if (!isxdigit(start
[0]) || !isxdigit(start
[1])) {
886 if (!edid_add_byte(start
))
890 return state
.edid_size
;
893 static bool extract_edid_xorg(const char *start
)
895 bool find_first_num
= true;
897 for (; *start
; start
++) {
898 if (find_first_num
) {
901 /* skip ahead to the : */
902 s
= strstr(start
, ": \t");
904 s
= strstr(start
, ": ");
908 /* and find the first number */
909 while (!isxdigit(start
[1]))
911 find_first_num
= false;
914 /* Read a %02x from the log */
915 if (!isxdigit(*start
)) {
916 find_first_num
= true;
919 if (!edid_add_byte(start
))
924 return state
.edid_size
;
927 static bool extract_edid(int fd
, FILE *error
)
929 std::vector
<char> edid_data
;
930 char buf
[EDID_PAGE_SIZE
];
933 ssize_t i
= read(fd
, buf
, sizeof(buf
));
939 edid_data
.insert(edid_data
.end(), buf
, buf
+ i
);
942 if (edid_data
.empty()) {
947 const char *data
= &edid_data
[0];
950 /* Look for edid-decode output */
951 start
= strstr(data
, "EDID (hex):");
953 start
= strstr(data
, "edid-decode (hex):");
955 return extract_edid_hex(strchr(start
, ':'));
957 /* Look for C-array */
958 start
= strstr(data
, "unsigned char edid[] = {");
960 return extract_edid_hex(strchr(start
, '{') + 1, false);
962 /* Look for QuantumData EDID output */
963 start
= strstr(data
, "<BLOCK");
965 return extract_edid_quantumdata(start
);
967 /* Look for xrandr --verbose output (lines of 16 hex bytes) */
968 start
= strstr(data
, "EDID_DATA:");
970 start
= strstr(data
, "EDID:");
972 return extract_edid_xrandr(start
);
974 /* Look for an EDID in an Xorg.0.log file */
975 start
= strstr(data
, "EDID (in hex):");
977 start
= strstr(start
, "(II)");
979 return extract_edid_xorg(start
);
983 /* Is the EDID provided in hex? */
984 for (i
= 0; i
< 32 && (isspace(data
[i
]) || strchr(ignore_chars
, data
[i
]) ||
985 tolower(data
[i
]) == 'x' || isxdigit(data
[i
])); i
++);
988 return extract_edid_hex(data
);
991 if (edid_data
.size() > sizeof(edid
)) {
992 fprintf(error
, "Binary EDID length %zu is greater than %zu.\n",
993 edid_data
.size(), sizeof(edid
));
996 memcpy(edid
, data
, edid_data
.size());
997 state
.edid_size
= edid_data
.size();
1001 static unsigned char crc_calc(const unsigned char *b
)
1003 unsigned char sum
= 0;
1006 for (i
= 0; i
< 127; i
++)
1011 static int crc_ok(const unsigned char *b
)
1013 return crc_calc(b
) == b
[127];
1016 static void hexdumpedid(FILE *f
, const unsigned char *edid
, unsigned size
)
1020 for (b
= 0; b
< size
/ 128; b
++) {
1021 const unsigned char *buf
= edid
+ 128 * b
;
1025 for (i
= 0; i
< 128; i
+= 0x10) {
1026 fprintf(f
, "%02x", buf
[i
]);
1027 for (j
= 1; j
< 0x10; j
++) {
1028 fprintf(f
, " %02x", buf
[i
+ j
]);
1033 fprintf(f
, "Block %u has a checksum error (should be 0x%02x).\n",
1038 static void carraydumpedid(FILE *f
, const unsigned char *edid
, unsigned size
)
1042 fprintf(f
, "const unsigned char edid[] = {\n");
1043 for (b
= 0; b
< size
/ 128; b
++) {
1044 const unsigned char *buf
= edid
+ 128 * b
;
1048 for (i
= 0; i
< 128; i
+= 8) {
1049 fprintf(f
, "\t0x%02x,", buf
[i
]);
1050 for (j
= 1; j
< 8; j
++) {
1051 fprintf(f
, " 0x%02x,", buf
[i
+ j
]);
1056 fprintf(f
, "\t/* Block %u has a checksum error (should be 0x%02x). */\n",
1062 // This format can be read by the QuantumData EDID editor
1063 static void xmldumpedid(FILE *f
, const unsigned char *edid
, unsigned size
)
1065 fprintf(f
, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
1066 fprintf(f
, "<DATAOBJ>\n");
1067 fprintf(f
, " <HEADER TYPE=\"DID\" VERSION=\"1.0\"/>\n");
1068 fprintf(f
, " <DATA>\n");
1069 for (unsigned b
= 0; b
< size
/ 128; b
++) {
1070 const unsigned char *buf
= edid
+ 128 * b
;
1072 fprintf(f
, " <BLOCK%u>", b
);
1073 for (unsigned i
= 0; i
< 128; i
++)
1074 fprintf(f
, "%02X", buf
[i
]);
1075 fprintf(f
, "</BLOCK%u>\n", b
);
1077 fprintf(f
, " </DATA>\n");
1078 fprintf(f
, "</DATAOBJ>\n");
1082 static int edid_to_file(const char *to_file
, enum output_format out_fmt
)
1086 if (!strcmp(to_file
, "-")) {
1089 } else if ((out
= fopen(to_file
, "w")) == NULL
) {
1093 if (out_fmt
== OUT_FMT_DEFAULT
)
1094 out_fmt
= out
== stdout
? OUT_FMT_HEX
: OUT_FMT_RAW
;
1099 hexdumpedid(out
, edid
, state
.edid_size
);
1102 fwrite(edid
, state
.edid_size
, 1, out
);
1104 case OUT_FMT_CARRAY
:
1105 carraydumpedid(out
, edid
, state
.edid_size
);
1108 xmldumpedid(out
, edid
, state
.edid_size
);
1117 static int edid_from_file(const char *from_file
, FILE *error
)
1120 // Windows compatibility
1121 int flags
= O_RDONLY
| O_BINARY
;
1123 int flags
= O_RDONLY
;
1127 if (!strcmp(from_file
, "-")) {
1128 from_file
= "stdin";
1130 } else if ((fd
= open(from_file
, flags
)) == -1) {
1135 odd_hex_digits
= false;
1136 if (!extract_edid(fd
, error
)) {
1137 if (!state
.edid_size
) {
1138 fprintf(error
, "EDID of '%s' was empty.\n", from_file
);
1141 fprintf(error
, "EDID extract of '%s' failed: ", from_file
);
1143 fprintf(error
, "odd number of hexadecimal digits.\n");
1145 fprintf(error
, "unknown format.\n");
1148 if (state
.edid_size
% EDID_PAGE_SIZE
) {
1149 fprintf(error
, "EDID length %u is not a multiple of %u.\n",
1150 state
.edid_size
, EDID_PAGE_SIZE
);
1153 state
.num_blocks
= state
.edid_size
/ EDID_PAGE_SIZE
;
1157 if (memcmp(edid
, "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", 8)) {
1158 fprintf(error
, "No EDID header found in '%s'.\n", from_file
);
1164 /* generic extension code */
1166 std::string
block_name(unsigned char block
)
1171 case 0x00: return "Base EDID";
1172 case 0x02: return "CTA-861 Extension Block";
1173 case 0x10: return "Video Timing Extension Block";
1174 case 0x20: return "EDID 2.0 Extension Block";
1175 case 0x40: return "Display Information Extension Block";
1176 case 0x50: return "Localized String Extension Block";
1177 case 0x60: return "Microdisplay Interface Extension Block";
1178 case 0x70: return "DisplayID Extension Block";
1179 case 0xf0: return "Block Map Extension Block";
1180 case 0xff: return "Manufacturer-Specific Extension Block";
1182 sprintf(buf
, " 0x%02x", block
);
1183 return std::string("Unknown EDID Extension Block") + buf
;
1187 void edid_state::parse_block_map(const unsigned char *x
)
1189 unsigned last_valid_block_tag
= 0;
1190 bool fail_once
= false;
1191 unsigned offset
= 1;
1195 block_map
.saw_block_1
= true;
1196 else if (!block_map
.saw_block_1
)
1197 fail("No EDID Block Map Extension found in block 1.\n");
1198 else if (block_nr
== 128)
1199 block_map
.saw_block_128
= true;
1204 for (i
= 1; i
< 127; i
++) {
1205 unsigned block
= offset
+ i
;
1208 last_valid_block_tag
++;
1209 if (i
!= last_valid_block_tag
&& !fail_once
) {
1210 fail("Valid block tags are not consecutive.\n");
1213 printf(" Block %3u: %s\n", block
, block_name(x
[i
]).c_str());
1214 if (block
>= num_blocks
) {
1216 fail("Invalid block number %u.\n", block
);
1218 } else if (x
[i
] != edid
[block
* EDID_PAGE_SIZE
]) {
1219 fail("Block %u tag mismatch: expected 0x%02x, but got 0x%02x.\n",
1220 block
, edid
[block
* EDID_PAGE_SIZE
], x
[i
]);
1222 } else if (block
< num_blocks
) {
1223 fail("Block %u tag mismatch: expected 0x%02x, but got 0x00.\n",
1224 block
, edid
[block
* EDID_PAGE_SIZE
]);
1229 void edid_state::preparse_extension(const unsigned char *x
)
1234 preparse_cta_block(x
);
1238 preparse_displayid_block(x
);
1243 void edid_state::parse_extension(const unsigned char *x
)
1245 block
= block_name(x
[0]);
1249 if (block_nr
&& x
[0] == 0)
1250 block
= "Unknown EDID Extension Block 0x00";
1251 printf("Block %u, %s:\n", block_nr
, block
.c_str());
1258 parse_vtb_ext_block(x
);
1261 fail("Deprecated extension block for EDID 2.0, do not use.\n");
1264 parse_di_ext_block(x
);
1267 parse_ls_ext_block(x
);
1270 parse_displayid_block(x
);
1274 if (block_nr
!= 1 && block_nr
!= 128)
1275 fail("Must be used in block 1 and 128.\n");
1278 hex_block(" ", x
, EDID_PAGE_SIZE
);
1279 fail("Unknown Extension Block.\n");
1284 do_checksum("", x
, EDID_PAGE_SIZE
);
1287 int edid_state::parse_edid()
1289 hide_serial_numbers
= options
[OptHideSerialNumbers
];
1291 for (unsigned i
= 1; i
< num_blocks
; i
++)
1292 preparse_extension(edid
+ i
* EDID_PAGE_SIZE
);
1294 if (options
[OptPhysicalAddress
]) {
1295 printf("%x.%x.%x.%x\n",
1296 (cta
.preparsed_phys_addr
>> 12) & 0xf,
1297 (cta
.preparsed_phys_addr
>> 8) & 0xf,
1298 (cta
.preparsed_phys_addr
>> 4) & 0xf,
1299 cta
.preparsed_phys_addr
& 0xf);
1303 if (!options
[OptSkipHexDump
]) {
1304 printf("edid-decode (hex):\n\n");
1305 for (unsigned i
= 0; i
< num_blocks
; i
++) {
1306 hex_block("", edid
+ i
* EDID_PAGE_SIZE
, EDID_PAGE_SIZE
, false);
1307 if (i
== num_blocks
- 1 && options
[OptOnlyHexDump
])
1311 printf("----------------\n\n");
1314 block
= block_name(0x00);
1315 printf("Block %u, %s:\n", block_nr
, block
.c_str());
1316 parse_base_block(edid
);
1318 for (unsigned i
= 1; i
< num_blocks
; i
++) {
1320 printf("\n----------------\n");
1321 parse_extension(edid
+ i
* EDID_PAGE_SIZE
);
1325 block_nr
= EDID_MAX_BLOCKS
;
1330 if (options
[OptPreferredTimings
] && base
.preferred_timing
.is_valid()) {
1331 printf("\n----------------\n");
1332 printf("\nPreferred Video Timing if only Block 0 is parsed:\n");
1333 print_timings(" ", base
.preferred_timing
, true, false);
1336 if (options
[OptNativeTimings
] &&
1337 base
.preferred_timing
.is_valid() && base
.preferred_is_also_native
) {
1338 printf("\n----------------\n");
1339 printf("\nNative Video Timing if only Block 0 is parsed:\n");
1340 print_timings(" ", base
.preferred_timing
, true, false);
1343 if (options
[OptPreferredTimings
] && !cta
.preferred_timings
.empty()) {
1344 printf("\n----------------\n");
1345 printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
1346 cta
.preferred_timings
.size() > 1 ? "s" : "");
1347 for (vec_timings_ext::iterator iter
= cta
.preferred_timings
.begin();
1348 iter
!= cta
.preferred_timings
.end(); ++iter
)
1349 print_timings(" ", *iter
, true, false);
1352 if (options
[OptNativeTimings
] && !cta
.native_timings
.empty()) {
1353 printf("\n----------------\n");
1354 printf("\nNative Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
1355 cta
.native_timings
.size() > 1 ? "s" : "");
1356 for (vec_timings_ext::iterator iter
= cta
.native_timings
.begin();
1357 iter
!= cta
.native_timings
.end(); ++iter
)
1358 print_timings(" ", *iter
, true, false);
1361 if (options
[OptPreferredTimings
] && !dispid
.preferred_timings
.empty()) {
1362 printf("\n----------------\n");
1363 printf("\nPreferred Video Timing%s if Block 0 and DisplayID Blocks are parsed:\n",
1364 dispid
.preferred_timings
.size() > 1 ? "s" : "");
1365 for (vec_timings_ext::iterator iter
= dispid
.preferred_timings
.begin();
1366 iter
!= dispid
.preferred_timings
.end(); ++iter
)
1367 print_timings(" ", *iter
, true, false);
1370 if (!options
[OptCheck
] && !options
[OptCheckInline
])
1377 check_displayid_blocks();
1379 printf("\n----------------\n");
1381 if (!options
[OptSkipSHA
] && strlen(STRING(SHA
))) {
1382 printf("\nedid-decode SHA: %s %s\n", STRING(SHA
), STRING(DATE
));
1385 if (options
[OptCheck
]) {
1391 printf("\nEDID conformity: %s\n", failures
? "FAIL" : "PASS");
1392 return failures
? -2 : 0;
1407 static int parse_cvt_subopt(char **subopt_str
, double *value
)
1412 static const char * const subopt_list
[] = {
1425 opt
= getsubopt(subopt_str
, (char* const*) subopt_list
, &opt_str
);
1428 fprintf(stderr
, "Invalid suboptions specified.\n");
1430 std::exit(EXIT_FAILURE
);
1432 if (opt_str
== nullptr && opt
!= CVT_INTERLACED
&& opt
!= CVT_ALT
&&
1433 opt
!= CVT_OVERSCAN
) {
1434 fprintf(stderr
, "No value given to suboption <%s>.\n",
1437 std::exit(EXIT_FAILURE
);
1441 *value
= strtod(opt_str
, nullptr);
1445 static void parse_cvt(char *optarg
)
1447 unsigned w
= 0, h
= 0;
1449 unsigned rb
= RB_NONE
;
1450 unsigned rb_h_blank
= 0;
1451 unsigned rb_add_v_blank
= 0;
1452 bool interlaced
= false;
1454 bool overscan
= false;
1456 while (*optarg
!= '\0') {
1460 opt
= parse_cvt_subopt(&optarg
, &opt_val
);
1478 case CVT_INTERLACED
:
1479 interlaced
= opt_val
;
1484 case CVT_RB_H_BLANK
:
1485 rb_h_blank
= opt_val
;
1487 case CVT_RB_ADD_V_BLANK
:
1488 rb_add_v_blank
= opt_val
;
1495 if (!w
|| !h
|| !fps
) {
1496 fprintf(stderr
, "Missing width, height and/or fps.\n");
1498 std::exit(EXIT_FAILURE
);
1502 timings t
= state
.calc_cvt_mode(w
, h
, fps
, rb
, interlaced
, overscan
, alt
,
1503 rb_h_blank
, rb_add_v_blank
);
1504 state
.print_timings("", &t
, "CVT", "", true, false);
1507 struct gtf_parsed_data
{
1514 bool params_from_edid
;
1515 enum gtf_ip_parm ip_parm
;
1533 static int parse_gtf_subopt(char **subopt_str
, double *value
)
1538 static const char * const subopt_list
[] = {
1554 opt
= getsubopt(subopt_str
, (char * const *)subopt_list
, &opt_str
);
1557 fprintf(stderr
, "Invalid suboptions specified.\n");
1559 std::exit(EXIT_FAILURE
);
1561 if (opt_str
== nullptr && opt
!= GTF_INTERLACED
&& opt
!= GTF_OVERSCAN
&&
1562 opt
!= GTF_SECONDARY
) {
1563 fprintf(stderr
, "No value given to suboption <%s>.\n",
1566 std::exit(EXIT_FAILURE
);
1569 if (opt
== GTF_C2
|| opt
== GTF_J2
)
1570 *value
= round(2.0 * strtod(opt_str
, nullptr));
1572 *value
= strtod(opt_str
, nullptr);
1576 static void parse_gtf(char *optarg
, gtf_parsed_data
&data
)
1578 memset(&data
, 0, sizeof(data
));
1579 data
.params_from_edid
= true;
1585 while (*optarg
!= '\0') {
1589 opt
= parse_gtf_subopt(&optarg
, &opt_val
);
1593 data
.w
= round(opt_val
);
1596 data
.h
= round(opt_val
);
1599 data
.freq
= opt_val
;
1600 data
.ip_parm
= gtf_ip_vert_freq
;
1603 data
.freq
= opt_val
;
1604 data
.ip_parm
= gtf_ip_hor_freq
;
1607 data
.freq
= opt_val
;
1608 data
.ip_parm
= gtf_ip_clk_freq
;
1610 case GTF_INTERLACED
:
1611 data
.interlaced
= true;
1614 data
.overscan
= true;
1617 data
.secondary
= true;
1620 data
.C
= opt_val
/ 2.0;
1621 data
.params_from_edid
= false;
1624 data
.M
= round(opt_val
);
1625 data
.params_from_edid
= false;
1628 data
.K
= round(opt_val
);
1629 data
.params_from_edid
= false;
1632 data
.J
= opt_val
/ 2.0;
1633 data
.params_from_edid
= false;
1640 if (!data
.w
|| !data
.h
) {
1641 fprintf(stderr
, "Missing width and/or height.\n");
1643 std::exit(EXIT_FAILURE
);
1646 fprintf(stderr
, "One of fps, horfreq or pixclk must be given.\n");
1648 std::exit(EXIT_FAILURE
);
1650 if (!data
.secondary
)
1651 data
.params_from_edid
= false;
1652 if (data
.interlaced
&& data
.ip_parm
== gtf_ip_vert_freq
)
1656 static void show_gtf(gtf_parsed_data
&data
)
1660 t
= state
.calc_gtf_mode(data
.w
, data
.h
, data
.freq
, data
.interlaced
,
1661 data
.ip_parm
, data
.overscan
, data
.secondary
,
1662 data
.C
, data
.M
, data
.K
, data
.J
);
1664 state
.print_timings("", &t
, "GTF", "", true, false);
1667 int main(int argc
, char **argv
)
1669 char short_options
[26 * 2 * 2 + 1];
1670 enum output_format out_fmt
= OUT_FMT_DEFAULT
;
1671 gtf_parsed_data gtf_data
;
1675 int option_index
= 0;
1681 for (i
= 0; long_options
[i
].name
; i
++) {
1682 if (!isalpha(long_options
[i
].val
))
1684 short_options
[idx
++] = long_options
[i
].val
;
1685 if (long_options
[i
].has_arg
== required_argument
)
1686 short_options
[idx
++] = ':';
1688 short_options
[idx
] = 0;
1689 int ch
= getopt_long(argc
, argv
, short_options
,
1690 long_options
, &option_index
);
1699 case OptOutputFormat
:
1700 if (!strcmp(optarg
, "hex")) {
1701 out_fmt
= OUT_FMT_HEX
;
1702 } else if (!strcmp(optarg
, "raw")) {
1703 out_fmt
= OUT_FMT_RAW
;
1704 } else if (!strcmp(optarg
, "carray")) {
1705 out_fmt
= OUT_FMT_CARRAY
;
1706 } else if (!strcmp(optarg
, "xml")) {
1707 out_fmt
= OUT_FMT_XML
;
1714 unsigned char byte1
, byte2
= 0;
1717 byte1
= strtoul(optarg
, &endptr
, 0);
1719 byte2
= strtoul(endptr
+ 1, NULL
, 0);
1720 state
.print_standard_timing("", byte1
, byte2
, false, true);
1724 val
= strtoul(optarg
, NULL
, 0);
1725 t
= find_dmt_id(val
);
1727 sprintf(buf
, "DMT 0x%02x", val
);
1728 state
.print_timings("", t
, buf
, "", true, false);
1730 fprintf(stderr
, "Unknown DMT code 0x%02x.\n", val
);
1734 val
= strtoul(optarg
, NULL
, 0);
1735 t
= find_vic_id(val
);
1737 sprintf(buf
, "VIC %3u", val
);
1738 state
.print_timings("", t
, buf
, "", true, false);
1740 fprintf(stderr
, "Unknown VIC code %u.\n", val
);
1744 val
= strtoul(optarg
, NULL
, 0);
1745 t
= find_hdmi_vic_id(val
);
1747 sprintf(buf
, "HDMI VIC %u", val
);
1748 state
.print_timings("", t
, buf
, "", true, false);
1750 fprintf(stderr
, "Unknown HDMI VIC code %u.\n", val
);
1757 parse_gtf(optarg
, gtf_data
);
1760 fprintf(stderr
, "Option '%s' requires a value.\n",
1765 fprintf(stderr
, "Unknown argument '%s'.\n",
1771 if (optind
== argc
&& options
[OptVersion
]) {
1772 if (strlen(STRING(SHA
)))
1773 printf("edid-decode SHA: %s %s\n", STRING(SHA
), STRING(DATE
));
1775 printf("edid-decode SHA: not available\n");
1779 if (options
[OptListEstTimings
])
1780 state
.list_established_timings();
1781 if (options
[OptListDMTs
])
1783 if (options
[OptListVICs
])
1784 state
.cta_list_vics();
1785 if (options
[OptListHDMIVICs
])
1786 state
.cta_list_hdmi_vics();
1788 if (options
[OptListEstTimings
] || options
[OptListDMTs
] ||
1789 options
[OptListVICs
] || options
[OptListHDMIVICs
])
1792 if (options
[OptCVT
] || options
[OptDMT
] || options
[OptVIC
] ||
1793 options
[OptHDMIVIC
] || options
[OptSTD
])
1796 if (options
[OptGTF
] && (!gtf_data
.params_from_edid
|| optind
== argc
)) {
1802 ret
= edid_from_file("-", stdout
);
1804 ret
= edid_from_file(argv
[optind
], argv
[optind
+ 1] ? stderr
: stdout
);
1806 if (ret
&& options
[OptPhysicalAddress
]) {
1807 printf("f.f.f.f\n");
1810 if (optind
< argc
- 1)
1811 return ret
? ret
: edid_to_file(argv
[optind
+ 1], out_fmt
);
1813 if (options
[OptGTF
]) {
1816 // Find the Secondary Curve
1817 state
.preparse_detailed_block(edid
+ 0x36);
1818 state
.preparse_detailed_block(edid
+ 0x48);
1819 state
.preparse_detailed_block(edid
+ 0x5a);
1820 state
.preparse_detailed_block(edid
+ 0x6c);
1822 t
= state
.calc_gtf_mode(gtf_data
.w
, gtf_data
.h
, gtf_data
.freq
,
1823 gtf_data
.interlaced
, gtf_data
.ip_parm
,
1825 unsigned hbl
= t
.hfp
+ t
.hsync
+ t
.hbp
;
1826 unsigned htotal
= t
.hact
+ hbl
;
1827 double hor_freq_khz
= htotal
? (double)t
.pixclk_khz
/ htotal
: 0;
1829 if (state
.base
.supports_sec_gtf
&&
1830 hor_freq_khz
>= state
.base
.sec_gtf_start_freq
) {
1831 t
= state
.calc_gtf_mode(gtf_data
.w
, gtf_data
.h
, gtf_data
.freq
,
1832 gtf_data
.interlaced
, gtf_data
.ip_parm
,
1833 gtf_data
.overscan
, true,
1834 state
.base
.C
, state
.base
.M
,
1835 state
.base
.K
, state
.base
.J
);
1839 state
.print_timings("", &t
, "GTF", "INVALID: Hfront <= 0", true, false);
1841 state
.print_timings("", &t
, "GTF", "", true, false);
1845 return ret
? ret
: state
.parse_edid();
1848 #ifdef __EMSCRIPTEN__
1850 * The surrounding JavaScript implementation will call this function
1851 * each time it wants to decode an EDID. So this should reset all the
1852 * state and start over.
1854 extern "C" int parse_edid(const char *input
)
1856 for (unsigned i
= 0; i
< EDID_MAX_BLOCKS
+ 1; i
++) {
1857 s_msgs
[i
][0].clear();
1858 s_msgs
[i
][1].clear();
1860 options
[OptCheck
] = 1;
1861 options
[OptPreferredTimings
] = 1;
1862 options
[OptNativeTimings
] = 1;
1863 state
= edid_state();
1864 int ret
= edid_from_file(input
, stderr
);
1865 return ret
? ret
: state
.parse_edid();