This is an automatic generated email to let you know that the following patch were queued:
Subject: edid-decode: add OVT support Author: Hans Verkuil <[email protected]> Date: Thu Sep 16 15:22:22 2021 +0200 Add support for the new CTA-861.6 Optimized Video Timings. Signed-off-by: Hans Verkuil <[email protected]> Makefile | 3 +- calc-ovt.cpp | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++ edid-decode.cpp | 2 +- edid-decode.h | 18 ++++++ parse-cta-block.cpp | 172 +++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 368 insertions(+), 4 deletions(-) --- diff --git a/Makefile b/Makefile index 184370095926..d5a826f8df38 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,8 @@ 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 + parse-di-ext-block.cpp parse-vtb-ext-block.cpp \ + calc-gtf-cvt.cpp calc-ovt.cpp WARN_FLAGS = -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Wimplicit-fallthrough all: edid-decode diff --git a/calc-ovt.cpp b/calc-ovt.cpp new file mode 100644 index 000000000000..47113d39a744 --- /dev/null +++ b/calc-ovt.cpp @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * Author: Hans Verkuil <[email protected]> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <time.h> +#include <numeric> + +#include "edid-decode.h" + +#define MinVblankDuration 460 +#define MinVsyncLeadingEdge 400 +#define MinClockRate420 590000000 +#define PixelFactor420 2 +#define MinHblank444 80 +#define MinHblank420 128 +#define PixelClockGranularity 1000 +#define MinHtotalGranularity 8 +#define MaxChunkRate 650000000 +#define AudioPacketRate 195000 +#define AudioPacketSize 32 +#define LineOverhead 32 + +static unsigned roundup_to_power_of_two(unsigned v) +{ + unsigned shift = 1; + unsigned mask = 0; + + if (!v || v > 0x80000000) { + fprintf(stderr, "roundup_to_power_of_two: invalid input %u.\n", v); + exit(1); + } + + v--; + do { + mask = v >> shift; + v |= mask; + shift <<= 1; + } while (mask); + return v + 1; +} + +static unsigned greatest_power_of_two_divider(unsigned x) +{ + return x & ~(x - 1); +} + +timings edid_state::calc_ovt_mode(unsigned Hactive, unsigned Vactive, + unsigned Hratio, unsigned Vratio, + unsigned Vrate) +{ + timings t = {}; + + t.hact = Hactive; + t.vact = Vactive; + t.hratio = Hratio; + t.vratio = Vratio; + + unsigned MaxVrate = Vrate; + unsigned VtotalGranularity = 1; + + // Step 1 + switch (Vrate) { + case 24: case 25: case 30: + MaxVrate = 30; + VtotalGranularity = 20; + break; + case 48: case 50: case 60: + MaxVrate = 60; + VtotalGranularity = 20; + break; + case 100: case 120: + MaxVrate = 120; + VtotalGranularity = 5; + break; + case 200: case 240: + MaxVrate = 240; + VtotalGranularity = 5; + break; + case 300: case 360: + MaxVrate = 360; + VtotalGranularity = 5; + break; + case 400: case 480: + MaxVrate = 480; + VtotalGranularity = 5; + break; + } + + // Step 2 + double MaxActiveTime = (1000000.0 / MaxVrate) - MinVblankDuration; + double MinLineTime = MaxActiveTime / Vactive; + unsigned MinVblank = ceil(MinVblankDuration / MinLineTime); + unsigned MinVtotal = Vactive + MinVblank; + + if (MinVtotal % VtotalGranularity) + MinVtotal += VtotalGranularity - (MinVtotal % VtotalGranularity); + + // Step 3 + unsigned MinLineRate = MaxVrate * MinVtotal; + unsigned MaxAudioPacketsPerLine = ceil((double)AudioPacketRate / MinLineRate); + + // Step 4 + unsigned MinHtotal = Hactive + + max(MinHblank444, LineOverhead + AudioPacketSize * MaxAudioPacketsPerLine); + double MinPixelClockRate = (double)MaxVrate * MinHtotal * MinVtotal; + double HCalcGranularity = roundup_to_power_of_two(ceil(MinPixelClockRate / MaxChunkRate)); + unsigned HtotalGranularity = max(MinHtotalGranularity, HCalcGranularity); + + if (MinHtotal % HtotalGranularity) + MinHtotal += HtotalGranularity - (MinHtotal % HtotalGranularity); + + unsigned ResolutionGranularity = PixelClockGranularity / + gcd(PixelClockGranularity, MaxVrate); + unsigned Htotal = 0; + unsigned Vtotal = 0; + unsigned long long PixelClockRate = 0; + + for (;;) { + // Step 5 + unsigned long long RMin = 0; + unsigned V = MinVtotal; + + for (;;) { + unsigned H = MinHtotal; + unsigned long long R = H * V; + if (RMin && R > RMin) + break; + while (R % ResolutionGranularity || + MaxVrate * R / greatest_power_of_two_divider(H) > MaxChunkRate) { + H += HtotalGranularity; + R = H * V; + } + if (!RMin || R < RMin) { + Htotal = H; + Vtotal = V; + RMin = R; + } + V += VtotalGranularity; + } + PixelClockRate = MaxVrate * RMin; + + // Step 6 + MinHtotal = Hactive + max(MinHblank420, PixelFactor420 * + (LineOverhead + AudioPacketSize * MaxAudioPacketsPerLine)); + if (PixelClockRate >= MinClockRate420 && + Htotal < MinHtotal) + continue; + break; + } + + // Step 7 + Vtotal = Vtotal * MaxVrate / Vrate; + + // Step 8 + unsigned Vblank = Vtotal - Vactive; + unsigned VsyncPosition = ceil((double)MinVsyncLeadingEdge * PixelClockRate / (1000000.0 * Htotal)); + t.vfp = Vblank - VsyncPosition; + + t.vsync = 8; + t.vbp = Vblank - t.vfp - t.vsync; + t.pos_pol_vsync = true; + unsigned Hblank = Htotal - Hactive; + t.hsync = 32; + t.hbp = 32; + t.hfp = Hblank - t.hsync - t.hbp; + t.pos_pol_hsync = true; + t.pixclk_khz = PixelClockRate / 1000; + if (!t.hratio || !t.vratio) + calc_ratio(&t); + return t; +} diff --git a/edid-decode.cpp b/edid-decode.cpp index d189f59c3102..38560579d10d 100644 --- a/edid-decode.cpp +++ b/edid-decode.cpp @@ -243,7 +243,7 @@ void do_checksum(const char *prefix, const unsigned char *x, size_t len) printf("\n"); } -static unsigned gcd(unsigned a, unsigned b) +unsigned gcd(unsigned a, unsigned b) { while (b) { unsigned t = b; diff --git a/edid-decode.h b/edid-decode.h index 932769441fa7..7ba972d9ce49 100644 --- a/edid-decode.h +++ b/edid-decode.h @@ -118,6 +118,16 @@ enum gtf_ip_parm { typedef std::vector<timings_ext> vec_timings_ext; +struct cta_vfd { + unsigned char rid; + unsigned char fr_factor; + unsigned int bfr50:1; + unsigned int fr24:1; + unsigned int bfr60:1; + unsigned int fr144:1; + unsigned int fr48:1; +}; + struct edid_state { edid_state() { @@ -162,6 +172,7 @@ struct edid_state { 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)); + memset(&cta.preparsed_first_vfd, 0, sizeof(cta.preparsed_first_vfd)); cta.preparsed_phys_addr = 0xffff; cta.preparsed_speaker_count = 0; cta.preparsed_sld = false; @@ -250,6 +261,7 @@ struct edid_state { vec_timings_ext vec_dtds; unsigned preparsed_total_vtdbs; vec_timings_ext vec_vtdbs; + cta_vfd preparsed_first_vfd; vec_timings_ext preferred_timings; bool preparsed_has_t8vtdb; // Keep track of the found Tag/Extended Tag pairs. @@ -335,6 +347,9 @@ struct edid_state { void edid_cvt_mode(unsigned refresh, struct timings &t, unsigned rb_h_blank = 0, unsigned rb_v_blank = 460, bool early_vsync_rqd = false); void detailed_cvt_descriptor(const char *prefix, const unsigned char *x, bool first); + timings calc_ovt_mode(unsigned hact, unsigned vact, + unsigned hratio, unsigned vratio, + unsigned frame_rate); void print_standard_timing(const char *prefix, unsigned char b1, unsigned char b2, bool gtf_only = false, bool show_both = false); void detailed_display_range_limits(const unsigned char *x); @@ -355,10 +370,12 @@ struct edid_state { 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_vfdb(const unsigned char *x, unsigned n); 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_nvrdb(const unsigned char *x, unsigned length); + cta_vfd cta_parse_vfd(const unsigned char *x, unsigned lvfd); void cta_rcdb(const unsigned char *x, unsigned length); void cta_sldb(const unsigned char *x, unsigned length); void cta_preparse_sldb(const unsigned char *x, unsigned length); @@ -482,6 +499,7 @@ void hex_block(const char *prefix, const unsigned char *x, unsigned length, std::string block_name(unsigned char block); void calc_ratio(struct timings *t); const char *oui_name(unsigned oui, unsigned *ouinum = NULL); +unsigned gcd(unsigned a, unsigned b); bool timings_close_match(const timings &t1, const timings &t2); const struct timings *find_dmt_id(unsigned char dmt_id); diff --git a/parse-cta-block.cpp b/parse-cta-block.cpp index 358048606198..d3c0aec5755a 100644 --- a/parse-cta-block.cpp +++ b/parse-cta-block.cpp @@ -189,6 +189,53 @@ static const struct timings edid_cta_modes2[] = { { 4096, 2160, 256, 135, 1188000, 0, false, 88, 88, 128, true, 8, 10, 72, true }, }; +struct edid_rid { + unsigned hact, vact; + unsigned hratio, vratio; +}; + +static const edid_rid rids[] = { + /* RID 0-9 */ + { 0, 0, 0, 0 }, + { 1280, 720, 16, 9 }, + { 1280, 720, 64, 27 }, + { 1680, 720, 64, 27 }, + { 1920, 1080, 16, 9 }, + { 1920, 1080, 64, 27 }, + { 2560, 1080, 64, 27 }, + { 3840, 1080, 32, 9 }, + { 2560, 1440, 16, 9 }, + { 3440, 1440, 64, 27 }, + /* RID 10-19 */ + { 5120, 1440, 32, 9 }, + { 3840, 2160, 16, 9 }, + { 3840, 2160, 64, 27 }, + { 5120, 2160, 64, 27 }, + { 7680, 2160, 32, 9 }, + { 5120, 2880, 16, 9 }, + { 5120, 2880, 64, 27 }, + { 6880, 2880, 64, 27 }, + { 10240, 2880, 32, 9 }, + { 7680, 4320, 16, 9 }, + /* RID 20-28 */ + { 7680, 4320, 64, 27 }, + { 10240, 4320, 64, 27 }, + { 15360, 4320, 32, 9 }, + { 11520, 6480, 16, 9 }, + { 11520, 6480, 64, 27 }, + { 15360, 6480, 64, 27 }, + { 15360, 8640, 16, 9 }, + { 15360, 8640, 64, 27 }, + { 20480, 8640, 64, 27 }, +}; + +static const unsigned vf_rate_values[] = { + /* Rate Index 0-7 */ + 0, 24, 25, 30, 48, 50, 60, 100, + /* Rate Index 8-15 */ + 120, 144, 200, 240, 300, 360, 400, 480, +}; + static const unsigned char edid_hdmi_mode_map[] = { 95, 94, 93, 98 }; unsigned char hdmi_vic_to_vic(unsigned char hdmi_vic) @@ -500,6 +547,105 @@ void edid_state::cta_svd(const unsigned char *x, unsigned n, bool for_ycbcr420) } } +cta_vfd edid_state::cta_parse_vfd(const unsigned char *x, unsigned lvfd) +{ + cta_vfd vfd = {}; + + vfd.rid = x[0] & 0x3f; + if (vfd.rid >= ARRAY_SIZE(rids)) { + vfd.rid = 0; + return vfd; + } + vfd.bfr50 = !!(x[0] & 0x80); + vfd.fr24 = !!(x[0] & 0x40); + vfd.bfr60 = lvfd > 1 ? !!(x[1] & 0x80) : 1; + vfd.fr144 = lvfd > 1 ? !!(x[1] & 0x40) : 0; + vfd.fr_factor = lvfd > 1 ? (x[1] & 0x3f) : 3; + vfd.fr48 = lvfd > 2 ? !!(x[2] & 0x01) : 0; + return vfd; +} + +static bool vfd_has_rate(cta_vfd &vfd, unsigned rate_index) +{ + static const unsigned factors[6] = { + 1, 2, 4, 8, 12, 16 + }; + unsigned rate = vf_rate_values[rate_index]; + unsigned factor = 0; + + if (!vfd.rid) + return false; + if (rate == 24) + return vfd.fr24; + if (rate == 48) + return vfd.fr48; + if (rate == 144) + return vfd.fr144; + + if (!(rate % 30)) { + if (!vfd.bfr60) + return false; + factor = rate / 30; + } + if (!(rate % 25)) { + if (!vfd.bfr50) + return false; + factor = rate / 25; + } + + for (unsigned i = 0; i < ARRAY_SIZE(factors); i++) + if (factors[i] == factor && (vfd.fr_factor & (1 << i))) + return true; + return false; +} + +void edid_state::cta_vfdb(const unsigned char *x, unsigned n) +{ + if (n-- == 0) { + fail("Length is 0.\n"); + return; + } + unsigned char flags = *x++; + unsigned lvfd = (flags & 3) + 1; + + if (n % lvfd) { + fail("Length - 1 is not a multiple of Lvfd (%u).\n", lvfd); + return; + } + if (flags & 0x80) + printf(" Supports YCbCr 4:2:0\n"); + if (flags & 0x40) + printf(" NTSC fractional frame rates are preferred\n"); + for (unsigned i = 0; i < n; i += lvfd, x += lvfd) { + unsigned char rid = x[0] & 0x3f; + cta_vfd vfd = cta_parse_vfd(x, lvfd); + + if (lvfd > 2 && (x[2] & 0xfe)) + fail("Bits F31-F37 must be 0.\n"); + if (lvfd > 3 && x[3]) + fail("Bits F40-F47 must be 0.\n"); + if (rid == 0 || rid >= ARRAY_SIZE(rids)) { + fail("Unknown RID %u.\n", rid); + continue; + } + for (unsigned rate_index = 1; rate_index < ARRAY_SIZE(vf_rate_values); rate_index++) { + if (!vfd_has_rate(vfd, rate_index)) + continue; + struct timings t = calc_ovt_mode(rids[vfd.rid].hact, + rids[vfd.rid].vact, + rids[vfd.rid].hratio, + rids[vfd.rid].vratio, + vf_rate_values[i]); + char type[16]; + sprintf(type, "RID %u@%up", rid, vf_rate_values[rate_index]); + print_timings(" ", &t, type); + if (rid_to_vic(vfd.rid, i)) + fail("%s not allowed since it maps to VIC %u.\n", + rid_to_vic(vfd.rid, i)); + } + } +} + void edid_state::print_vic_index(const char *prefix, unsigned idx, const char *suffix, bool ycbcr420) { if (!suffix) @@ -603,6 +749,16 @@ void edid_state::cta_print_svr(unsigned char svr, vec_timings_ext &vec_tim) printf(" %s\n", suffix); vec_tim.push_back(timings_ext(svr, suffix)); } + } else if (svr >= 161 && svr <= 175) { + sprintf(suffix, "RID %u@%up", + cta.preparsed_first_vfd.rid, vf_rate_values[svr - 160]); + if (!vfd_has_rate(cta.preparsed_first_vfd, svr - 160)) { + printf(" %s: Invalid\n", suffix); + fail("Invalid %s.\n", suffix); + } 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) { @@ -2119,7 +2275,7 @@ void edid_state::cta_block(const unsigned char *x, std::vector<unsigned> &found_ 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 0x06: data_block = "Video Format 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; @@ -2229,6 +2385,7 @@ void edid_state::cta_block(const unsigned char *x, std::vector<unsigned> &found_ 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 0x06: cta_vfdb(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; @@ -2317,6 +2474,11 @@ void edid_state::preparse_cta_block(const unsigned char *x) cta.preparsed_phys_addr = (x[i + 4] << 8) | x[i + 5]; } break; + case 0x06: + if (!(x[i] & 0x1f) || cta.preparsed_first_vfd.rid) + break; + cta.preparsed_first_vfd = cta_parse_vfd(x + 2, (x[i + 1] & 3) + 1); + break; case 0x07: if (x[i + 1] == 0x0d) cta.has_vfpdb = true; @@ -2476,9 +2638,15 @@ void edid_state::cta_resolve_svr(vec_timings_ext::iterator iter) } else if (iter->svr() <= 144) { iter->flags = cta.vec_dtds[iter->svr() - 129].flags; iter->t = cta.vec_dtds[iter->svr() - 129].t; - } else { + } else if (iter->svr() <= 160) { iter->flags = cta.vec_vtdbs[iter->svr() - 145].flags; iter->t = cta.vec_vtdbs[iter->svr() - 145].t; + } else if (iter->svr() <= 175) { + iter->flags.clear(); + unsigned char rid = cta.preparsed_first_vfd.rid; + iter->t = calc_ovt_mode(rids[rid].hact, rids[rid].vact, + rids[rid].hratio, rids[rid].vratio, + vf_rate_values[iter->svr() - 160]); } } _______________________________________________ linuxtv-commits mailing list [email protected] https://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits
