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

Reply via email to