aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--calc-gtf-cvt.cpp24
-rw-r--r--data/apple-imac-retina-4k-21.5-inch-late-2015bin0 -> 256 bytes
-rw-r--r--data/vizio-m60c3-hdmi-onkyo-txnr555bin0 -> 256 bytes
-rw-r--r--edid-decode.132
-rw-r--r--edid-decode.cpp340
-rw-r--r--edid-decode.h42
-rw-r--r--oui.h20
-rw-r--r--parse-base-block.cpp7
-rw-r--r--parse-cta-block.cpp585
-rw-r--r--parse-displayid-block.cpp620
11 files changed, 932 insertions, 744 deletions
diff --git a/Makefile b/Makefile
index cf3d732..1843700 100644
--- a/Makefile
+++ b/Makefile
@@ -17,17 +17,17 @@ EMXX ?= em++
SOURCES = edid-decode.cpp parse-base-block.cpp parse-cta-block.cpp \
parse-displayid-block.cpp parse-ls-ext-block.cpp \
parse-di-ext-block.cpp parse-vtb-ext-block.cpp calc-gtf-cvt.cpp
-WARN_FLAGS = -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter
+WARN_FLAGS = -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Wimplicit-fallthrough
all: edid-decode
sha = -DSHA=$(shell if test -d .git ; then git rev-parse --short=12 HEAD ; fi)
date = -DDATE=$(shell if test -d .git ; then TZ=UTC git show --quiet --date='format-local:"%F %T"' --format='%cd'; fi)
-edid-decode: $(SOURCES) edid-decode.h Makefile
+edid-decode: $(SOURCES) edid-decode.h oui.h Makefile
$(CXX) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(WARN_FLAGS) -g $(sha) $(date) -o $@ $(SOURCES) -lm
-edid-decode.js: $(SOURCES) edid-decode.h Makefile
+edid-decode.js: $(SOURCES) edid-decode.h oui.h Makefile
$(EMXX) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(WARN_FLAGS) $(sha) $(date) -s EXPORTED_FUNCTIONS='["_parse_edid"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -o $@ $(SOURCES) -lm
clean:
diff --git a/calc-gtf-cvt.cpp b/calc-gtf-cvt.cpp
index 9cd6e5e..dc22ad5 100644
--- a/calc-gtf-cvt.cpp
+++ b/calc-gtf-cvt.cpp
@@ -132,7 +132,7 @@ void edid_state::edid_gtf_mode(unsigned refresh, struct timings &t)
timings edid_state::calc_cvt_mode(unsigned h_pixels, unsigned v_lines,
double ip_freq_rqd, unsigned rb, bool int_rqd,
bool margins_rqd, bool alt, unsigned rb_h_blank,
- double add_vert_time)
+ unsigned rb_v_blank, bool early_vsync_rqd)
{
timings t = {};
@@ -140,6 +140,9 @@ timings edid_state::calc_cvt_mode(unsigned h_pixels, unsigned v_lines,
t.vact = v_lines;
t.interlaced = int_rqd;
+ if (rb_v_blank < CVT_RB_MIN_VBLANK)
+ rb_v_blank = CVT_RB_MIN_VBLANK;
+
double cell_gran = rb == RB_CVT_V2 ? 1 : CELL_GRAN;
double h_pixels_rnd = floor(h_pixels / cell_gran) * cell_gran;
double v_lines_rnd = int_rqd ? floor(v_lines / 2.0) : v_lines;
@@ -153,7 +156,7 @@ timings edid_state::calc_cvt_mode(unsigned h_pixels, unsigned v_lines,
double h_blank = (rb == RB_CVT_V1 || (rb == RB_CVT_V3 && alt)) ? 160 : 80;
double rb_v_fporch = rb == RB_CVT_V1 ? 3 : 1;
double refresh_multiplier = (rb == RB_CVT_V2 && alt) ? 1000.0 / 1001.0 : 1;
- double rb_min_vblank = CVT_RB_MIN_VBLANK;
+ double rb_min_vblank = rb == RB_CVT_V3 ? rb_v_blank : CVT_RB_MIN_VBLANK;
double h_sync = 32;
double v_sync;
@@ -161,11 +164,6 @@ timings edid_state::calc_cvt_mode(unsigned h_pixels, unsigned v_lines,
double v_blank;
double v_sync_bp;
- if (rb == RB_CVT_V3 && add_vert_time) {
- if (add_vert_time + rb_min_vblank <= 1000000.0 / ip_freq_rqd / 4.0)
- rb_min_vblank += add_vert_time;
- }
-
if (rb == RB_CVT_V3 && rb_h_blank) {
h_blank = rb_h_blank & ~7;
if (h_blank < 80)
@@ -211,14 +209,16 @@ timings edid_state::calc_cvt_mode(unsigned h_pixels, unsigned v_lines,
double h_period_est = ((1000000.0 / v_field_rate_rqd) - rb_min_vblank) /
(v_lines_rnd + vert_margin * 2);
double vbi_lines = floor(rb_min_vblank / h_period_est) + 1;
- double rb_min_vbi = rb_v_fporch + v_sync +
- (rb == RB_CVT_V1 ? CVT_MIN_V_BPORCH : CVT_FIXED_V_BPORCH);
+ double rb_v_bporch = (rb == RB_CVT_V1 ? CVT_MIN_V_BPORCH : CVT_FIXED_V_BPORCH);
+ double rb_min_vbi = rb_v_fporch + v_sync + rb_v_bporch;
v_blank = vbi_lines < rb_min_vbi ? rb_min_vbi : vbi_lines;
double total_v_lines = v_blank + v_lines_rnd + vert_margin * 2 + interlace;
+ if (rb == RB_CVT_V3 && early_vsync_rqd)
+ rb_v_bporch = floor(vbi_lines / 2.0);
if (rb == RB_CVT_V1)
v_sync_bp = v_blank - rb_v_fporch;
else
- v_sync_bp = v_sync + CVT_FIXED_V_BPORCH;
+ v_sync_bp = v_sync + rb_v_bporch;
double total_pixels = h_blank + total_active_pixels;
double freq = v_field_rate_rqd * total_v_lines * total_pixels * refresh_multiplier;
if (rb == RB_CVT_V3)
@@ -249,13 +249,13 @@ timings edid_state::calc_cvt_mode(unsigned h_pixels, unsigned v_lines,
}
void edid_state::edid_cvt_mode(unsigned refresh, struct timings &t, unsigned rb_h_blank,
- double add_vert_time)
+ unsigned rb_v_blank, bool early_vsync_rqd)
{
unsigned hratio = t.hratio;
unsigned vratio = t.vratio;
t = calc_cvt_mode(t.hact, t.vact, refresh, t.rb & ~RB_ALT, t.interlaced,
- false, t.rb & RB_ALT, rb_h_blank, add_vert_time);
+ false, t.rb & RB_ALT, rb_h_blank, rb_v_blank, early_vsync_rqd);
t.hratio = hratio;
t.vratio = vratio;
}
diff --git a/data/apple-imac-retina-4k-21.5-inch-late-2015 b/data/apple-imac-retina-4k-21.5-inch-late-2015
new file mode 100644
index 0000000..0a28a06
--- /dev/null
+++ b/data/apple-imac-retina-4k-21.5-inch-late-2015
Binary files differ
diff --git a/data/vizio-m60c3-hdmi-onkyo-txnr555 b/data/vizio-m60c3-hdmi-onkyo-txnr555
index e69de29..93369ab 100644
--- a/data/vizio-m60c3-hdmi-onkyo-txnr555
+++ b/data/vizio-m60c3-hdmi-onkyo-txnr555
Binary files differ
diff --git a/edid-decode.1 b/edid-decode.1
index 5bb69d1..a304aa8 100644
--- a/edid-decode.1
+++ b/edid-decode.1
@@ -113,7 +113,7 @@ DisplayID 1.3: VESA Display Identification Data (DisplayID) Standard, Version 1.
.TP
DisplayID 2.0: VESA DisplayID Standard, Version 2.0
.TP
-DisplayID 2.0: VESA DisplayID v2.0 Errata E8
+DisplayID 2.0: VESA DisplayID v2.0 Errata E9
.TP
DI-EXT: VESA Display Information Extension Block Standard, Release A
.TP
@@ -146,9 +146,11 @@ The following related standards are also used by edid-decode:
.TP
DMT 1.3: VESA and Industry Standards and Guidelines for Computer Display Monitor Timing (DMT), Version 1.0, Rev. 13
.TP
+CVT 2.0: VESA Coordinated Video Timings (CVT) Standard, Version 2.0
+.TP
CVT 1.2: VESA Coordinated Video Timings (CVT) Standard, Version 1.2
.TP
-CVT 1.2: VESA CVT v1.2 Errata E1
+CVT 1.2: VESA CVT v1.2 Errata E2
.TP
GTF 1.1: VESA Generalized Timing Formula Standard, Version: 1.1
.RE
@@ -179,13 +181,16 @@ reported at the end.
Check if the EDID conforms to the standards. Warnings and failures are
reported as they happen.
.TP
-\fB\-n\fR, \fB\-\-native\-timings\fR
-Report the native timings at the end. There may be multiple native timing reports
+\fB\-n\fR, \fB\-\-native\-resolution\fR
+Report the native resolution at the end. There may be multiple native resolution reports
depending on whether the Source only parses Block 0 (e.g. DVI outputs) or Block 0
-and the CTA-861 Extension Blocks (HDMI).
+and the CTA-861 Extension Blocks (HDMI), or just the DisplayID Extension Blocks
+(typical for DisplayPort). If all blocks contain the same native resolution, then
+only that resolution is reported. For older displays there may be two separate native
+resolutions: progressive and interlaced.
.TP
\fB\-p\fR, \fB\-\-preferred\-timings\fR
-Report the preferred timings at the end. There may be multiple native timing reports
+Report the preferred timings at the end. There may be multiple preferred timing reports
depending on whether the Source only parses Block 0 (e.g. DVI outputs), or Block 0
and the CTA-861 Extension Blocks (HDMI), or Block 0 and the DisplayID Extension Blocks
(typical for DisplayPort).
@@ -203,6 +208,11 @@ Report all video timings in a short format.
\fB\-L\fR, \fB\-\-long\-timings\fR
Report all video timings in a long format.
.TP
+\fB\-N\fR, \fB\-\-ntsc\fR
+Report the video timings with values suitable for NTSC-based video.
+E.g., this will show refresh rates of 29.97 Hz instead of 30 Hz.
+This is only done for timings with refresh rates that are a multiple of 6.
+.TP
\fB\-X\fR, \fB\-\-xmodeline\fR
Report all long video timings in the ModeLine format as defined in xorg.conf(5).
This ModeLine can be used in the xorg.conf file or passed to xrandr(1) with the
@@ -256,7 +266,7 @@ Show the timings for this VIC.
Show the timings for this HDMI VIC.
.TP
\fB\-\-cvt\fR \fBw\fR=\fI<width>\fR,\fBh\fR=\fI<height>\fR,\fBfps\fR=\fI<fps>\fR[,\fBrb\fR=\fI<rb>\fR][,\fBinterlaced\fR][,\fBoverscan\fR]
-[,\fBalt\fR][,\fBhblank\fR=\fI<hblank>\fR][,\fBadd\-vblank\fR=\fI<add\-vblank>\fR]
+[,\fBalt\fR][,\fBhblank\fR=\fI<hblank>\fR][,\fBvblank\fR=\fI<vblank>\fR][,\fBearly\-vsync\fR]
.br
Calculate the CVT timings for the given format.
@@ -279,9 +289,11 @@ is 160 instead of 80 pixels.
If \fBhblank\fR is given and \fI<rb>\fR=3, then the horizontal blanking
is \fI<hblank>\fR pixels (range of 80-200 and divisible by 8), overriding \fBalt\fR.
.br
-If \fBadd\-vblank\fR is given and \fI<rb>\fR=3, then \fI<add\-vblank>\fR microseconds are
-added to the minimum vertical blank time of 460 microseconds as long as the total blank
-time does not exceed 25% of the frame time.
+If \fBvblank\fR is given and \fI<rb>\fR=3, then the vertical blanking time
+is \fI<vblank>\fR microseconds (460 minimum, values > 705 might not be supported by
+all RBv3 timings compliant source devices.
+.br
+If \fBearly\-vsync\fR is given and \fI<rb>\fR=3, then select an early vsync timing.
.TP
\fB\-\-gtf\fR \fBw\fR=\fI<width>\fR,\fBh\fR=\fI<height>\fR[,\fBfps\fR=\fI<fps>\fR][,\fBhorfreq\fR=\fI<horfreq>\fR][,\fBpixclk\fR=\fI<pixclk>\fR]
[,\fBinterlaced\fR][,\fBoverscan\fR][,\fBsecondary\fR][,\fBC\fR=\fI<c>\fR][,\fBM\fR=\fI<m>\fR][,\fBK\fR=\fI<k>\fR][,\fBJ\fR=\fI<j>\fR]
diff --git a/edid-decode.cpp b/edid-decode.cpp
index d5062dd..fcae469 100644
--- a/edid-decode.cpp
+++ b/edid-decode.cpp
@@ -46,7 +46,8 @@ enum Option {
OptHelp = 'h',
OptOnlyHexDump = 'H',
OptLongTimings = 'L',
- OptNativeTimings = 'n',
+ OptNativeResolution = 'n',
+ OptNTSC = 'N',
OptOutputFormat = 'o',
OptPreferredTimings = 'p',
OptPhysicalAddress = 'P',
@@ -75,7 +76,7 @@ static char options[OptLast];
static struct option long_options[] = {
{ "help", no_argument, 0, OptHelp },
{ "output-format", required_argument, 0, OptOutputFormat },
- { "native-timings", no_argument, 0, OptNativeTimings },
+ { "native-resolution", no_argument, 0, OptNativeResolution },
{ "preferred-timings", no_argument, 0, OptPreferredTimings },
{ "physical-address", no_argument, 0, OptPhysicalAddress },
{ "skip-hex-dump", no_argument, 0, OptSkipHexDump },
@@ -87,6 +88,7 @@ static struct option long_options[] = {
{ "check", no_argument, 0, OptCheck },
{ "short-timings", no_argument, 0, OptShortTimings },
{ "long-timings", no_argument, 0, OptLongTimings },
+ { "ntsc", no_argument, 0, OptNTSC },
{ "xmodeline", no_argument, 0, OptXModeLineTimings },
{ "fbmode", no_argument, 0, OptFBModeTimings },
{ "v4l2-timings", no_argument, 0, OptV4L2Timings },
@@ -127,6 +129,7 @@ static void usage(void)
" -P, --physical-address Only report the CEC physical address.\n"
" -S, --short-timings Report all video timings in a short format.\n"
" -L, --long-timings Report all video timings in a long format.\n"
+ " -N, --ntsc Report the video timings suitable for NTSC-based video.\n"
" -X, --xmodeline Report all long video timings in Xorg.conf format.\n"
" -F, --fbmode Report all long video timings in fb.modes format.\n"
" -V, --v4l2-timings Report all long video timings in v4l2-dv-timings.h format.\n"
@@ -139,7 +142,7 @@ static void usage(void)
" --dmt <dmt> Show the timings for the DMT with the given DMT ID.\n"
" --vic <vic> Show the timings for this VIC.\n"
" --hdmi-vic <hdmivic> Show the timings for this HDMI VIC.\n"
- " --cvt w=<width>,h=<height>,fps=<fps>[,rb=<rb>][,interlaced][,overscan][,alt][,hblank=<hblank][,add-vblank=<add-vblank>\n"
+ " --cvt w=<width>,h=<height>,fps=<fps>[,rb=<rb>][,interlaced][,overscan][,alt][,hblank=<hblank][,vblank=<vblank][,early-vsync]\n"
" Calculate the CVT timings for the given format.\n"
" <fps> is frames per second for progressive timings,\n"
" or fields per second for interlaced timings.\n"
@@ -153,8 +156,9 @@ static void usage(void)
" is 160 instead of 80 pixels.\n"
" If 'hblank' is given and <rb>=3, then the horizontal blanking\n"
" is <hblank> pixels (range of 80-200), overriding 'alt'.\n"
- " If 'add-vblank' is given and <rb>=3, then <add-vblank> usecs are\n"
- " added to the minimum vertical blank time of 460 usecs.\n"
+ " If 'vblank' is given and <rb>=3, then the vertical blanking\n"
+ " time is <vblank> microseconds (range of 460-705).\n"
+ " If 'early-vsync' is given and <rb=3>, then select early vsync.\n"
" --gtf w=<width>,h=<height>[,fps=<fps>][,horfreq=<horfreq>][,pixclk=<pixclk>][,interlaced]\n"
" [,overscan][,secondary][,C=<c>][,M=<m>][,K=<k>][,J=<j>]\n"
" Calculate the GTF timings for the given format.\n"
@@ -460,14 +464,14 @@ static void print_v4l2_timing(const struct timings *t,
static void print_detailed_timing(unsigned indent, const struct timings *t)
{
- printf("%*sHfront %4d Hsync %3u Hback %3d Hpol %s",
+ printf("%*sHfront %4d Hsync %3u Hback %4d Hpol %s",
indent, "",
t->hfp, t->hsync, t->hbp, t->pos_pol_hsync ? "P" : "N");
if (t->hborder)
printf(" Hborder %u", t->hborder);
printf("\n");
- printf("%*sVfront %4u Vsync %3u Vback %3d",
+ printf("%*sVfront %4u Vsync %3u Vback %4d",
indent, "", t->vfp, t->vsync, t->vbp);
if (!t->no_pol_vsync)
printf(" Vpol %s", t->pos_pol_vsync ? "P" : "N");
@@ -477,7 +481,7 @@ static void print_detailed_timing(unsigned indent, const struct timings *t)
printf(" Both Fields");
} else if (t->interlaced) {
printf(" Vfront +0.5 Odd Field\n");
- printf("%*sVfront %4d Vsync %3u Vback %3d",
+ printf("%*sVfront %4d Vsync %3u Vback %4d",
indent, "", t->vfp, t->vsync, t->vbp);
if (!t->no_pol_vsync)
printf(" Vpol %s", t->pos_pol_vsync ? "P" : "N");
@@ -538,7 +542,14 @@ bool edid_state::print_timings(const char *prefix, const struct timings *t,
else if (t->interlaced)
vtotal = vact + t->vfp + t->vsync + t->vbp + 0.5;
- double refresh = (double)t->pixclk_khz * 1000.0 / (htotal * vtotal);
+ double refresh = t->pixclk_khz * 1000.0 / (htotal * vtotal);
+ double pixclk = t->pixclk_khz * 1000.0;
+ if (options[OptNTSC] && fmod(refresh, 6.0) == 0) {
+ const double ntsc_fact = 1000.0 / 1001.0;
+ pixclk *= ntsc_fact;
+ refresh *= ntsc_fact;
+ out_hor_freq_khz *= ntsc_fact;
+ }
std::string s;
unsigned rb = t->rb & ~RB_ALT;
@@ -559,19 +570,18 @@ bool edid_state::print_timings(const char *prefix, const struct timings *t,
dtd_max_vsize_mm = t->vsize_mm;
if (!s.empty())
s = " (" + s + ")";
- unsigned out_pixclk_khz = t->pixclk_khz;
unsigned pixclk_khz = t->pixclk_khz / (t->ycbcr420 ? 2 : 1);
char buf[10];
sprintf(buf, "%u%s", t->vact, t->interlaced ? "i" : "");
- printf("%s%s: %5ux%-5s %7.3f Hz %3u:%-3u %7.3f kHz %8.3f MHz%s\n",
+ printf("%s%s: %5ux%-5s %10.6f Hz %3u:%-3u %8.3f kHz %13.6f MHz%s\n",
prefix, type,
t->hact, buf,
refresh,
t->hratio, t->vratio,
out_hor_freq_khz,
- out_pixclk_khz / 1000.0,
+ pixclk / 1000000.0,
s.c_str());
unsigned len = strlen(prefix) + 2;
@@ -657,25 +667,92 @@ std::string utohex(unsigned char x)
return buf;
}
-const char *oui_name(unsigned oui, bool reverse)
+const char *oui_name(unsigned oui, unsigned *ouinum)
{
- if (reverse)
- oui = (oui >> 16) | (oui & 0xff00) | ((oui & 0xff) << 16);
-
+ unsigned ouinumscratch;
+ if (!ouinum) ouinum = &ouinumscratch;
+ const char *name;
switch (oui) {
- case 0x00001a: return "AMD";
- case 0x000c03: return "HDMI";
- case 0x00044b: return "NVIDIA";
- case 0x000c6e: return "ASUS";
- case 0x0010fa: return "Apple";
- case 0x0014b9: return "MSTAR";
- case 0x00d046: return "Dolby";
- case 0x00e047: return "InFocus";
- case 0x3a0292: return "VESA";
- case 0x90848b: return "HDR10+";
- case 0xc45dd8: return "HDMI Forum";
- case 0xca125c: return "Microsoft";
- default: return NULL;
+ #define oneoui(c,k,n) case c: *ouinum = kOUI_##k; name = n; break;
+ #include "oui.h"
+ default: *ouinum = 0; name = NULL; break;
+ }
+ return name;
+}
+
+void edid_state::data_block_oui(std::string block_name, const unsigned char *x,
+ unsigned length, unsigned *ouinum, bool ignorezeros, bool do_ascii, bool big_endian)
+{
+ std::string buf;
+ char ascii[4];
+ unsigned oui;
+ const char *ouiname = NULL;
+ bool matched_reverse = false;
+ bool matched_ascii = false;
+ bool valid_ascii = false;
+
+ if (big_endian)
+ oui = ((length > 0 ? x[0] : 0) << 16) + ((length > 1 ? x[1] : 0) << 8) + (length > 2 ? x[2] : 0);
+ else
+ oui = ((length > 2 ? x[2] : 0) << 16) + ((length > 1 ? x[1] : 0) << 8) + (length > 0 ? x[0] : 0);
+
+ buf = ouitohex(oui);
+ if (length < 3) {
+ sprintf(ascii, "?"); // some characters are null
+ if (ouinum) *ouinum = 0; // doesn't match a known OUI
+ } else {
+ valid_ascii = (x[0] >= 'A' && x[1] >= 'A' && x[2] >= 'A' && x[0] <= 'Z' && x[1] <= 'Z' && x[2] <= 'Z');
+ sprintf(ascii, "%c%c%c", x[0], x[1], x[2]);
+
+ ouiname = oui_name(oui, ouinum);
+ if (!ouiname) {
+ big_endian = !big_endian;
+ unsigned reversedoui = ((oui & 0xff) << 16) + (oui & 0x00ff00) + (oui >> 16);
+ ouiname = oui_name(reversedoui, ouinum);
+ if (ouiname) {
+ oui = reversedoui;
+ buf = ouitohex(oui);
+ matched_reverse = true;
+ } else if (do_ascii && valid_ascii) {
+ unsigned asciioui = (x[0] << 24) + (x[1] << 16) + (x[2] << 8);
+ ouiname = oui_name(asciioui, ouinum);
+ if (ouiname) {
+ matched_ascii = true;
+ }
+ }
+ }
+ }
+
+ std::string name;
+ if (ouiname) {
+ if (matched_ascii)
+ name = block_name + " (" + ouiname + ")" + ", PNP ID '" + ascii + "'";
+ else
+ name = block_name + " (" + ouiname + ")" + ", OUI " + buf;
+ } else if (do_ascii && valid_ascii) {
+ name = block_name + ", PNP ID '" + ascii + "'";
+ } else {
+ name = block_name + ", OUI " + buf;
+ }
+ // assign string to data_block before outputting errors
+ data_block = name;
+
+ if (oui || !ignorezeros) {
+ printf(" %s:\n", data_block.c_str());
+ if (length < 3)
+ fail("Data block length (%d) is not enough to contain an OUI.\n", length);
+ else if (ouiname) {
+ if (do_ascii && !valid_ascii)
+ warn("Expected PNP ID but found OUI.\n");
+ if (matched_reverse)
+ fail("Endian-ness (%s) of OUI is different than expected (%s).\n", big_endian ? "be" : "le", big_endian ? "le" : "be");
+ }
+ else {
+ if (valid_ascii)
+ warn("Unknown OUI %s (possible PNP %s).\n", buf.c_str(), ascii);
+ else
+ warn("Unknown OUI %s.\n", buf.c_str());
+ }
}
}
@@ -700,9 +777,6 @@ void hex_block(const char *prefix, const unsigned char *x,
{
unsigned i, j;
- if (!length)
- return;
-
for (i = 0; i < length; i += step) {
unsigned len = min(step, length - i);
@@ -1222,6 +1296,138 @@ void edid_state::parse_extension(const unsigned char *x)
do_checksum("", x, EDID_PAGE_SIZE);
}
+void edid_state::print_preferred_timings()
+{
+ if (base.preferred_timing.is_valid()) {
+ printf("\n----------------\n");
+ printf("\nPreferred Video Timing if only Block 0 is parsed:\n");
+ print_timings(" ", base.preferred_timing, true, false);
+ }
+
+ if (!cta.preferred_timings.empty()) {
+ printf("\n----------------\n");
+ printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
+ cta.preferred_timings.size() > 1 ? "s" : "");
+ for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
+ iter != cta.preferred_timings.end(); ++iter)
+ print_timings(" ", *iter, true, false);
+ }
+
+ if (!dispid.preferred_timings.empty()) {
+ printf("\n----------------\n");
+ printf("\nPreferred Video Timing%s if Block 0 and DisplayID Blocks are parsed:\n",
+ dispid.preferred_timings.size() > 1 ? "s" : "");
+ for (vec_timings_ext::iterator iter = dispid.preferred_timings.begin();
+ iter != dispid.preferred_timings.end(); ++iter)
+ print_timings(" ", *iter, true, false);
+ }
+}
+
+void edid_state::print_native_res()
+{
+ typedef std::pair<unsigned, unsigned> resolution;
+ typedef std::set<resolution> resolution_set;
+ resolution_set native_prog, native_int;
+ unsigned native_width = 0, native_height = 0;
+ unsigned native_width_int = 0, native_height_int = 0;
+
+ // Note: it is also a mismatch if Block 0 does not define a
+ // native resolution, but other blocks do.
+ bool native_mismatch = false;
+ bool native_int_mismatch = false;
+
+ if (base.preferred_timing.is_valid() && base.preferred_is_also_native) {
+ if (base.preferred_timing.t.interlaced) {
+ native_width_int = base.preferred_timing.t.hact;
+ native_height_int = base.preferred_timing.t.vact;
+ } else {
+ native_width = base.preferred_timing.t.hact;
+ native_height = base.preferred_timing.t.vact;
+ }
+ }
+
+ if (!native_width && dispid.native_width) {
+ native_width = dispid.native_width;
+ native_height = dispid.native_height;
+ native_mismatch = true;
+ } else if (dispid.native_width && native_width &&
+ (dispid.native_width != native_width ||
+ dispid.native_height != native_height)) {
+ native_mismatch = true;
+ }
+
+ for (vec_timings_ext::iterator iter = cta.native_timings.begin();
+ iter != cta.native_timings.end(); ++iter) {
+ if (iter->t.interlaced) {
+ native_int.insert(std::pair<unsigned, unsigned>(iter->t.hact, iter->t.vact));
+ if (!native_width_int) {
+ native_width_int = iter->t.hact;
+ native_height_int = iter->t.vact;
+ native_int_mismatch = true;
+ } else if (native_width_int &&
+ (iter->t.hact != native_width_int ||
+ iter->t.vact != native_height_int)) {
+ native_int_mismatch = true;
+ }
+ } else {
+ native_prog.insert(std::pair<unsigned, unsigned>(iter->t.hact, iter->t.vact));
+ if (!native_width) {
+ native_width = iter->t.hact;
+ native_height = iter->t.vact;
+ native_mismatch = true;
+ } else if (native_width &&
+ (iter->t.hact != native_width ||
+ iter->t.vact != native_height)) {
+ native_mismatch = true;
+ }
+ }
+ }
+
+ if (native_width == 0 && native_width_int == 0) {
+ printf("\n----------------\n");
+ printf("\nNo Native Video Resolution was defined.\n");
+ return;
+ }
+
+ if ((native_width || native_width_int) &&
+ !native_mismatch && !native_int_mismatch) {
+ printf("\n----------------\n");
+ printf("\nNative Video Resolution%s:\n",
+ native_width && native_width_int ? "s" : "");
+ if (native_width)
+ printf(" %ux%u\n", native_width, native_height);
+ if (native_width_int)
+ printf(" %ux%ui\n", native_width_int, native_height_int);
+ return;
+ }
+
+ if (base.preferred_timing.is_valid() && base.preferred_is_also_native) {
+ printf("\n----------------\n");
+ printf("\nNative Video Resolution if only Block 0 is parsed:\n");
+ printf(" %ux%u%s\n",
+ base.preferred_timing.t.hact, base.preferred_timing.t.vact,
+ base.preferred_timing.t.interlaced ? "i" : "");
+ }
+
+ if (!cta.native_timings.empty()) {
+ printf("\n----------------\n");
+ printf("\nNative Video Resolution%s if Block 0 and CTA-861 Blocks are parsed:\n",
+ native_prog.size() + native_int.size() > 1 ? "s" : "");
+ for (resolution_set::iterator iter = native_prog.begin();
+ iter != native_prog.end(); ++iter)
+ printf(" %ux%u\n", iter->first, iter->second);
+ for (resolution_set::iterator iter = native_int.begin();
+ iter != native_int.end(); ++iter)
+ printf(" %ux%ui\n", iter->first, iter->second);
+ }
+
+ if (dispid.native_width) {
+ printf("\n----------------\n");
+ printf("\nNative Video Resolution if the DisplayID Blocks are parsed:\n");
+ printf(" %ux%u\n", dispid.native_width, dispid.native_height);
+ }
+}
+
int edid_state::parse_edid()
{
hide_serial_numbers = options[OptHideSerialNumbers];
@@ -1265,45 +1471,11 @@ int edid_state::parse_edid()
if (has_cta)
cta_resolve_svrs();
- if (options[OptPreferredTimings] && base.preferred_timing.is_valid()) {
- printf("\n----------------\n");
- printf("\nPreferred Video Timing if only Block 0 is parsed:\n");
- print_timings(" ", base.preferred_timing, true, false);
- }
-
- if (options[OptNativeTimings] &&
- base.preferred_timing.is_valid() && base.preferred_is_also_native) {
- printf("\n----------------\n");
- printf("\nNative Video Timing if only Block 0 is parsed:\n");
- print_timings(" ", base.preferred_timing, true, false);
- }
+ if (options[OptPreferredTimings])
+ print_preferred_timings();
- if (options[OptPreferredTimings] && !cta.preferred_timings.empty()) {
- printf("\n----------------\n");
- printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
- cta.preferred_timings.size() > 1 ? "s" : "");
- for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
- iter != cta.preferred_timings.end(); ++iter)
- print_timings(" ", *iter, true, false);
- }
-
- if (options[OptNativeTimings] && !cta.native_timings.empty()) {
- printf("\n----------------\n");
- printf("\nNative Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
- cta.native_timings.size() > 1 ? "s" : "");
- for (vec_timings_ext::iterator iter = cta.native_timings.begin();
- iter != cta.native_timings.end(); ++iter)
- print_timings(" ", *iter, true, false);
- }
-
- if (options[OptPreferredTimings] && !dispid.preferred_timings.empty()) {
- printf("\n----------------\n");
- printf("\nPreferred Video Timing%s if Block 0 and DisplayID Blocks are parsed:\n",
- dispid.preferred_timings.size() > 1 ? "s" : "");
- for (vec_timings_ext::iterator iter = dispid.preferred_timings.begin();
- iter != dispid.preferred_timings.end(); ++iter)
- print_timings(" ", *iter, true, false);
- }
+ if (options[OptNativeResolution])
+ print_native_res();
if (!options[OptCheck] && !options[OptCheckInline])
return 0;
@@ -1339,7 +1511,8 @@ enum cvt_opts {
CVT_RB,
CVT_ALT,
CVT_RB_H_BLANK,
- CVT_RB_ADD_V_BLANK,
+ CVT_RB_V_BLANK,
+ CVT_EARLY_VSYNC,
};
static int parse_cvt_subopt(char **subopt_str, double *value)
@@ -1356,7 +1529,8 @@ static int parse_cvt_subopt(char **subopt_str, double *value)
"rb",
"alt",
"hblank",
- "add-vblank",
+ "vblank",
+ "early-vsync",
nullptr
};
@@ -1368,7 +1542,7 @@ static int parse_cvt_subopt(char **subopt_str, double *value)
std::exit(EXIT_FAILURE);
}
if (opt_str == nullptr && opt != CVT_INTERLACED && opt != CVT_ALT &&
- opt != CVT_OVERSCAN) {
+ opt != CVT_OVERSCAN && opt != CVT_EARLY_VSYNC) {
fprintf(stderr, "No value given to suboption <%s>.\n",
subopt_list[opt]);
usage();
@@ -1386,10 +1560,11 @@ static void parse_cvt(char *optarg)
double fps = 0;
unsigned rb = RB_NONE;
unsigned rb_h_blank = 0;
- unsigned rb_add_v_blank = 0;
+ unsigned rb_v_blank = 460;
bool interlaced = false;
bool alt = false;
bool overscan = false;
+ bool early_vsync = false;
while (*optarg != '\0') {
int opt;
@@ -1422,8 +1597,17 @@ static void parse_cvt(char *optarg)
case CVT_RB_H_BLANK:
rb_h_blank = opt_val;
break;
- case CVT_RB_ADD_V_BLANK:
- rb_add_v_blank = opt_val;
+ case CVT_RB_V_BLANK:
+ rb_v_blank = opt_val;
+ if (rb_v_blank < 460) {
+ fprintf(stderr, "vblank must be >= 460, set to 460.\n");
+ rb_v_blank = 460;
+ } else if (rb_v_blank > 705) {
+ fprintf(stderr, "warning: vblank values > 705 might not be supported by RBv3 compliant sources.\n");
+ }
+ break;
+ case CVT_EARLY_VSYNC:
+ early_vsync = true;
break;
default:
break;
@@ -1438,7 +1622,7 @@ static void parse_cvt(char *optarg)
if (interlaced)
fps /= 2;
timings t = state.calc_cvt_mode(w, h, fps, rb, interlaced, overscan, alt,
- rb_h_blank, rb_add_v_blank);
+ rb_h_blank, rb_v_blank, early_vsync);
state.print_timings("", &t, "CVT", "", true, false);
}
@@ -1797,7 +1981,7 @@ extern "C" int parse_edid(const char *input)
}
options[OptCheck] = 1;
options[OptPreferredTimings] = 1;
- options[OptNativeTimings] = 1;
+ options[OptNativeResolution] = 1;
state = edid_state();
int ret = edid_from_file(input, stderr);
return ret ? ret : state.parse_edid();
diff --git a/edid-decode.h b/edid-decode.h
index 612d22a..87cf662 100644
--- a/edid-decode.h
+++ b/edid-decode.h
@@ -153,8 +153,10 @@ struct edid_state {
// CTA-861 block state
cta.has_vic_1 = cta.first_svd_might_be_preferred = cta.has_sldb =
cta.has_hdmi = cta.has_vcdb = cta.has_vfpdb = false;
- cta.last_block_was_hdmi_vsdb = cta.have_hf_vsdb = cta.have_hf_scdb = false;
- cta.first_block = cta.first_svd = true;
+ cta.previous_cta_tag = 0xfff;
+ cta.have_hf_vsdb = cta.have_hf_scdb = false;
+ cta.block_number = 0;
+ cta.first_svd = true;
cta.supported_hdmi_vic_codes = cta.supported_hdmi_vic_vsb_codes = 0;
memset(cta.vics, 0, sizeof(cta.vics));
memset(cta.preparsed_has_vic, 0, sizeof(cta.preparsed_has_vic));
@@ -175,6 +177,7 @@ struct edid_state {
dispid.is_display = dispid.has_product_identification =
dispid.has_display_parameters = dispid.has_type_1_7 =
dispid.has_display_interface_features = false;
+ dispid.block_number = 0;
// Block Map block state
block_map.saw_block_1 = false;
@@ -242,8 +245,8 @@ struct edid_state {
vec_timings_ext preferred_timings;
bool preparsed_has_t8vtdb;
// Keep track of the found Tag/Extended Tag pairs.
- // The unsigned value is equal to: (tag << 8) | ext_tag
- std::set<unsigned> found_tags;
+ // The unsigned value is equal to: (tag) | (OUI enum << 12) or (extended tag) | (tag << 8) | (OUI enum << 12)
+ std::vector<unsigned> found_tags;
timings_ext t8vtdb;
vec_timings_ext native_timings;
bool has_vic_1;
@@ -257,9 +260,9 @@ struct edid_state {
bool preparsed_sld;
bool has_sldb;
unsigned short preparsed_phys_addr;
- bool last_block_was_hdmi_vsdb;
+ unsigned previous_cta_tag;
bool have_hf_vsdb, have_hf_scdb;
- bool first_block;
+ unsigned block_number;
bool first_svd;
unsigned supported_hdmi_vic_codes;
unsigned supported_hdmi_vic_vsb_codes;
@@ -282,9 +285,10 @@ struct edid_state {
bool has_display_interface_features;
vec_timings_ext preferred_timings;
unsigned native_width, native_height;
+ unsigned block_number;
// Keep track of the found CTA-861 Tag/Extended Tag pairs.
- // The unsigned value is equal to: (tag << 8) | ext_tag
- std::set<unsigned> found_tags;
+ // The unsigned value is equal to: (tag) | (OUI enum << 12) or (extended tag) | (tag << 8) | (OUI enum << 12)
+ std::vector<unsigned> found_tags;
} dispid;
// Block Map block state
@@ -314,9 +318,10 @@ struct edid_state {
timings calc_cvt_mode(unsigned h_pixels, unsigned v_lines,
double ip_freq_rqd, unsigned rb, bool int_rqd = false,
bool margins_rqd = false, bool alt = false,
- unsigned rb_h_blank = 0, double add_vert_time = 0);
+ unsigned rb_h_blank = 0, unsigned rb_v_blank = 460,
+ bool early_vsync_rqd = false);
void edid_cvt_mode(unsigned refresh, struct timings &t, unsigned rb_h_blank = 0,
- double add_vert_time = 0);
+ unsigned rb_v_blank = 460, bool early_vsync_rqd = false);
void detailed_cvt_descriptor(const char *prefix, const unsigned char *x, bool first);
void print_standard_timing(const char *prefix, unsigned char b1, unsigned char b2,
bool gtf_only = false, bool show_both = false);
@@ -331,11 +336,15 @@ struct edid_state {
void list_dmts();
void list_established_timings();
+ void data_block_oui(std::string block_name, const unsigned char *x, unsigned length, unsigned *ouinum,
+ bool ignorezeros = false, bool do_ascii = false, bool big_endian = false);
+
void print_vic_index(const char *prefix, unsigned idx, const char *suffix, bool ycbcr420 = false);
void hdmi_latency(unsigned char vid_lat, unsigned char aud_lat, bool is_ilaced);
void cta_vcdb(const unsigned char *x, unsigned length);
void cta_svd(const unsigned char *x, unsigned n, bool for_ycbcr420);
void cta_y420cmdb(const unsigned char *x, unsigned length);
+ void cta_print_svr(unsigned char svr, vec_timings_ext &vec_tim);
void cta_vfpdb(const unsigned char *x, unsigned length);
void cta_rcdb(const unsigned char *x, unsigned length);
void cta_sldb(const unsigned char *x, unsigned length);
@@ -344,8 +353,7 @@ struct edid_state {
void cta_displayid_type_7(const unsigned char *x, unsigned length);
void cta_displayid_type_8(const unsigned char *x, unsigned length);
void cta_displayid_type_10(const unsigned char *x, unsigned length);
- void cta_ext_block(const unsigned char *x, unsigned length, bool duplicate);
- void cta_block(const unsigned char *x, bool duplicate);
+ void cta_block(const unsigned char *x, std::vector<unsigned> &found_tags);
void preparse_cta_block(const unsigned char *x);
void parse_cta_block(const unsigned char *x);
void cta_resolve_svr(vec_timings_ext::iterator iter);
@@ -354,6 +362,7 @@ struct edid_state {
void cta_list_vics();
void cta_list_hdmi_vics();
+ void set_displayid_native_res(unsigned w, unsigned h);
void parse_digital_interface(const unsigned char *x);
void parse_display_device(const unsigned char *x);
void parse_display_caps(const unsigned char *x);
@@ -387,9 +396,11 @@ struct edid_state {
void parse_displayid_type_9_timing(const unsigned char *x);
void parse_displayid_dynamic_video_timings_range_limits(const unsigned char *x);
void parse_displayid_ContainerID(const unsigned char *x);
+ void parse_displayid_adaptive_sync(const unsigned char *x);
void parse_displayid_type_10_timing(const unsigned char *x, unsigned sz,
bool is_cta = false);
void preparse_displayid_block(const unsigned char *x);
+ unsigned displayid_block(const unsigned version, const unsigned char *x, unsigned length);
void parse_displayid_block(const unsigned char *x);
void parse_displayid_vesa(const unsigned char *x);
void parse_displayid_cta_data_block(const unsigned char *x);
@@ -404,6 +415,8 @@ struct edid_state {
void preparse_extension(const unsigned char *x);
void parse_extension(const unsigned char *x);
+ void print_preferred_timings();
+ void print_native_res();
int parse_edid();
};
@@ -454,7 +467,7 @@ void hex_block(const char *prefix, const unsigned char *x, unsigned length,
bool show_ascii = true, unsigned step = 16);
std::string block_name(unsigned char block);
void calc_ratio(struct timings *t);
-const char *oui_name(unsigned oui, bool reverse = false);
+const char *oui_name(unsigned oui, unsigned *ouinum = NULL);
bool timings_close_match(const timings &t1, const timings &t2);
const struct timings *find_dmt_id(unsigned char dmt_id);
@@ -465,4 +478,7 @@ const struct timings *cta_close_match_to_vic(const timings &t, unsigned &vic);
unsigned char hdmi_vic_to_vic(unsigned char hdmi_vic);
char *extract_string(const unsigned char *x, unsigned len);
+#define oneoui(c,k,n) const unsigned kOUI_##k = __LINE__<<12;
+#include "oui.h"
+
#endif
diff --git a/oui.h b/oui.h
new file mode 100644
index 0000000..d4fea3c
--- /dev/null
+++ b/oui.h
@@ -0,0 +1,20 @@
+// http://standards-oui.ieee.org/oui/oui.txt
+oneoui(0x000c03, HDMI, "HDMI" )
+oneoui(0xc45dd8, HDMIForum, "HDMI Forum" )
+oneoui(0x90848b, HDR10, "HDR10+" )
+oneoui(0x00001a, AMD, "AMD" )
+oneoui(0x00044b, NVIDIA, "NVIDIA" )
+oneoui(0x000c6e, ASUS, "ASUS" )
+oneoui(0x0010fa, Apple, "Apple" )
+oneoui(0x0014b9, MSTAR, "MSTAR" )
+oneoui(0x00d046, Dolby, "Dolby" )
+oneoui(0x00e047, InFocus, "InFocus" )
+oneoui(0xca125c, Microsoft, "Microsoft" )
+
+// http://standards-oui.ieee.org/cid/cid.txt
+oneoui(0x3a0292, VESA, "VESA" )
+
+// https://uefi.org/pnp_id_list
+oneoui(0x41505000, asciiApple, "Apple" ) // 'APP\0'
+
+#undef oneoui
diff --git a/parse-base-block.cpp b/parse-base-block.cpp
index 174af51..9e2ef4f 100644
--- a/parse-base-block.cpp
+++ b/parse-base-block.cpp
@@ -573,7 +573,6 @@ void edid_state::print_standard_timing(const char *prefix, unsigned char b1, uns
break;
}
vact = (double)hact * vratio / hratio;
- vact = 8 * ((vact + 7) / 8);
refresh = (b2 & 0x3f) + 60;
formula.hact = hact;
@@ -605,6 +604,10 @@ void edid_state::print_standard_timing(const char *prefix, unsigned char b1, uns
min_vert_freq_hz = min(min_vert_freq_hz, refresh);
max_vert_freq_hz = max(max_vert_freq_hz, refresh);
}
+
+ // See Ref. D-8 in the EDID-1.4 spec
+ if (vact & 1)
+ warn("Standard Timing %ux%u has a dubious odd vertical resolution.\n", hact, vact);
}
void edid_state::detailed_display_range_limits(const unsigned char *x)
@@ -1472,7 +1475,7 @@ void edid_state::parse_base_block(const unsigned char *x)
case 0x00: printf("Monochrome or grayscale display\n"); break;
case 0x08: printf("RGB color display\n"); break;
case 0x10: printf("Non-RGB color display\n"); break;
- case 0x18: printf("Undefined display color type\n");
+ case 0x18: printf("Undefined display color type\n"); break;
}
} else {
printf(" Supported color formats: RGB 4:4:4");
diff --git a/parse-cta-block.cpp b/parse-cta-block.cpp
index ee0b01d..7b3f0c3 100644
--- a/parse-cta-block.cpp
+++ b/parse-cta-block.cpp
@@ -7,6 +7,7 @@
* Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
*/
+#include <algorithm>
#include <stdio.h>
#include <math.h>
@@ -546,6 +547,55 @@ void edid_state::cta_y420cmdb(const unsigned char *x, unsigned length)
max_idx + 1, cta.preparsed_svds[0].size());
}
+void edid_state::cta_print_svr(unsigned char svr, vec_timings_ext &vec_tim)
+{
+ char suffix[16];
+
+ if ((svr > 0 && svr < 128) || (svr > 192 && svr < 254)) {
+ const struct timings *t;
+ unsigned char vic = svr;
+
+ sprintf(suffix, "VIC %3u", vic);
+
+ t = find_vic_id(vic);
+ if (t) {
+ print_timings(" ", t, suffix);
+ vec_tim.push_back(timings_ext(*t, suffix, ""));
+ } else {
+ printf(" %s: Unknown\n", suffix);
+ fail("Unknown VIC %u.\n", vic);
+ }
+
+ } else if (svr >= 129 && svr <= 144) {
+ sprintf(suffix, "DTD %3u", svr - 128);
+ if (svr >= cta.preparsed_total_dtds + 129) {
+ printf(" %s: Invalid\n", suffix);
+ fail("Invalid DTD %u.\n", svr - 128);
+ } else {
+ printf(" %s\n", suffix);
+ vec_tim.push_back(timings_ext(svr, suffix));
+ }
+ } else if (svr >= 145 && svr <= 160) {
+ sprintf(suffix, "VTDB %3u", svr - 144);
+ if (svr >= cta.preparsed_total_vtdbs + 145) {
+ printf(" %s: Invalid\n", suffix);
+ fail("Invalid VTDB %u.\n", svr - 144);
+ } else {
+ printf(" %s\n", suffix);
+ vec_tim.push_back(timings_ext(svr, suffix));
+ }
+ } else if (svr == 254) {
+ sprintf(suffix, "T8VTDB");
+ if (!cta.preparsed_has_t8vtdb) {
+ printf(" %s: Invalid\n", suffix);
+ fail("Invalid T8VTDB.\n");
+ } else {
+ printf(" %s\n", suffix);
+ vec_tim.push_back(timings_ext(svr, suffix));
+ }
+ }
+}
+
void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
{
unsigned i;
@@ -555,54 +605,8 @@ void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
return;
}
cta.preferred_timings.clear();
- for (i = 0; i < length; i++) {
- unsigned char svr = x[i];
- char suffix[16];
-
- if ((svr > 0 && svr < 128) || (svr > 192 && svr < 254)) {
- const struct timings *t;
- unsigned char vic = svr;
-
- sprintf(suffix, "VIC %3u", vic);
-
- t = find_vic_id(vic);
- if (t) {
- print_timings(" ", t, suffix);
- cta.preferred_timings.push_back(timings_ext(*t, suffix, ""));
- } else {
- printf(" %s: Unknown\n", suffix);
- fail("Unknown VIC %u.\n", vic);
- }
-
- } else if (svr >= 129 && svr <= 144) {
- sprintf(suffix, "DTD %3u", svr - 128);
- if (svr >= cta.preparsed_total_dtds + 129) {
- printf(" %s: Invalid\n", suffix);
- fail("Invalid DTD %u.\n", svr - 128);
- } else {
- printf(" %s\n", suffix);
- cta.preferred_timings.push_back(timings_ext(svr, suffix));
- }
- } else if (svr >= 145 && svr <= 160) {
- sprintf(suffix, "VTDB %3u", svr - 144);
- if (svr >= cta.preparsed_total_vtdbs + 145) {
- printf(" %s: Invalid\n", suffix);
- fail("Invalid VTDB %u.\n", svr - 144);
- } else {
- printf(" %s\n", suffix);
- cta.preferred_timings.push_back(timings_ext(svr, suffix));
- }
- } else if (svr == 254) {
- sprintf(suffix, "T8VTDB");
- if (!cta.preparsed_has_t8vtdb) {
- printf(" %s: Invalid\n", suffix);
- fail("Invalid T8VTDB.\n");
- } else {
- printf(" %s\n", suffix);
- cta.preferred_timings.push_back(timings_ext(svr, suffix));
- }
- }
- }
+ for (i = 0; i < length; i++)
+ cta_print_svr(x[i], cta.preferred_timings);
}
static std::string hdmi_latency2s(unsigned char l, bool is_video)
@@ -652,57 +656,57 @@ void edid_state::cta_hdmi_block(const unsigned char *x, unsigned length)
{
unsigned len_vic, len_3d;
- if (length < 4) {
+ if (length < 1) {
fail("Empty Data Block with length %u.\n", length);
return;
}
- printf(" Source physical address: %x.%x.%x.%x\n", x[3] >> 4, x[3] & 0x0f,
- x[4] >> 4, x[4] & 0x0f);
+ printf(" Source physical address: %x.%x.%x.%x\n", x[0] >> 4, x[0] & 0x0f,
+ x[1] >> 4, x[1] & 0x0f);
- if (length < 6)
+ if (length < 3)
return;
- if (x[5] & 0x80)
+ if (x[2] & 0x80)
printf(" Supports_AI\n");
- if (x[5] & 0x40)
+ if (x[2] & 0x40)
printf(" DC_48bit\n");
- if (x[5] & 0x20)
+ if (x[2] & 0x20)
printf(" DC_36bit\n");
- if (x[5] & 0x10)
+ if (x[2] & 0x10)
printf(" DC_30bit\n");
- if (x[5] & 0x08)
+ if (x[2] & 0x08)
printf(" DC_Y444\n");
/* two reserved bits */
- if (x[5] & 0x01)
+ if (x[2] & 0x01)
printf(" DVI_Dual\n");
- if (length < 7)
+ if (length < 4)
return;
- printf(" Maximum TMDS clock: %u MHz\n", x[6] * 5);
- if (x[6] * 5 > 340)
+ printf(" Maximum TMDS clock: %u MHz\n", x[3] * 5);
+ if (x[3] * 5 > 340)
fail("HDMI VSDB Max TMDS rate is > 340.\n");
- if (length < 8)
+ if (length < 5)
return;
- if (x[7] & 0x0f) {
+ if (x[4] & 0x0f) {
printf(" Supported Content Types:\n");
- if (x[7] & 0x01)
+ if (x[4] & 0x01)
printf(" Graphics\n");
- if (x[7] & 0x02)
+ if (x[4] & 0x02)
printf(" Photo\n");
- if (x[7] & 0x04)
+ if (x[4] & 0x04)
printf(" Cinema\n");
- if (x[7] & 0x08)
+ if (x[4] & 0x08)
printf(" Game\n");
}
- unsigned b = 8;
- if (x[7] & 0x80) {
+ unsigned b = 5;
+ if (x[4] & 0x80) {
hdmi_latency(x[b], x[b + 1], false);
- if (x[7] & 0x40) {
+ if (x[4] & 0x40) {
if (x[b] == x[b + 2] &&
x[b + 1] == x[b + 3])
warn("Progressive and Interlaced latency values are identical, no need for both.\n");
@@ -712,7 +716,7 @@ void edid_state::cta_hdmi_block(const unsigned char *x, unsigned length)
b += 2;
}
- if (!(x[7] & 0x20))
+ if (!(x[4] & 0x20))
return;
bool mask = false;
@@ -1133,11 +1137,12 @@ static void cta_microsoft(const unsigned char *x, unsigned length)
static void cta_hdr10plus(const unsigned char *x, unsigned length)
{
- printf(" Application Version: %u", x[0]);
- if (length > 1)
- hex_block(" ", x + 1, length - 1);
- else
- printf("\n");
+ if (length == 0) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+ printf(" Application Version: %u\n", x[0]);
+ hex_block(" ", x + 1, length - 1);
}
// Convert a PQ value (0-1) to cd/m^2 aka nits (0-10000)
@@ -2026,293 +2031,187 @@ static void cta_hdmi_audio_block(const unsigned char *x, unsigned length)
}
}
-void edid_state::cta_ext_block(const unsigned char *x, unsigned length,
- bool duplicate)
+void edid_state::cta_block(const unsigned char *x, std::vector<unsigned> &found_tags)
{
- const char *name;
- unsigned oui;
- bool reverse = false;
+ unsigned length = x[0] & 0x1f;
+ unsigned tag = (x[0] & 0xe0) >> 5;
+ unsigned extended = (tag == 0x07) ? 1 : 0;
+
+ x++;
+ if (extended && length) {
+ tag <<= 8;
+ tag |= x[0];
+ length--;
+ x++;
+ }
+
+ bool dooutputname = true;
bool audio_block = false;
+ data_block.clear();
+
+ switch (tag) {
+ case 0x01: data_block = "Audio Data Block"; audio_block = true; break;
+ case 0x02: data_block = "Video Data Block"; break;
+ case 0x03: data_block = "Vendor-Specific Data Block"; break;
+ case 0x04: data_block = "Speaker Allocation Data Block"; audio_block = true; break;
+ case 0x05: data_block = "VESA Display Transfer Characteristics Data Block"; break;
+
+ case 0x07: data_block = "Unknown CTA-861 Data Block (extended tag truncated)"; break;
+
+ case 0x700: data_block = "Video Capability Data Block"; break;
+ case 0x701: data_block = "Vendor-Specific Video Data Block"; break;
+ case 0x702: data_block = "VESA Video Display Device Data Block"; break;
+ case 0x703: data_block = "VESA Video Timing Block Extension"; break;
+ case 0x704: data_block = "Reserved for HDMI Video Data Block"; break;
+ case 0x705: data_block = "Colorimetry Data Block"; break;
+ case 0x706: data_block = "HDR Static Metadata Data Block"; break;
+ case 0x707: data_block = "HDR Dynamic Metadata Data Block"; break;
+
+ case 0x70d: data_block = "Video Format Preference Data Block"; break;
+ case 0x70e: data_block = "YCbCr 4:2:0 Video Data Block"; break;
+ case 0x70f: data_block = "YCbCr 4:2:0 Capability Map Data Block"; break;
+ case 0x710: data_block = "Reserved for CTA-861 Miscellaneous Audio Fields"; break;
+ case 0x711: data_block = "Vendor-Specific Audio Data Block"; audio_block = true; break;
+ case 0x712: data_block = "HDMI Audio Data Block"; audio_block = true; break;
+ case 0x713: data_block = "Room Configuration Data Block"; audio_block = true; break;
+ case 0x714: data_block = "Speaker Location Data Block"; audio_block = true; break;
+
+ case 0x720: data_block = "InfoFrame Data Block"; break;
+
+ case 0x734: data_block = "DisplayID Type VII Video Timing Data Block"; break;
+ case 0x735: data_block = "DisplayID Type VIII Video Timing Data Block"; break;
+ case 0x742: data_block = "DisplayID Type X Video Timing Data Block"; break;
+
+ case 0x778: data_block = "HDMI Forum EDID Extension Override Data Block"; break;
+ case 0x779: data_block = "HDMI Forum Sink Capability Data Block"; break;
- switch (x[0]) {
- case 0x00: data_block = "Video Capability Data Block"; break;
- case 0x01: data_block.clear(); break;
- case 0x02: data_block = "VESA Video Display Device Data Block"; break;
- case 0x03: data_block = "VESA Video Timing Block Extension"; break;
- case 0x04: data_block = "Reserved for HDMI Video Data Block"; break;
- case 0x05: data_block = "Colorimetry Data Block"; break;
- case 0x06: data_block = "HDR Static Metadata Data Block"; break;
- case 0x07: data_block = "HDR Dynamic Metadata Data Block"; break;
-
- case 0x0d: data_block = "Video Format Preference Data Block"; break;
- case 0x0e: data_block = "YCbCr 4:2:0 Video Data Block"; break;
- case 0x0f: data_block = "YCbCr 4:2:0 Capability Map Data Block"; break;
- case 0x10: data_block = "Reserved for CTA-861 Miscellaneous Audio Fields"; break;
- case 0x11: data_block.clear(); audio_block = true; break;
- case 0x12: data_block = "HDMI Audio Data Block"; audio_block = true; break;
- case 0x13: data_block = "Room Configuration Data Block"; audio_block = true; break;
- case 0x14: data_block = "Speaker Location Data Block"; audio_block = true; break;
-
- case 0x20: data_block = "InfoFrame Data Block"; break;
-
- case 0x34: data_block = "DisplayID Type VII Video Timing Data Block"; break;
- case 0x35: data_block = "DisplayID Type VIII Video Timing Data Block"; break;
- case 0x42: data_block = "DisplayID Type X Video Timing Data Block"; break;
-
- case 0x78: data_block = "HDMI Forum EDID Extension Override Data Block"; break;
- case 0x79: data_block = "HDMI Forum Sink Capability Data Block"; break;
default:
- if (x[0] <= 12)
- printf(" Unknown CTA-861 Video-Related");
- else if (x[0] <= 31)
- printf(" Unknown CTA-861 Audio-Related");
- else if (x[0] >= 120 && x[0] <= 127)
- printf(" Unknown CTA-861 HDMI-Related");
- else
- printf(" Unknown CTA-861");
- printf(" Data Block (extended tag 0x%02x, length %u)\n", x[0], length);
- hex_block(" ", x + 1, length);
- data_block.clear();
- warn("Unknown Extended CTA-861 Data Block 0x%02x.\n", x[0]);
- return;
+ std::string unknown_name;
+ if (tag < 0x700) unknown_name = "Unknown CTA-861 Data Block";
+ else if (tag < 0x70d) unknown_name = "Unknown CTA-861 Video-Related Data Block";
+ else if (tag < 0x720) unknown_name = "Unknown CTA-861 Audio-Related Data Block";
+ else if (tag < 0x778) unknown_name = "Unknown CTA-861 Data Block";
+ else if (tag < 0x780) unknown_name = "Unknown CTA-861 HDMI-Related Data Block";
+ else unknown_name = "Unknown CTA-861 Data Block";
+ unknown_name += std::string(" (") + (extended ? "extended " : "") + "tag " + utohex(tag & 0xff) + ", length " + std::to_string(length) + ")";
+ printf(" %s:\n", unknown_name.c_str());
+ warn("%s.\n", unknown_name.c_str());
+ break;
+ }
+
+ switch (tag) {
+ case 0x03:
+ case 0x701:
+ case 0x711: {
+ unsigned ouinum;
+
+ data_block_oui(data_block, x, length, &ouinum);
+ x += (length < 3) ? length : 3;
+ length -= (length < 3) ? length : 3;
+ dooutputname = false;
+ tag |= ouinum;
+ break;
}
+ }
+
+ if (dooutputname && data_block.length())
+ printf(" %s:\n", data_block.c_str());
- switch (x[0]) {
- case 0x00:
- case 0x02:
+ switch (tag) {
+ case 0x04:
case 0x05:
- case 0x06:
- case 0x0d:
- case 0x0f:
- case 0x12:
- case 0x13:
- case 0x78:
- case 0x79:
- if (duplicate)
+ case 0x700:
+ case 0x702:
+ case 0x705:
+ case 0x706:
+ case 0x70d:
+ case 0x70f:
+ case 0x712:
+ case 0x713:
+ case 0x778:
+ case 0x779:
+ if (std::find(found_tags.begin(), found_tags.end(), tag) != found_tags.end())
fail("Only one instance of this Data Block is allowed.\n");
break;
}
-
// See Table 52 of CTA-861-G for a description of Byte 3
if (audio_block && !(cta.byte3 & 0x40))
- fail("audio information is present, but bit 6 of Byte 3 of the CTA-861 Extension header indicates no Basic Audio support.\n");
-
- if (data_block.length())
- printf(" %s:\n", data_block.c_str());
-
- switch (x[0]) {
- case 0x00: cta_vcdb(x + 1, length); return;
- case 0x01:
- if (length < 3) {
- data_block = std::string("Vendor-Specific Video Data Block");
- fail("Invalid length %u < 3.\n", length);
- return;
- }
- oui = (x[3] << 16) + (x[2] << 8) + x[1];
- name = oui_name(oui);
- if (!name) {
- name = oui_name(oui, true);
- if (name)
- reverse = true;
- }
- if (!name) {
- printf(" Vendor-Specific Video Data Block, OUI %s:\n",
- ouitohex(oui).c_str());
- hex_block(" ", x + 4, length - 3);
- data_block.clear();
- warn("Unknown Extended Vendor-Specific Video Data Block, OUI %s.\n",
- ouitohex(oui).c_str());
- return;
- }
- data_block = std::string("Vendor-Specific Video Data Block (") + name + ")";
- if (reverse)
- fail((std::string("OUI ") + ouitohex(oui) + " is in the wrong byte order\n").c_str());
- printf(" %s, OUI %s:\n", data_block.c_str(), ouitohex(oui).c_str());
- if (oui == 0x90848b)
- cta_hdr10plus(x + 4, length - 3);
- else if (oui == 0x00d046)
- cta_dolby_video(x + 4, length - 3);
- else
- hex_block(" ", x + 4, length - 3);
- return;
- case 0x02: cta_vesa_vdddb(x + 1, length); return;
- case 0x05: cta_colorimetry_block(x + 1, length); return;
- case 0x06: cta_hdr_static_metadata_block(x + 1, length); return;
- case 0x07: cta_hdr_dyn_metadata_block(x + 1, length); return;
- case 0x0d: cta_vfpdb(x + 1, length); return;
- case 0x0e: cta_svd(x + 1, length, true); return;
- case 0x0f: cta_y420cmdb(x + 1, length); return;
- case 0x11:
- if (length < 3) {
- data_block = std::string("Vendor-Specific Audio Data Block");
- fail("Invalid length %u < 3.\n", length);
- return;
- }
- oui = (x[3] << 16) + (x[2] << 8) + x[1];
- name = oui_name(oui);
- if (!name) {
- name = oui_name(oui, true);
- if (name)
- reverse = true;
- }
- if (!name) {
- printf(" Vendor-Specific Audio Data Block, OUI %s:\n",
- ouitohex(oui).c_str());
- hex_block(" ", x + 4, length - 3);
- data_block.clear();
- warn("Unknown Extended Vendor-Specific Audio Data Block, OUI %s.\n",
- ouitohex(oui).c_str());
- return;
- }
- data_block = std::string("Vendor-Specific Audio Data Block (") + name + ")";
- if (reverse)
- fail((std::string("OUI ") + ouitohex(oui) + " is in the wrong byte order\n").c_str());
- printf(" %s, OUI %s:\n", data_block.c_str(), ouitohex(oui).c_str());
- if (oui == 0x00d046)
- cta_dolby_audio(x + 4, length - 3);
- else
- hex_block(" ", x + 4, length - 3);
- return;
- case 0x12: cta_hdmi_audio_block(x + 1, length); return;
- case 0x13: cta_rcdb(x + 1, length); return;
- case 0x14: cta_sldb(x + 1, length); return;
- case 0x20: cta_ifdb(x + 1, length); return;
- case 0x34: cta_displayid_type_7(x + 1, length); return;
- case 0x35: cta_displayid_type_8(x + 1, length); return;
- case 0x42: cta_displayid_type_10(x + 1, length); return;
- case 0x78:
- cta_hf_eeodb(x + 1, length);
+ fail("Audio information is present, but bit 6 of Byte 3 of the CTA-861 Extension header indicates no Basic Audio support.\n");
+
+ switch (tag) {
+ case 0x01: cta_audio_block(x, length); break;
+ case 0x02: cta_svd(x, length, false); break;
+ case 0x03|kOUI_HDMI:
+ cta_hdmi_block(x, length);
+ // The HDMI OUI is present, so this EDID represents an HDMI
+ // interface. And HDMI interfaces must use EDID version 1.3
+ // according to the HDMI Specification, so check for this.
+ if (base.edid_minor != 3)
+ fail("The HDMI Specification requires EDID 1.3 instead of 1.%u.\n",
+ base.edid_minor);
+ break;
+ case 0x03|kOUI_HDMIForum:
+ if (cta.previous_cta_tag != (0x03|kOUI_HDMI))
+ fail("HDMI Forum VSDB did not immediately follow the HDMI VSDB.\n");
+ if (cta.have_hf_scdb || cta.have_hf_vsdb)
+ fail("Duplicate HDMI Forum VSDB/SCDB.\n");
+ cta_hf_scdb(x, length);
+ cta.have_hf_vsdb = true;
+ break;
+ case 0x03|kOUI_AMD: cta_amd(x, length); break;
+ case 0x03|kOUI_Microsoft: if (length != 0x12) goto dodefault; cta_microsoft(x, length); break;
+ case 0x04: cta_sadb(x, length); break;
+ case 0x05: cta_vesa_dtcdb(x, length); break;
+ case 0x07: fail("Extended tag cannot have zero length.\n"); break;
+ case 0x700: cta_vcdb(x, length); break;
+ case 0x701|kOUI_HDR10: cta_hdr10plus(x, length); break;
+ case 0x701|kOUI_Dolby: cta_dolby_video(x, length); break;
+ case 0x702: cta_vesa_vdddb(x, length); break;
+ case 0x705: cta_colorimetry_block(x, length); break;
+ case 0x706: cta_hdr_static_metadata_block(x, length); break;
+ case 0x707: cta_hdr_dyn_metadata_block(x, length); break;
+ case 0x70d: cta_vfpdb(x, length); break;
+ case 0x70e: cta_svd(x, length, true); break;
+ case 0x70f: cta_y420cmdb(x, length); break;
+ case 0x711|kOUI_Dolby: cta_dolby_audio(x, length); break;
+ case 0x712: cta_hdmi_audio_block(x, length); break;
+ case 0x713: cta_rcdb(x, length); break;
+ case 0x714: cta_sldb(x, length); break;
+ case 0x720: cta_ifdb(x, length); break;
+ case 0x734: cta_displayid_type_7(x, length); break;
+ case 0x735: cta_displayid_type_8(x, length); break;
+ case 0x742: cta_displayid_type_10(x, length); break;
+ case 0x778:
+ cta_hf_eeodb(x, length);
// This must be the first CTA-861 block
- if (!cta.first_block)
+ if (cta.block_number > 0)
fail("Block starts at a wrong offset.\n");
- return;
- case 0x79:
- if (!cta.last_block_was_hdmi_vsdb)
+ break;
+ case 0x779:
+ if (cta.previous_cta_tag != (0x03|kOUI_HDMI))
fail("HDMI Forum SCDB did not immediately follow the HDMI VSDB.\n");
if (cta.have_hf_scdb || cta.have_hf_vsdb)
fail("Duplicate HDMI Forum VSDB/SCDB.\n");
if (length < 2) {
data_block = std::string("HDMI Forum SCDB");
fail("Invalid length %u < 2.\n", length);
- return;
- }
- if (x[1] || x[2])
- printf(" Non-zero SCDB reserved fields!\n");
- cta_hf_scdb(x + 3, length - 2);
- cta.have_hf_scdb = 1;
- return;
- }
-
- hex_block(" ", x + 1, length);
-}
-
-void edid_state::cta_block(const unsigned char *x, bool duplicate)
-{
- unsigned length = x[0] & 0x1f;
- const char *name;
- unsigned oui;
- bool reverse = false;
- bool audio_block = false;
-
- switch ((x[0] & 0xe0) >> 5) {
- case 0x01:
- data_block = "Audio Data Block";
- printf(" %s:\n", data_block.c_str());
- cta_audio_block(x + 1, length);
- audio_block = true;
- break;
- case 0x02:
- data_block = "Video Data Block";
- printf(" %s:\n", data_block.c_str());
- cta_svd(x + 1, length, false);
- break;
- case 0x03:
- oui = (x[3] << 16) + (x[2] << 8) + x[1];
- name = oui_name(oui);
- if (!name) {
- name = oui_name(oui, true);
- if (name)
- reverse = true;
- }
- if (!name) {
- printf(" Vendor-Specific Data Block, OUI %s:\n", ouitohex(oui).c_str());
- hex_block(" ", x + 4, length - 3);
- data_block.clear();
- warn("Unknown Vendor-Specific Data Block, OUI %s.\n",
- ouitohex(oui).c_str());
- return;
- }
- data_block = std::string("Vendor-Specific Data Block (") + name + ")";
- if (reverse)
- fail((std::string("OUI ") + ouitohex(oui) + " is in the wrong byte order\n").c_str());
- printf(" %s, OUI %s:\n", data_block.c_str(), ouitohex(oui).c_str());
- if (oui == 0x000c03) {
- cta_hdmi_block(x + 1, length);
- cta.last_block_was_hdmi_vsdb = 1;
- cta.first_block = 0;
- // The HDMI OUI is present, so this EDID represents an HDMI
- // interface. And HDMI interfaces must use EDID version 1.3
- // according to the HDMI Specification, so check for this.
- if (base.edid_minor != 3)
- fail("The HDMI Specification requires EDID 1.3 instead of 1.%u.\n",
- base.edid_minor);
- return;
- }
- if (oui == 0xc45dd8) {
- if (!cta.last_block_was_hdmi_vsdb)
- fail("HDMI Forum VSDB did not immediately follow the HDMI VSDB.\n");
- if (cta.have_hf_scdb || cta.have_hf_vsdb)
- fail("Duplicate HDMI Forum VSDB/SCDB.\n");
- cta_hf_scdb(x + 4, length - 3);
- cta.have_hf_vsdb = 1;
- break;
- }
- if (oui == 0x00001a) {
- cta_amd(x + 4, length - 3);
- break;
- }
- if (oui == 0xca125c && length == 0x15) {
- cta_microsoft(x + 4, length - 3);
break;
}
- hex_block(" ", x + 4, length - 3);
- break;
- case 0x04:
- data_block = "Speaker Allocation Data Block";
- printf(" %s:\n", data_block.c_str());
- cta_sadb(x + 1, length);
- audio_block = true;
- if (duplicate)
- fail("Only one instance of this Data Block is allowed.\n");
- break;
- case 0x05:
- data_block = "VESA Display Transfer Characteristics Data Block";
- printf(" %s:\n", data_block.c_str());
- cta_vesa_dtcdb(x + 1, length);
- if (duplicate)
- fail("Only one instance of this Data Block is allowed.\n");
- break;
- case 0x07:
- cta_ext_block(x + 1, length - 1, duplicate);
+ if (x[0] || x[1])
+ printf(" Non-zero SCDB reserved fields!\n");
+ cta_hf_scdb(x + 2, length - 2);
+ cta.have_hf_scdb = true;
break;
- default: {
- unsigned tag = (*x & 0xe0) >> 5;
- unsigned length = *x & 0x1f;
-
- printf(" Unknown CTA-861 tag 0x%02x, length %u\n", tag, length);
- hex_block(" ", x + 1, length);
- data_block.clear();
- warn("Unknown CTA-861 Data Block %u.\n", tag);
+dodefault:
+ default:
+ hex_block(" ", x, length);
break;
}
- }
- // See Table 52 of CTA-861-G for a description of Byte 3
- if (audio_block && !(cta.byte3 & 0x40))
- fail("audio information is present, but bit 6 of Byte 3 of the CTA-861 Extension header indicates no Basic Audio support.\n");
- cta.first_block = 0;
- cta.last_block_was_hdmi_vsdb = 0;
+ cta.block_number++;
+ cta.previous_cta_tag = tag;
+ found_tags.push_back(tag);
}
void edid_state::preparse_cta_block(const unsigned char *x)
@@ -2428,11 +2327,11 @@ void edid_state::parse_cta_block(const unsigned char *x)
// msg(!cta.has_hdmi, "If YCbCr support is indicated, then both 4:2:2 and 4:4:4 %s be supported.\n",
// cta.has_hdmi ? "shall" : "should");
printf(" Native detailed modes: %u\n", x[3] & 0x0f);
- if (cta.first_block)
+ if (cta.block_number == 0)
cta.byte3 = x[3];
else if (x[3] != cta.byte3)
fail("Byte 3 must be the same for all CTA-861 Extension Blocks.\n");
- if (cta.first_block) {
+ if (cta.block_number == 0) {
unsigned native_dtds = x[3] & 0x0f;
cta.native_timings.clear();
@@ -2458,15 +2357,7 @@ void edid_state::parse_cta_block(const unsigned char *x)
unsigned i;
for (i = 4; i < offset; i += (x[i] & 0x1f) + 1) {
- unsigned tag = (x[i] & 0xe0) << 3;
-
- if (tag == 0x700)
- tag |= x[i + 1];
- bool duplicate = cta.found_tags.find(tag) != cta.found_tags.end();
-
- cta_block(x + i, duplicate);
- if (!duplicate)
- cta.found_tags.insert(tag);
+ cta_block(x + i, cta.found_tags);
}
data_block.clear();
diff --git a/parse-displayid-block.cpp b/parse-displayid-block.cpp
index 5c81294..ad5099f 100644
--- a/parse-displayid-block.cpp
+++ b/parse-displayid-block.cpp
@@ -81,12 +81,6 @@ void edid_state::parse_displayid_product_id(const unsigned char *x)
check_displayid_datablock_revision(x[1]);
dispid.has_product_identification = true;
- if (dispid.version >= 0x20) {
- unsigned oui = (x[3] << 16) | (x[4] << 8) | x[5];
- printf(" Vendor OUI %s\n", ouitohex(oui).c_str());
- } else {
- printf(" Vendor ID: %c%c%c\n", x[3], x[4], x[5]);
- }
printf(" Product Code: %u\n", x[6] | (x[7] << 8));
unsigned sn = x[8] | (x[9] << 8) | (x[10] << 16) | (x[11] << 24);
if (sn) {
@@ -136,6 +130,26 @@ static void print_flag_lines(const char *indent, const char *label,
}
}
+void edid_state::set_displayid_native_res(unsigned w, unsigned h)
+{
+ if (dispid.native_width &&
+ (dispid.native_width != w || dispid.native_height != h)) {
+ fail("Native resolution mismatch: %ux%u -> %ux%u.\n",
+ dispid.native_width, dispid.native_height, w, h);
+ return;
+ }
+
+ if (!w && !h)
+ return;
+
+ if (!w ^ !h) {
+ fail("Invalid Native Pixel Format %ux%u.\n", w, h);
+ } else {
+ dispid.native_width = w;
+ dispid.native_height = h;
+ }
+}
+
void edid_state::parse_displayid_parameters(const unsigned char *x)
{
check_displayid_datablock_revision(x[1]);
@@ -147,8 +161,10 @@ void edid_state::parse_displayid_parameters(const unsigned char *x)
printf(" Image size: %.1f mm x %.1f mm\n",
((x[4] << 8) + x[3]) / 10.0,
((x[6] << 8) + x[5]) / 10.0);
- printf(" Pixels: %d x %d\n",
- (x[8] << 8) + x[7], (x[10] << 8) + x[9]);
+ unsigned w = (x[8] << 8) + x[7];
+ unsigned h = (x[10] << 8) + x[9];
+ printf(" Display native pixel format: %ux%u\n", w, h);
+ set_displayid_native_res(w, h);
print_flag_lines(" ", " Feature support flags:",
x[11], feature_support_flags);
@@ -597,13 +613,12 @@ void edid_state::parse_displayid_display_device(const unsigned char *x)
printf(" The backlight's intensity can be controlled\n");
unsigned w = x[5] | (x[6] << 8);
unsigned h = x[7] | (x[8] << 8);
- if (w && h) {
- printf(" Display native pixel format: %ux%u\n", w + 1, h + 1);
- dispid.native_width = w + 1;
- dispid.native_height = h + 1;
- } else if (w || h) {
- fail("Invalid Native Pixel Format %ux%u.\n", w, h);
- }
+
+ if (w) w++;
+ if (h) h++;
+
+ printf(" Display native pixel format: %ux%u\n", w, h);
+ set_displayid_native_res(w, h);
printf(" Aspect ratio and orientation:\n");
printf(" Aspect Ratio: %.2f\n", (100 + x[9]) / 100.0);
unsigned char v = x[0x0a];
@@ -1128,13 +1143,10 @@ void edid_state::parse_displayid_parameters_v2(const unsigned char *x,
hor_size / 10.0, vert_size / 10.0);
unsigned w = (x[8] << 8) + x[7];
unsigned h = (x[10] << 8) + x[9];
- if (w && h) {
- printf(" Native Format: %ux%u\n", w, h);
- dispid.native_width = w;
- dispid.native_height = h;
- } else if (w || h) {
- fail("Invalid Native Format %ux%u.\n", w, h);
- }
+
+ printf(" Display native pixel format: %ux%u\n", w, h);
+ set_displayid_native_res(w, h);
+
unsigned char v = x[11];
printf(" Scan Orientation: ");
switch (v & 0x07) {
@@ -1368,6 +1380,48 @@ void edid_state::parse_displayid_ContainerID(const unsigned char *x)
}
}
+// tag 0x2b
+
+void edid_state::parse_displayid_adaptive_sync(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1], 0x70);
+
+ unsigned size = 6 + ((x[1] >> 4) & 0x7);
+ unsigned len = x[2];
+ unsigned descriptor = 1;
+
+ x += 3;
+ if (len % size)
+ fail("DisplayID payload length %u is not a multiple of %u.\n", len, size);
+ while (len >= size) {
+ printf(" Descriptor #%u:\n", descriptor++);
+
+ printf(" %sNative Panel Range\n", (x[0] & 1) ? "" : "Non-");
+ unsigned v = (x[0] >> 2) & 3;
+ switch (v) {
+ case 0: printf(" Fixed Average V-Total\n"); break;
+ case 1: printf(" Fixed Average V-Total and Adaptive V-Total\n"); break;
+ default:
+ printf(" Reserved %u\n", v);
+ fail("Use of reserved value %u.\n", v);
+ break;
+ }
+ if (x[0] & 0x10)
+ printf(" Supports Seamless Transition\n");
+ if (x[0] & 0x02)
+ printf(" 'Max Single Frame Duration Increase' field value without jitter impact\n");
+ if (x[0] & 0x20)
+ printf(" 'Max Single Frame Duration Decrease' field value without jitter impact\n");
+ printf(" Max Duration Increase: %.2f ms\n", x[1] / 4.0);
+ printf(" Max Duration Decrease: %.2f ms\n", x[5] / 4.0);
+ printf(" Min Refresh Rate: %u Hz\n", x[2]);
+ printf(" Max Refresh Rate: %u Hz\n", 1 + x[3] + (x[4] & 3) * 256);
+
+ len -= size;
+ x += size;
+ }
+}
+
// tag 0x32
void edid_state::parse_displayid_type_10_timing(const unsigned char *x,
@@ -1423,24 +1477,24 @@ void edid_state::parse_displayid_type_10_timing(const unsigned char *x,
s += ", YCbCr 4:2:0";
unsigned refresh = 1 + x[5] + (sz == 6 ? 0 : ((x[6] & 3) << 8));
- double add_vert_time = 0;
if (sz > 6 && rb == RB_CVT_V3) {
unsigned delta_hblank = (x[6] >> 2) & 7;
- if (rb_h_blank == 80)
+ if ((x[6] >> 5) & 7)
+ fail("Bits 5-7 of byte 6 must be 0.\n");
+ if (rb != RB_CVT_V3) {
+ if ((x[6] >> 2) & 7)
+ fail("Bits 2-4 of byte 6 must be 0.\n");
+ } else if (rb_h_blank == 80)
rb_h_blank = 80 + 8 * delta_hblank;
else if (delta_hblank <= 5)
rb_h_blank = 160 + 8 * delta_hblank;
else
rb_h_blank = 160 - (delta_hblank - 5) * 8;
-
- unsigned vblank_time_perc = (x[6] >> 5) & 7;
-
- add_vert_time = (vblank_time_perc * 10000.0) / refresh;
}
- edid_cvt_mode(refresh, t, rb_h_blank, add_vert_time);
+ edid_cvt_mode(refresh, t, rb_h_blank);
print_timings(" ", &t, "CVT", s.c_str());
if (is_cta) {
@@ -1514,15 +1568,7 @@ void edid_state::parse_displayid_cta_data_block(const unsigned char *x)
x += 3;
for (i = 0; i < len; i += (x[i] & 0x1f) + 1) {
- unsigned tag = (x[i] & 0xe0) << 3;
-
- if (tag == 0x700)
- tag |= x[i + 1];
- bool duplicate = dispid.found_tags.find(tag) != dispid.found_tags.end();
-
- cta_block(x + i, duplicate);
- if (!duplicate)
- dispid.found_tags.insert(tag);
+ cta_block(x + i, dispid.found_tags);
}
if (i != len)
@@ -1609,13 +1655,265 @@ void edid_state::preparse_displayid_block(const unsigned char *x)
}
}
+unsigned edid_state::displayid_block(const unsigned version, const unsigned char *x, unsigned length)
+{
+ unsigned i;
+ unsigned tag = x[0];
+ unsigned tag_version = (tag < 0x20) ? 1 : (tag < 0x7f) ? 2 : (tag < 0x80) ? 1 : 0;
+ bool dooutputname = true;
+ unsigned len = (length < 3) ? 0 : x[2];
+ bool hasoui = false;
+ unsigned ouinum;
+
+ switch (tag) {
+ // DisplayID 1.3:
+ case 0x00:
+ data_block_oui("Product Identification Data Block (" + utohex(tag) + ")",
+ x + 3, len, &ouinum, true, true, true);
+ dooutputname = false;
+ hasoui = true;
+ break;
+ case 0x01: data_block = "Display Parameters Data Block (" + utohex(tag) + ")"; break;
+ case 0x02: data_block = "Color Characteristics Data Block"; break;
+ case 0x03: data_block = "Video Timing Modes Type 1 - Detailed Timings Data Block"; break;
+ case 0x04: data_block = "Video Timing Modes Type 2 - Detailed Timings Data Block"; break;
+ case 0x05: data_block = "Video Timing Modes Type 3 - Short Timings Data Block"; break;
+ case 0x06: data_block = "Video Timing Modes Type 4 - DMT Timings Data Block"; break;
+ case 0x07: data_block = "Supported Timing Modes Type 1 - VESA DMT Timings Data Block"; break;
+ case 0x08: data_block = "Supported Timing Modes Type 2 - CTA-861 Timings Data Block"; break;
+ case 0x09: data_block = "Video Timing Range Data Block"; break;
+ case 0x0a: data_block = "Product Serial Number Data Block"; break;
+ case 0x0b: data_block = "GP ASCII String Data Block"; break;
+ case 0x0c: data_block = "Display Device Data Data Block"; break;
+ case 0x0d: data_block = "Interface Power Sequencing Data Block"; break;
+ case 0x0e: data_block = "Transfer Characteristics Data Block"; break;
+ case 0x0f: data_block = "Display Interface Data Block"; break;
+ case 0x10: data_block = "Stereo Display Interface Data Block (" + utohex(tag) + ")"; break;
+ case 0x11: data_block = "Video Timing Modes Type 5 - Short Timings Data Block"; break;
+ case 0x12: data_block = "Tiled Display Topology Data Block (" + utohex(tag) + ")"; break;
+ case 0x13: data_block = "Video Timing Modes Type 6 - Detailed Timings Data Block"; break;
+ // 0x14 .. 0x7e RESERVED for Additional VESA-defined Data Blocks
+ // DisplayID 2.0
+ case 0x20:
+ data_block_oui("Product Identification Data Block (" + utohex(tag) + ")",
+ x + 3, len, &ouinum, false, false, true);
+ dooutputname = false;
+ hasoui = true;
+ break;
+ case 0x21: data_block = "Display Parameters Data Block (" + utohex(tag) + ")"; break;
+ case 0x22: data_block = "Video Timing Modes Type 7 - Detailed Timings Data Block"; break;
+ case 0x23: data_block = "Video Timing Modes Type 8 - Enumerated Timing Codes Data Block"; break;
+ case 0x24: data_block = "Video Timing Modes Type 9 - Formula-based Timings Data Block"; break;
+ case 0x25: data_block = "Dynamic Video Timing Range Limits Data Block"; break;
+ case 0x26: data_block = "Display Interface Features Data Block"; break;
+ case 0x27: data_block = "Stereo Display Interface Data Block (" + utohex(tag) + ")"; break;
+ case 0x28: data_block = "Tiled Display Topology Data Block (" + utohex(tag) + ")"; break;
+ case 0x29: data_block = "ContainerID Data Block"; break;
+ case 0x2b: data_block = "Adaptive Sync Data Block"; break;
+ case 0x32: data_block = "Video Timing Modes Type 10 - Formula-based Timings Data Block"; break;
+ // 0x2a .. 0x7d RESERVED for Additional VESA-defined Data Blocks
+ case 0x7e: // DisplayID 2.0
+ data_block_oui("Vendor-Specific Data Block (" + utohex(tag) + ")",
+ x + 3, len, &ouinum, false, false, true);
+ dooutputname = false;
+ hasoui = true;
+ tag |= ouinum;
+ break;
+ case 0x7f: // DisplayID 1.3
+ data_block_oui("Vendor-Specific Data Block (" + utohex(tag) + ")",
+ x + 3, len, &ouinum, false, true, true);
+ dooutputname = false;
+ hasoui = true;
+ tag |= ouinum;
+ break;
+ // 0x80 RESERVED
+ case 0x81: data_block = "CTA-861 DisplayID Data Block"; break;
+ // 0x82 .. 0xff RESERVED
+ default: data_block = "Unknown DisplayID Data Block (" + utohex(tag) + ", length " + std::to_string(len) + ")"; break;
+ }
+
+ if (length < 3) {
+ // Report a problem when the remaining bytes are not 0.
+ data_block.clear(); // Probably not a Data Block so clear this.
+ if (tag || (length > 1 && x[1])) {
+ printf(" Filler:\n");
+ fail("Not enough bytes remain (%d) for a DisplayID data block and the DisplayID filler is non-0.\n", length);
+ hex_block(" ", x, length);
+ }
+ return length;
+ }
+
+ if (length < len + 3) {
+ data_block.clear(); // Probably not a Data Block so clear this.
+ printf(" Filler:\n");
+ fail("The length of this DisplayID data block (%d) exceeds the number of bytes remaining (%d).\n", len + 3, length);
+ hex_block(" ", x, length);
+ return length;
+ }
+
+ if (!tag && !len) {
+ // A Product Identification Data Block with no payload bytes is not valid - assume this is the end.
+ data_block.clear(); // Probably not a Product Identification Data Block so clear this.
+ if (!memchk(x, length)) {
+ printf(" Filler:\n");
+ fail("Non-0 filler bytes in the DisplayID block.\n");
+ hex_block(" ", x, length);
+ }
+ return length;
+ }
+
+ if (dooutputname && data_block.length())
+ printf(" %s:\n", data_block.c_str());
+
+ if (version >= 0x20 && tag_version == 1)
+ fail("Use of DisplayID v1.x tag for DisplayID v%u.%u.\n",
+ version >> 4, version & 0xf);
+ if (version < 0x20 && tag_version == 2)
+ fail("Use of DisplayID v2.0 tag for DisplayID v%u.%u.\n",
+ version >> 4, version & 0xf);
+
+ unsigned block_rev = x[1] & 0x07;
+
+ switch (tag) {
+ case 0x00: parse_displayid_product_id(x); break;
+ case 0x01: parse_displayid_parameters(x); break;
+ case 0x02: parse_displayid_color_characteristics(x); break;
+ case 0x03:
+ check_displayid_datablock_revision(x[1], 0, block_rev & 1);
+ for (i = 0; i < len / 20; i++)
+ parse_displayid_type_1_7_timing(&x[3 + (i * 20)], false, block_rev);
+ break;
+ case 0x04:
+ check_displayid_datablock_revision(x[1]);
+ for (i = 0; i < len / 11; i++)
+ parse_displayid_type_2_timing(&x[3 + (i * 11)]);
+ break;
+ case 0x05:
+ check_displayid_datablock_revision(x[1], 0, block_rev & 1);
+ for (i = 0; i < len / 3; i++)
+ parse_displayid_type_3_timing(&x[3 + (i * 3)]);
+ break;
+ case 0x06:
+ check_displayid_datablock_revision(x[1], 0xc0, 1);
+ for (i = 0; i < len; i++)
+ parse_displayid_type_4_8_timing((x[1] & 0xc0) >> 6, x[3 + i]);
+ break;
+ case 0x07:
+ check_displayid_datablock_revision(x[1]);
+ for (i = 0; i < min(len, 10) * 8; i++)
+ if (x[3 + i / 8] & (1 << (i % 8))) {
+ char type[16];
+ sprintf(type, "DMT 0x%02x", i + 1);
+ print_timings(" ", find_dmt_id(i + 1), type);
+ }
+ break;
+ case 0x08:
+ check_displayid_datablock_revision(x[1]);
+ for (i = 0; i < min(len, 8) * 8; i++)
+ if (x[3 + i / 8] & (1 << (i % 8))) {
+ char type[16];
+ sprintf(type, "VIC %3u", i + 1);
+ print_timings(" ", find_vic_id(i + 1), type);
+ }
+ break;
+ case 0x09: parse_displayid_video_timing_range_limits(x); break;
+ case 0x0a:
+ case 0x0b: parse_displayid_string(x); break;
+ case 0x0c: parse_displayid_display_device(x); break;
+ case 0x0d: parse_displayid_intf_power_sequencing(x); break;
+ case 0x0e: parse_displayid_transfer_characteristics(x); break;
+ case 0x0f: parse_displayid_display_intf(x); break;
+ case 0x10: parse_displayid_stereo_display_intf(x); break;
+ case 0x11:
+ check_displayid_datablock_revision(x[1]);
+ for (i = 0; i < len / 7; i++)
+ parse_displayid_type_5_timing(&x[3 + (i * 7)]);
+ break;
+ case 0x12: parse_displayid_tiled_display_topology(x, false); break;
+ case 0x13:
+ check_displayid_datablock_revision(x[1]);
+ for (i = 0; i < len; i += (x[3 + i + 2] & 0x40) ? 17 : 14)
+ parse_displayid_type_6_timing(&x[3 + i]);
+ break;
+ case 0x20: parse_displayid_product_id(x); break;
+ case 0x21:
+ if (block_rev >= 1)
+ check_displayid_datablock_revision(x[1], 0x80, 1);
+ else
+ check_displayid_datablock_revision(x[1], 0x80, 0);
+ parse_displayid_parameters_v2(x, block_rev);
+ break;
+ case 0x22: {
+ unsigned sz = 20;
+
+ if (block_rev >= 2)
+ check_displayid_datablock_revision(x[1], 0x08, 2);
+ else if (block_rev == 1)
+ check_displayid_datablock_revision(x[1], 0x08, 1);
+ else
+ check_displayid_datablock_revision(x[1]);
+ sz += (x[1] & 0x70) >> 4;
+ if (block_rev >= 1 && (x[1] & 0x08))
+ printf(" These timings support DSC pass-through\n");
+ for (i = 0; i < len / sz; i++)
+ parse_displayid_type_1_7_timing(&x[3 + i * sz], true, block_rev);
+ break;
+ }
+ case 0x23:
+ if (block_rev)
+ check_displayid_datablock_revision(x[1], 0xe8, 1);
+ else
+ check_displayid_datablock_revision(x[1], 0xc8);
+ if (x[1] & 0x08) {
+ for (i = 0; i < len / 2; i++)
+ parse_displayid_type_4_8_timing((x[1] & 0xc0) >> 6,
+ x[3 + i * 2] |
+ (x[4 + i * 2] << 8));
+ } else {
+ for (i = 0; i < len; i++)
+ parse_displayid_type_4_8_timing((x[1] & 0xc0) >> 6,
+ x[3 + i]);
+ }
+ break;
+ case 0x24:
+ check_displayid_datablock_revision(x[1]);
+ for (i = 0; i < len / 6; i++)
+ parse_displayid_type_9_timing(&x[3 + i * 6]);
+ break;
+ case 0x25: parse_displayid_dynamic_video_timings_range_limits(x); break;
+ case 0x26: parse_displayid_interface_features(x); break;
+ case 0x27: parse_displayid_stereo_display_intf(x); break;
+ case 0x28: parse_displayid_tiled_display_topology(x, true); break;
+ case 0x29: parse_displayid_ContainerID(x); break;
+ case 0x2b: parse_displayid_adaptive_sync(x); break;
+ case 0x32: {
+ unsigned sz = 6 + ((x[1] & 0x70) >> 4);
+
+ check_displayid_datablock_revision(x[1], 0x70);
+ for (i = 0; i < len / sz; i++)
+ parse_displayid_type_10_timing(&x[3 + i * sz], sz);
+ break;
+ }
+ case 0x7e|kOUI_VESA: parse_displayid_vesa(x); break;
+ case 0x81: parse_displayid_cta_data_block(x); break;
+ default: hex_block(" ", x + 3 + (hasoui ? 3 : 0), (len > (hasoui ? 3 : 0)) ? len - (hasoui ? 3 : 0) : 0); break;
+ }
+
+ if ((tag == 0x00 || tag == 0x20) &&
+ (!dispid.is_base_block || dispid.block_number > 0))
+ fail("%s is required to be the first DisplayID Data Block.\n",
+ data_block.c_str());
+
+ dispid.block_number++;
+ return len + 3;
+}
+
void edid_state::parse_displayid_block(const unsigned char *x)
{
unsigned version = x[1];
unsigned length = x[2];
unsigned prod_type = x[3]; // future check: based on type, check for required data blocks
unsigned ext_count = x[4];
- unsigned i;
printf(" Version: %u.%u\n Extension Count: %u\n",
version >> 4, version & 0xf, ext_count);
@@ -1647,246 +1945,10 @@ void edid_state::parse_displayid_block(const unsigned char *x)
length = 121;
}
- unsigned offset = 5;
- bool first_data_block = true;
- while (length > 0) {
- unsigned tag = x[offset];
- unsigned oui = 0;
-
- switch (tag) {
- // DisplayID 1.3:
- case 0x00: data_block = "Product Identification Data Block (" + utohex(tag) + ")"; break;
- case 0x01: data_block = "Display Parameters Data Block (" + utohex(tag) + ")"; break;
- case 0x02: data_block = "Color Characteristics Data Block"; break;
- case 0x03: data_block = "Video Timing Modes Type 1 - Detailed Timings Data Block"; break;
- case 0x04: data_block = "Video Timing Modes Type 2 - Detailed Timings Data Block"; break;
- case 0x05: data_block = "Video Timing Modes Type 3 - Short Timings Data Block"; break;
- case 0x06: data_block = "Video Timing Modes Type 4 - DMT Timings Data Block"; break;
- case 0x07: data_block = "Supported Timing Modes Type 1 - VESA DMT Timings Data Block"; break;
- case 0x08: data_block = "Supported Timing Modes Type 2 - CTA-861 Timings Data Block"; break;
- case 0x09: data_block = "Video Timing Range Data Block"; break;
- case 0x0a: data_block = "Product Serial Number Data Block"; break;
- case 0x0b: data_block = "GP ASCII String Data Block"; break;
- case 0x0c: data_block = "Display Device Data Data Block"; break;
- case 0x0d: data_block = "Interface Power Sequencing Data Block"; break;
- case 0x0e: data_block = "Transfer Characteristics Data Block"; break;
- case 0x0f: data_block = "Display Interface Data Block"; break;
- case 0x10: data_block = "Stereo Display Interface Data Block (" + utohex(tag) + ")"; break;
- case 0x11: data_block = "Video Timing Modes Type 5 - Short Timings Data Block"; break;
- case 0x12: data_block = "Tiled Display Topology Data Block (" + utohex(tag) + ")"; break;
- case 0x13: data_block = "Video Timing Modes Type 6 - Detailed Timings Data Block"; break;
- // 0x14 .. 0x7e RESERVED for Additional VESA-defined Data Blocks
- // DisplayID 2.0
- case 0x20: data_block = "Product Identification Data Block (" + utohex(tag) + ")"; break;
- case 0x21: data_block = "Display Parameters Data Block (" + utohex(tag) + ")"; break;
- case 0x22: data_block = "Video Timing Modes Type 7 - Detailed Timings Data Block"; break;
- case 0x23: data_block = "Video Timing Modes Type 8 - Enumerated Timing Codes Data Block"; break;
- case 0x24: data_block = "Video Timing Modes Type 9 - Formula-based Timings Data Block"; break;
- case 0x25: data_block = "Dynamic Video Timing Range Limits Data Block"; break;
- case 0x26: data_block = "Display Interface Features Data Block"; break;
- case 0x27: data_block = "Stereo Display Interface Data Block (" + utohex(tag) + ")"; break;
- case 0x28: data_block = "Tiled Display Topology Data Block (" + utohex(tag) + ")"; break;
- case 0x29: data_block = "ContainerID Data Block"; break;
- case 0x32: data_block = "Video Timing Modes Type 10 - Formula-based Timings Data Block"; break;
- // 0x2a .. 0x7d RESERVED for Additional VESA-defined Data Blocks
- case 0x7e: // DisplayID 2.0
- case 0x7f: // DisplayID 1.3
- if ((tag == 0x7e && version >= 0x20) ||
- (tag == 0x7f && version < 0x20)) {
- oui = (x[offset + 3] << 16) + (x[offset + 4] << 8) + x[offset + 5];
- const char *name = oui_name(oui);
- bool reversed = false;
-
- if (!name) {
- name = oui_name(oui, true);
- if (name)
- reversed = true;
- }
- if (name)
- data_block = std::string("Vendor-Specific Data Block (") + name + ")";
- else
- data_block = "Vendor-Specific Data Block, OUI " + ouitohex(oui);
- if (reversed)
- fail((std::string("OUI ") + ouitohex(oui) + " is in the wrong byte order.\n").c_str());
- } else {
- data_block = "Unknown DisplayID Data Block (" + utohex(tag) + ")";
- }
- break;
- // 0x80 RESERVED
- case 0x81: data_block = "CTA-861 DisplayID Data Block (" + utohex(tag) + ")"; break;
- // 0x82 .. 0xff RESERVED
- default: data_block = "Unknown DisplayID Data Block (" + utohex(tag) + ")"; break;
- }
-
- if (version >= 0x20 && (tag < 0x20 || tag == 0x7f))
- fail("Use of DisplayID v1.x tag for DisplayID v%u.%u.\n",
- version >> 4, version & 0xf);
- if (version < 0x20 && tag >= 0x20 && tag <= 0x7e)
- fail("Use of DisplayID v2.0 tag for DisplayID v%u.%u.\n",
- version >> 4, version & 0xf);
-
- if (length < 3) {
- // report a problem when the remaining bytes are not 0.
- if (tag || x[offset + 1]) {
- fail("Not enough bytes remain (%d) for a DisplayID data block or the DisplayID filler is non-0.\n", length);
- }
- break;
- }
-
- unsigned block_rev = x[offset + 1] & 0x07;
- unsigned len = x[offset + 2];
-
- if (length < len + 3) {
- fail("The length of this DisplayID data block (%d) exceeds the number of bytes remaining (%d).\n", len + 3, length);
- break;
- }
-
- if (!tag && !len) {
- // A Product Identification Data Block with no payload bytes is not valid - assume this is the end.
- if (!memchk(x + offset, length)) {
- fail("Non-0 filler bytes in the DisplayID block.\n");
- }
- break;
- }
-
- printf(" %s:\n", data_block.c_str());
-
- switch (tag) {
- case 0x00: parse_displayid_product_id(x + offset); break;
- case 0x01: parse_displayid_parameters(x + offset); break;
- case 0x02: parse_displayid_color_characteristics(x + offset); break;
- case 0x03:
- check_displayid_datablock_revision(x[offset + 1], 0, block_rev & 1);
- for (i = 0; i < len / 20; i++)
- parse_displayid_type_1_7_timing(&x[offset + 3 + (i * 20)], false, block_rev);
- break;
- case 0x04:
- check_displayid_datablock_revision(x[offset + 1]);
- for (i = 0; i < len / 11; i++)
- parse_displayid_type_2_timing(&x[offset + 3 + (i * 11)]);
- break;
- case 0x05:
- check_displayid_datablock_revision(x[offset + 1], 0, block_rev & 1);
- for (i = 0; i < len / 3; i++)
- parse_displayid_type_3_timing(&x[offset + 3 + (i * 3)]);
- break;
- case 0x06:
- check_displayid_datablock_revision(x[offset + 1], 0xc0, 1);
- for (i = 0; i < len; i++)
- parse_displayid_type_4_8_timing((x[offset + 1] & 0xc0) >> 6, x[offset + 3 + i]);
- break;
- case 0x07:
- check_displayid_datablock_revision(x[offset + 1]);
- for (i = 0; i < min(len, 10) * 8; i++)
- if (x[offset + 3 + i / 8] & (1 << (i % 8))) {
- char type[16];
- sprintf(type, "DMT 0x%02x", i + 1);
- print_timings(" ", find_dmt_id(i + 1), type);
- }
- break;
- case 0x08:
- check_displayid_datablock_revision(x[offset + 1]);
- for (i = 0; i < min(len, 8) * 8; i++)
- if (x[offset + 3 + i / 8] & (1 << (i % 8))) {
- char type[16];
- sprintf(type, "VIC %3u", i + 1);
- print_timings(" ", find_vic_id(i + 1), type);
- }
- break;
- case 0x09: parse_displayid_video_timing_range_limits(x + offset); break;
- case 0x0a:
- case 0x0b: parse_displayid_string(x + offset); break;
- case 0x0c: parse_displayid_display_device(x + offset); break;
- case 0x0d: parse_displayid_intf_power_sequencing(x + offset); break;
- case 0x0e: parse_displayid_transfer_characteristics(x + offset); break;
- case 0x0f: parse_displayid_display_intf(x + offset); break;
- case 0x10: parse_displayid_stereo_display_intf(x + offset); break;
- case 0x11:
- check_displayid_datablock_revision(x[offset + 1]);
- for (i = 0; i < len / 7; i++)
- parse_displayid_type_5_timing(&x[offset + 3 + (i * 7)]);
- break;
- case 0x12: parse_displayid_tiled_display_topology(x + offset, false); break;
- case 0x13:
- check_displayid_datablock_revision(x[offset + 1]);
- for (i = 0; i < len; i += (x[offset + 3 + i + 2] & 0x40) ? 17 : 14)
- parse_displayid_type_6_timing(&x[offset + 3 + i]);
- break;
- case 0x20: parse_displayid_product_id(x + offset); break;
- case 0x21:
- if (block_rev >= 1)
- check_displayid_datablock_revision(x[offset + 1], 0x80, 1);
- else
- check_displayid_datablock_revision(x[offset + 1], 0x80, 0);
- parse_displayid_parameters_v2(x + offset, block_rev);
- break;
- case 0x22: {
- unsigned sz = 20;
-
- if (block_rev >= 2)
- check_displayid_datablock_revision(x[offset + 1], 0x08, 2);
- else if (block_rev == 1)
- check_displayid_datablock_revision(x[offset + 1], 0x08, 1);
- else
- check_displayid_datablock_revision(x[offset + 1]);
- sz += (x[offset + 1] & 0x70) >> 4;
- if (block_rev >= 1 && (x[offset + 1] & 0x08))
- printf(" These timings support DSC pass-through\n");
- for (i = 0; i < len / sz; i++)
- parse_displayid_type_1_7_timing(&x[offset + 3 + i * sz], true, block_rev);
- break;
- }
- case 0x23:
- if (block_rev)
- check_displayid_datablock_revision(x[offset + 1], 0xe8, 1);
- else
- check_displayid_datablock_revision(x[offset + 1], 0xc8);
- if (x[offset + 1] & 0x08) {
- for (i = 0; i < len / 2; i++)
- parse_displayid_type_4_8_timing((x[offset + 1] & 0xc0) >> 6,
- x[offset + 3 + i * 2] |
- (x[offset + 4 + i * 2] << 8));
- } else {
- for (i = 0; i < len; i++)
- parse_displayid_type_4_8_timing((x[offset + 1] & 0xc0) >> 6,
- x[offset + 3 + i]);
- }
- break;
- case 0x24:
- check_displayid_datablock_revision(x[offset + 1]);
- for (i = 0; i < len / 6; i++)
- parse_displayid_type_9_timing(&x[offset + 3 + i * 6]);
- break;
- case 0x25: parse_displayid_dynamic_video_timings_range_limits(x + offset); break;
- case 0x26: parse_displayid_interface_features(x + offset); break;
- case 0x27: parse_displayid_stereo_display_intf(x + offset); break;
- case 0x28: parse_displayid_tiled_display_topology(x + offset, true); break;
- case 0x29: parse_displayid_ContainerID(x + offset); break;
- case 0x32: {
- unsigned sz = 6 + ((x[offset + 1] & 0x70) >> 4);
-
- check_displayid_datablock_revision(x[offset + 1], 0x70);
- for (i = 0; i < len / sz; i++)
- parse_displayid_type_10_timing(&x[offset + 3 + i * sz], sz);
- break;
- }
- case 0x81: parse_displayid_cta_data_block(x + offset); break;
- case 0x7e:
- if (oui == 0x3a0292) {
- parse_displayid_vesa(x + offset);
- break;
- }
- // fall-through
- default: hex_block(" ", x + offset + 3, len); break;
- }
-
- if ((tag == 0x00 || tag == 0x20) &&
- (!dispid.is_base_block || !first_data_block))
- fail("%s is required to be the first DisplayID Data Block.\n",
- data_block.c_str());
- length -= len + 3;
- offset += len + 3;
- first_data_block = false;
+ unsigned len;
+ for (const unsigned char *y = x + 5; length > 0; y += len) {
+ len = displayid_block(version, y, length);
+ length -= len;
}
/*

Privacy Policy