aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil-cisco@xs4all.nl>2020-07-09 13:58:39 +0200
committerHans Verkuil <hverkuil-cisco@xs4all.nl>2020-07-09 13:58:39 +0200
commit678184d408f6ba33b6b1a091d2e75a223739085a (patch)
tree1994fdddbe30defab6fcc2ab48451536d5387988
parent70dc804e101899320897b5f6f28b1d8c58a8bc28 (diff)
edid-decode: improve preferred/native timing handling
This adds support for multiple preferred and native timings, such as via the VFPDB. The sanity checks have been improved a lot as well. Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
-rw-r--r--edid-decode.cpp117
-rw-r--r--edid-decode.h17
-rw-r--r--parse-base-block.cpp31
-rw-r--r--parse-cta-block.cpp72
4 files changed, 162 insertions, 75 deletions
diff --git a/edid-decode.cpp b/edid-decode.cpp
index 222333d..ce18ffe 100644
--- a/edid-decode.cpp
+++ b/edid-decode.cpp
@@ -190,11 +190,11 @@ void calc_ratio(struct timings *t)
t->vratio = t->vact / d;
}
-std::string edid_state::dtd_type()
+std::string edid_state::dtd_type(unsigned cnt)
{
unsigned len = std::to_string(preparse_total_dtds).length();
char buf[16];
- sprintf(buf, "DTD %*u", len, dtd_cnt);
+ sprintf(buf, "DTD %*u", len, cnt);
return buf;
}
@@ -1122,34 +1122,103 @@ int edid_state::parse_edid()
msg(!fail || edid_minor >= 4, "%s", err.c_str());
}
- if (native_interlaced_timing.t.vact && !native_timing.t.vact)
- fail("A native interlaced timing is present, but not a native progressive timing.\n");
- if (native_interlaced_timing.t.vact && native_timing.t.vact &&
- native_interlaced_timing.t.vact != native_timing.t.vact)
- warn("The native interlaced frame height differs from the native progressive frame height.\n");
- if (native_timing.t.vact && preferred_timings.t.vact > native_timing.t.vact) {
- warn("Native resolution of %ux%u is smaller than the preferred resolution %ux%u.\n",
- native_timing.t.hact, native_timing.t.vact,
- preferred_timings.t.hact, preferred_timings.t.vact);
+ unsigned max_pref_prog_hact = 0;
+ unsigned max_pref_prog_vact = 0;
+ unsigned max_pref_ilace_hact = 0;
+ unsigned max_pref_ilace_vact = 0;
+
+ for (vec_timings_ext::iterator iter = preferred_timings.begin(); iter != preferred_timings.end(); ++iter) {
+ if (iter->t.hact >= 129 && !iter->t.vact) {
+ iter->flags = vec_dtds[iter->t.hact - 129].flags;
+ iter->t = vec_dtds[iter->t.hact - 129].t;
+ }
+ if (iter->t.interlaced &&
+ (iter->t.vact > max_pref_ilace_vact ||
+ (iter->t.vact == max_pref_ilace_vact && iter->t.hact >= max_pref_ilace_hact))) {
+ max_pref_ilace_hact = iter->t.hact;
+ max_pref_ilace_vact = iter->t.vact;
+ }
+ if (!iter->t.interlaced &&
+ (iter->t.vact > max_pref_prog_vact ||
+ (iter->t.vact == max_pref_prog_vact && iter->t.hact >= max_pref_prog_hact))) {
+ max_pref_prog_hact = iter->t.hact;
+ max_pref_prog_vact = iter->t.vact;
+ }
}
- if (options[OptPreferredTimings]) {
+ unsigned native_prog = 0;
+ unsigned native_prog_hact = 0;
+ unsigned native_prog_vact = 0;
+ bool native_prog_mixed_resolutions = false;
+ unsigned native_ilace = 0;
+ unsigned native_ilace_hact = 0;
+ unsigned native_ilace_vact = 0;
+ bool native_ilace_mixed_resolutions = false;
+
+ for (vec_timings_ext::iterator iter = native_timings.begin(); iter != native_timings.end(); ++iter) {
+ if (iter->t.hact >= 129 && !iter->t.vact) {
+ iter->flags = vec_dtds[iter->t.hact - 129].flags;
+ iter->t = vec_dtds[iter->t.hact - 129].t;
+ }
+ if (iter->t.interlaced) {
+ native_ilace++;
+ if (!native_ilace_hact) {
+ native_ilace_hact = iter->t.hact;
+ native_ilace_vact = iter->t.vact;
+ } else if (native_ilace_hact != iter->t.hact ||
+ native_ilace_vact != iter->t.vact) {
+ native_ilace_mixed_resolutions = true;
+ }
+ } else {
+ native_prog++;
+ if (!native_prog_hact) {
+ native_prog_hact = iter->t.hact;
+ native_prog_vact = iter->t.vact;
+ } else if (native_prog_hact != iter->t.hact ||
+ native_prog_vact != iter->t.vact) {
+ native_prog_mixed_resolutions = true;
+ }
+ }
+ }
+
+ if (native_prog_mixed_resolutions)
+ fail("Native progressive timings are a mix of several resolutions.\n");
+ if (native_ilace_mixed_resolutions)
+ fail("Native interlaced timings are a mix of several resolutions.\n");
+ if (native_ilace && !native_prog)
+ fail("A native interlaced timing is present, but not a native progressive timing.\n");
+ if (!native_prog_mixed_resolutions && native_prog > 1)
+ warn("Multiple native progressive timings are defined.\n");
+ if (!native_ilace_mixed_resolutions && native_ilace > 1)
+ warn("Multiple native interlaced timings are defined.\n");
+
+ if (!native_prog_mixed_resolutions && native_prog_vact &&
+ (max_pref_prog_vact > native_prog_vact ||
+ (max_pref_prog_vact == native_prog_vact && max_pref_prog_hact > native_prog_hact)))
+ warn("Native progressive resolution of %ux%u is smaller than the max preferred progressive resolution %ux%u.\n",
+ native_prog_hact, native_prog_vact,
+ max_pref_prog_hact, max_pref_prog_vact);
+ if (!native_ilace_mixed_resolutions && native_ilace_vact &&
+ (max_pref_ilace_vact > native_ilace_vact ||
+ (max_pref_ilace_vact == native_ilace_vact && max_pref_ilace_hact > native_ilace_hact)))
+ warn("Native interlaced resolution of %ux%u is smaller than the max preferred interlaced resolution %ux%u.\n",
+ native_ilace_hact, native_ilace_vact,
+ max_pref_ilace_hact, max_pref_ilace_vact);
+
+ if (options[OptPreferredTimings] && !preferred_timings.empty()) {
printf("\n----------------\n");
- printf("\nPreferred Video Timing:\n");
- print_timings(" ", preferred_timings, true);
+ printf("\nPreferred Video Timing%s:\n",
+ preferred_timings.size() > 1 ? "s" : "");
+ for (vec_timings_ext::iterator iter = preferred_timings.begin(); iter != preferred_timings.end(); ++iter)
+ print_timings(" ", *iter, true);
}
- if (options[OptNativeTimings] &&
- (native_timing.t.vact || native_interlaced_timing.t.vact)) {
+ if (options[OptNativeTimings] && !native_timings.empty()) {
printf("\n----------------\n");
- if (native_timing.t.vact) {
- printf("\nNative Progressive Video Timing:\n");
- print_timings(" ", native_timing, true);
- }
- if (native_interlaced_timing.t.vact) {
- printf("\nNative Interlaced Video Timing:\n");
- print_timings(" ", native_interlaced_timing, true);
- }
+ printf("\nNative Video Timing%s:\n",
+ native_timings.size() > 1 ? "s" : "");
+ for (vec_timings_ext::iterator iter = native_timings.begin(); iter != native_timings.end(); ++iter)
+ print_timings(" ", *iter, true);
}
if (!options[OptCheck] && !options[OptCheckInline])
diff --git a/edid-decode.h b/edid-decode.h
index da1ef90..1039190 100644
--- a/edid-decode.h
+++ b/edid-decode.h
@@ -61,12 +61,20 @@ struct timings_ext {
{
memset(&t, 0, sizeof(t));
}
+ timings_ext(const timings &_t, const std::string &_type, const std::string &_flags)
+ {
+ t = _t;
+ type = _type;
+ flags = _flags;
+ }
timings t;
std::string type;
std::string flags;
};
+typedef std::vector<timings_ext> vec_timings_ext;
+
struct edid_state {
edid_state()
{
@@ -118,10 +126,10 @@ struct edid_state {
unsigned block_nr;
std::string block;
std::string data_block;
- timings_ext preferred_timings;
- timings_ext native_timing;
- timings_ext native_interlaced_timing;
unsigned preparse_total_dtds;
+ vec_timings_ext vec_dtds;
+ vec_timings_ext preferred_timings;
+ vec_timings_ext native_timings;
unsigned min_hor_freq_hz;
unsigned max_hor_freq_hz;
@@ -182,7 +190,8 @@ struct edid_state {
// Block Map block state
bool saw_block_1;
- std::string dtd_type();
+ std::string dtd_type(unsigned dtd);
+ std::string dtd_type() { return dtd_type(dtd_cnt); }
bool print_timings(const char *prefix, const struct timings *t,
const char *type, const char *flags = "",
bool detailed = false);
diff --git a/parse-base-block.cpp b/parse-base-block.cpp
index 72e6e6f..08dfc04 100644
--- a/parse-base-block.cpp
+++ b/parse-base-block.cpp
@@ -1329,15 +1329,15 @@ void edid_state::detailed_timings(const char *prefix, const unsigned char *x,
std::string s_type = base_or_cta ? dtd_type() : "DTD";
bool ok = print_timings(prefix, &t, s_type.c_str(), s_flags.c_str(), true);
+ timings_ext te(t, s_type, s_flags);
if (block_nr == 0 && dtd_cnt == 1) {
- preferred_timings.t = t;
- preferred_timings.type = s_type;
- preferred_timings.flags = s_flags;
- native_timing.t = t;
- native_timing.type = s_type;
- native_timing.flags = s_flags;
+ te.type = "DTD 1";
+ preferred_timings.push_back(te);
+ native_timings.push_back(te);
}
+ if (base_or_cta)
+ vec_dtds.push_back(te);
if ((max_display_width_mm && !t.hsize_mm) ||
(max_display_height_mm && !t.vsize_mm)) {
@@ -1537,7 +1537,8 @@ void edid_state::parse_base_block(const unsigned char *x)
struct tm *ptm;
int analog, i;
unsigned col_x, col_y;
- int has_preferred_timing = 0;
+ bool has_preferred_timing = false;
+ bool has_native_timing = false;
data_block = "EDID Structure Version & Revision";
printf(" %s: %hhu.%hhu\n", data_block.c_str(), x[0x12], x[0x13]);
@@ -1728,13 +1729,19 @@ void edid_state::parse_base_block(const unsigned char *x)
}
if (edid_minor >= 4) {
/* 1.4 always has a preferred timing and this bit means something else. */
- has_preferred_timing = 1;
+ has_preferred_timing = true;
+ has_native_timing = x[0x18] & 0x02;
printf(" First detailed timing %s the native pixel format and preferred refresh rate\n",
- (x[0x18] & 0x02) ? "includes" : "does not include");
+ has_native_timing ? "includes" : "does not include");
} else {
if (x[0x18] & 0x02) {
- printf(" First detailed timing is the preferred timings\n");
- has_preferred_timing = 1;
+ printf(" First detailed timing is the preferred timing\n");
+ has_preferred_timing = true;
+ // 1.3 recommends that the preferred timing corresponds to the
+ // native timing, but it is not a requirement.
+ // That said, we continue with the assumption that it actually
+ // is the native timing.
+ has_native_timing = true;
} else if (edid_minor == 3) {
fail("EDID 1.3 requires that the first detailed timing is the preferred timing\n");
}
@@ -1832,6 +1839,8 @@ void edid_state::parse_base_block(const unsigned char *x)
detailed_block(x + 0x5a);
detailed_block(x + 0x6c);
has_spwg = false;
+ if (!has_native_timing)
+ native_timings.clear();
data_block = block;
if (x[0x7e])
diff --git a/parse-cta-block.cpp b/parse-cta-block.cpp
index 35f0e75..eb7d6dc 100644
--- a/parse-cta-block.cpp
+++ b/parse-cta-block.cpp
@@ -394,34 +394,11 @@ void edid_state::cta_svd(const unsigned char *x, unsigned n, bool for_ycbcr420)
print_timings(" ", t, type, flags);
}
if (override_pref) {
- preferred_timings.t = *t;
- preferred_timings.type = type;
- preferred_timings.flags = flags;
+ preferred_timings.push_back(timings_ext(*t, type, flags));
warn("VIC %u is the preferred timing, overriding the first detailed timings. Is this intended?\n", vic);
}
- if (native) {
- if (t->interlaced) {
- if (native_interlaced_timing.t.hact &&
- (native_interlaced_timing.t.hact != t->hact ||
- native_interlaced_timing.t.vact != t->vact))
- fail("Native VIC %u overrides earlier native interlaced timing.\n", vic);
- if (!native_interlaced_timing.t.hact) {
- native_interlaced_timing.t = *t;
- native_interlaced_timing.type = type;
- native_interlaced_timing.flags = flags;
- }
- } else {
- if (native_timing.t.hact &&
- (native_timing.t.hact != t->hact ||
- native_timing.t.vact != t->vact))
- fail("Native VIC %u overrides earlier native timing.\n", vic);
- if (!native_timing.t.hact) {
- native_timing.t = *t;
- native_timing.type = type;
- native_timing.flags = flags;
- }
- }
- }
+ if (native)
+ native_timings.push_back(timings_ext(*t, type, flags));
} else {
printf(" Unknown (VIC %3u)\n", vic);
fail("Unknown VIC %u.\n", vic);
@@ -510,11 +487,12 @@ void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
fail("Empty Data Block with length %u\n", length);
return;
}
+ 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)) {
- char suffix[16];
const struct timings *t;
unsigned char vic = svr;
@@ -523,13 +501,23 @@ void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
t = find_vic_id(vic);
if (t) {
print_timings(" ", t, suffix);
+ preferred_timings.push_back(timings_ext(*t, suffix, ""));
} else {
- printf(" Unknown (VIC %3u)\n", vic);
+ printf(" %s: Unknown\n", suffix);
fail("Unknown VIC %u.\n", vic);
}
- } else if (svr > 128 && svr < 145) {
- printf(" DTD %u\n", svr - 128);
+ } else if (svr >= 129 && svr <= 144) {
+ struct timings t = { svr, 0 };
+
+ sprintf(suffix, "DTD %3u", svr - 128);
+ if (svr >= preparse_total_dtds + 129) {
+ printf(" %s: Invalid\n", suffix);
+ fail("Invalid DTD %u.\n", svr - 128);
+ } else {
+ printf(" %s\n", suffix);
+ preferred_timings.push_back(timings_ext(t, suffix, ""));
+ }
}
}
}
@@ -1960,13 +1948,25 @@ void edid_state::parse_cta_block(const unsigned char *x)
else if (x[3] != cta_byte3)
fail("Byte 3 must be the same for all CTA Extension Blocks.\n");
if (first_block) {
- if (!(x[3] & 0x0f)) {
+ unsigned native_dtds = x[3] & 0x0f;
+
+ native_timings.clear();
+ if (!native_dtds) {
first_svd_might_be_preferred = true;
- memset(&native_timing.t, 0, sizeof(native_timing.t));
- memset(&native_interlaced_timing.t, 0,
- sizeof(native_interlaced_timing.t));
- } else if ((x[3] & 0x0f) > 1) {
- warn("More than one native DTD is unusual.\n");
+ } else if (native_dtds > preparse_total_dtds) {
+ fail("There are more Native DTDs (%u) than DTDs (%u).\n",
+ native_dtds, preparse_total_dtds);
+ }
+ if (native_dtds > preparse_total_dtds)
+ native_dtds = preparse_total_dtds;
+ for (unsigned i = 0; i < native_dtds; i++) {
+ timings_ext te;
+ char type[16];
+
+ te.t.hact = i + 129;
+ sprintf(type, "DTD %3u", i + 1);
+ te.type = type;
+ native_timings.push_back(te);
}
}
}

Privacy Policy