aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--calc-gtf-cvt.cpp238
-rw-r--r--edid-decode.186
-rw-r--r--edid-decode.cpp471
-rw-r--r--edid-decode.h47
-rw-r--r--parse-base-block.cpp438
-rw-r--r--parse-cta-block.cpp26
-rw-r--r--parse-displayid-block.cpp26
-rw-r--r--parse-vtb-ext-block.cpp14
9 files changed, 916 insertions, 432 deletions
diff --git a/Makefile b/Makefile
index cb85fc0..287b72d 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ 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
+ 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
all: edid-decode
diff --git a/calc-gtf-cvt.cpp b/calc-gtf-cvt.cpp
new file mode 100644
index 0000000..b9d62b2
--- /dev/null
+++ b/calc-gtf-cvt.cpp
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2006-2012 Red Hat, Inc.
+ * Copyright 2018-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * Author: Adam Jackson <ajax@nwnk.net>
+ * Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "edid-decode.h"
+
+#define CELL_GRAN 8.0
+#define MARGIN_PERC 1.8
+#define GTF_MIN_PORCH 1.0
+#define GTF_V_SYNC_RQD 3.0
+#define GTF_H_SYNC_PERC 8.0
+#define GTF_MIN_VSYNC_BP 550.0
+
+timings edid_state::calc_gtf_mode(unsigned h_pixels, unsigned v_lines,
+ double ip_freq_rqd, bool int_rqd,
+ enum gtf_ip_parm ip_parm, bool margins_rqd,
+ bool secondary, double C, double M, double K, double J)
+{
+ timings t = {};
+ /* C' and M' are part of the Blanking Duty Cycle computation */
+ double C_PRIME = ((C - J) * K / 256.0) + J;
+ double M_PRIME = K / 256.0 * M;
+
+ double h_pixels_rnd = round(h_pixels / CELL_GRAN) * CELL_GRAN;
+ double v_lines_rnd = int_rqd ? round(v_lines / 2.0) : v_lines;
+ unsigned hor_margin = margins_rqd ?
+ round(h_pixels_rnd * MARGIN_PERC / 100.0 / CELL_GRAN) * CELL_GRAN : 0;
+ unsigned vert_margin = margins_rqd ? round(MARGIN_PERC / 100.0 * v_lines_rnd) : 0;
+ double interlace = int_rqd ? 0.5 : 0;
+ double total_active_pixels = h_pixels_rnd + hor_margin * 2;
+
+ t.hact = h_pixels_rnd;
+ t.vact = v_lines;
+ t.interlaced = int_rqd;
+
+ double pixel_freq;
+ double h_blank_pixels;
+ double total_pixels;
+ double v_sync_bp;
+
+ if (ip_parm == gtf_ip_vert_freq) {
+ // vertical frame frequency (Hz)
+ double v_field_rate_rqd = int_rqd ? ip_freq_rqd * 2 : ip_freq_rqd;
+ double h_period_est = ((1.0 / v_field_rate_rqd) - GTF_MIN_VSYNC_BP / 1000000.0) /
+ (v_lines_rnd + vert_margin * 2 + GTF_MIN_PORCH + interlace) * 1000000.0;
+ v_sync_bp = round(GTF_MIN_VSYNC_BP / h_period_est);
+ double total_v_lines = v_lines_rnd + vert_margin * 2 +
+ v_sync_bp + interlace + GTF_MIN_PORCH;
+ double v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0;
+ double h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est);
+ double ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0);
+ h_blank_pixels = round(total_active_pixels * ideal_duty_cycle /
+ (100.0 - ideal_duty_cycle) /
+ (2 * CELL_GRAN)) * 2 * CELL_GRAN;
+ total_pixels = total_active_pixels + h_blank_pixels;
+ pixel_freq = total_pixels / h_period;
+ } else if (ip_parm == gtf_ip_hor_freq) {
+ // horizontal frequency (kHz)
+ double h_freq = ip_freq_rqd;
+ v_sync_bp = round(GTF_MIN_VSYNC_BP * h_freq / 1000.0);
+ double ideal_duty_cycle = C_PRIME - (M_PRIME / h_freq);
+ h_blank_pixels = round(total_active_pixels * ideal_duty_cycle /
+ (100.0 - ideal_duty_cycle) /
+ (2 * CELL_GRAN)) * 2 * CELL_GRAN;
+ total_pixels = total_active_pixels + h_blank_pixels;
+ pixel_freq = total_pixels * h_freq / 1000.0;
+ } else {
+ // pixel clock rate (MHz)
+ pixel_freq = ip_freq_rqd;
+ double ideal_h_period =
+ ((C_PRIME - 100.0) +
+ sqrt(((100.0 - C_PRIME) * (100.0 - C_PRIME) +
+ (0.4 * M_PRIME * (total_active_pixels + hor_margin * 2) /
+ pixel_freq)))) / 2.0 / M_PRIME * 1000.0;
+ double ideal_duty_cycle = C_PRIME - (M_PRIME * ideal_h_period) / 1000.0;
+ h_blank_pixels = round(total_active_pixels * ideal_duty_cycle /
+ (100.0 - ideal_duty_cycle) /
+ (2 * CELL_GRAN)) * 2 * CELL_GRAN;
+ total_pixels = total_active_pixels + h_blank_pixels;
+ double h_freq = pixel_freq / total_pixels * 1000.0;
+ v_sync_bp = round(GTF_MIN_VSYNC_BP * h_freq / 1000.0);
+ }
+
+ double v_back_porch = v_sync_bp - GTF_V_SYNC_RQD;
+
+ t.vbp = v_back_porch;
+ t.vsync = GTF_V_SYNC_RQD;
+ t.vfp = GTF_MIN_PORCH;
+ t.pixclk_khz = round(1000.0 * pixel_freq);
+ t.hsync = round(GTF_H_SYNC_PERC / 100.0 * total_pixels / CELL_GRAN) * CELL_GRAN;
+ t.hfp = (h_blank_pixels / 2.0) - t.hsync;
+ t.hbp = t.hfp + t.hsync;
+ t.hborder = hor_margin;
+ t.vborder = vert_margin;
+ t.pos_pol_hsync = secondary;
+ t.pos_pol_vsync = !secondary;
+ t.rb = secondary ? RB_GTF : 0;
+ return t;
+}
+
+void edid_state::edid_gtf_mode(unsigned refresh, struct timings &t)
+{
+ unsigned hratio = t.hratio;
+ unsigned vratio = t.vratio;
+ t = calc_gtf_mode(t.hact, t.vact, refresh, t.interlaced);
+ t.hratio = hratio;
+ t.vratio = vratio;
+}
+
+#define CVT_MIN_VSYNC_BP 550.0
+#define CVT_MIN_V_PORCH 3
+#define CVT_MIN_V_BPORCH 6
+#define CVT_C_PRIME 30.0
+#define CVT_M_PRIME 300.0
+#define CVT_RB_MIN_VBLANK 460.0
+
+// If rb == RB_CVT_V2, then alt means video-optimized (i.e. 59.94 instead of 60 Hz, etc.).
+// If rb == RB_CVT_V3, then alt means that rb_h_blank is 160 instead of 80.
+// Note: for RB_CVT_V3 this calculation is slightly different, but
+// since CVT 1.3 is not yet public, I cannot update the calculation yet. For now
+// it will follow V2. So RBv3 timings will be off for now.
+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)
+{
+ timings t = {};
+
+ t.hact = h_pixels;
+ t.vact = v_lines;
+ t.interlaced = int_rqd;
+
+ 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;
+ unsigned hor_margin = margins_rqd ?
+ floor((h_pixels_rnd * MARGIN_PERC / 100.0) / cell_gran) * cell_gran : 0;
+ unsigned vert_margin = margins_rqd ? floor(MARGIN_PERC / 100.0 * v_lines_rnd) : 0;
+ double interlace = int_rqd ? 0.5 : 0;
+ double total_active_pixels = h_pixels_rnd + hor_margin * 2;
+ double v_field_rate_rqd = int_rqd ? ip_freq_rqd * 2 : ip_freq_rqd;
+ double clock_step = rb == RB_CVT_V2 ? 0.001 : 0.25;
+ 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 h_sync = 32;
+
+ double v_sync;
+ double pixel_freq;
+ double v_blank;
+ double v_sync_bp;
+
+ /* Determine VSync Width from aspect ratio */
+ if ((t.vact * 4 / 3) == t.hact)
+ v_sync = 4;
+ else if ((t.vact * 16 / 9) == t.hact)
+ v_sync = 5;
+ else if ((t.vact * 16 / 10) == t.hact)
+ v_sync = 6;
+ else if (!(t.vact % 4) && ((t.vact * 5 / 4) == t.hact))
+ v_sync = 7;
+ else if ((t.vact * 15 / 9) == t.hact)
+ v_sync = 7;
+ else /* Custom */
+ v_sync = 10;
+
+ if (rb >= RB_CVT_V2)
+ v_sync = 8;
+
+ if (rb == 0) {
+ double h_period_est = ((1.0 / v_field_rate_rqd) - CVT_MIN_VSYNC_BP / 1000000.0) /
+ (v_lines_rnd + vert_margin * 2 + CVT_MIN_V_PORCH + interlace) * 1000000.0;
+ v_sync_bp = floor(CVT_MIN_VSYNC_BP / h_period_est) + 1;
+ if (v_sync_bp < v_sync + CVT_MIN_V_BPORCH)
+ v_sync_bp = v_sync + CVT_MIN_V_BPORCH;
+ v_blank = v_sync_bp + CVT_MIN_V_PORCH;
+ double ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * h_period_est / 1000.0);
+ if (ideal_duty_cycle < 20)
+ ideal_duty_cycle = 20;
+ h_blank = floor(total_active_pixels * ideal_duty_cycle /
+ (100.0 - ideal_duty_cycle) /
+ (2 * CELL_GRAN)) * 2 * CELL_GRAN;
+ double total_pixels = total_active_pixels + h_blank;
+ h_sync = floor(total_pixels * 0.08 / CELL_GRAN) * CELL_GRAN;
+ pixel_freq = floor((total_pixels / h_period_est) / clock_step) * clock_step;
+ } else {
+ double h_period_est = ((1000000.0 / v_field_rate_rqd) - CVT_RB_MIN_VBLANK) /
+ (v_lines_rnd + vert_margin * 2);
+ double vbi_lines = floor(CVT_RB_MIN_VBLANK / h_period_est) + 1;
+ double rb_min_vbi = rb_v_fporch + v_sync + CVT_MIN_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_V1)
+ v_sync_bp = v_blank - rb_v_fporch;
+ else
+ v_sync_bp = v_sync + CVT_MIN_V_BPORCH;
+ double total_pixels = h_blank + total_active_pixels;
+ pixel_freq = floor((v_field_rate_rqd * total_v_lines * total_pixels / 1000000.0 *
+ refresh_multiplier) / clock_step) * clock_step;
+ }
+
+ t.vbp = v_sync_bp - v_sync;
+ t.vsync = v_sync;
+ t.vfp = v_blank - t.vbp - t.vsync;
+ t.pixclk_khz = round(1000.0 * pixel_freq);
+ t.hsync = h_sync;
+ t.hfp = (h_blank / 2.0) - t.hsync;
+ t.hbp = t.hfp + t.hsync;
+ t.hborder = hor_margin;
+ t.vborder = vert_margin;
+ t.rb = rb;
+ if (alt && (rb == RB_CVT_V2 || rb == RB_CVT_V3))
+ t.rb |= RB_FLAG;
+ t.pos_pol_hsync = t.rb;
+ t.pos_pol_vsync = !t.rb;
+ calc_ratio(&t);
+ return t;
+}
+
+void edid_state::edid_cvt_mode(unsigned refresh, struct timings &t)
+{
+ unsigned hratio = t.hratio;
+ unsigned vratio = t.vratio;
+
+ t = calc_cvt_mode(t.hact, t.vact, refresh, t.rb & ~RB_FLAG, t.interlaced,
+ false, t.rb & RB_FLAG);
+ t.hratio = hratio;
+ t.vratio = vratio;
+}
diff --git a/edid-decode.1 b/edid-decode.1
index 58e81bf..e7c86bf 100644
--- a/edid-decode.1
+++ b/edid-decode.1
@@ -154,9 +154,9 @@ GTF 1.1: VESA Generalized Timing Formula Standard, Version: 1.1
\fB\-h\fR, \fB\-\-help\fR
Prints the help message.
.TP
-\fB\-o\fR, \fB\-\-output\-format\fR=\fI<fmt>\fR
+\fB\-o\fR, \fB\-\-output\-format\fR \fI<fmt>\fR
If [out] is specified, then write the EDID in format \fI<fmt>\fR.
-.br
+
The output format can be one of:
.br
hex: hex numbers in ascii text (default for stdout)
@@ -230,11 +230,87 @@ the start.
\fB\-\-version\fR
Show the SHA hash and the last commit date.
+.SH TIMING OPTIONS
+The following options report the timings for DMT, VIC and HDMI VIC codes and
+calculate the timings for CVT or GTF timings, based on the given parameters.
+The EDID will not be shown, although it can be used with the \fB\-\-gtf\fR
+option in order to read the secondary curve parameters.
+.TP
+\fB\-\-std\fR \fI<byte1>\fR,\fI<byte2>\fR
+Show the standard timing represented by these two bytes.
+.TP
+\fB\-\-dmt\fR \fI<dmt>\fR
+Show the timings for the DMT with the given DMT ID.
+.TP
+\fB\-\-vic\fR \fI<vic>\fR
+Show the timings for this VIC.
+.TP
+\fB\-\-hdmi\-vic\fR \fI<hdmivic>\fR
+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]
+.br
+Calculate the CVT timings for the given format.
+
+\fI<width>\fR is the width in pixels, \fI<height>\fR is the frame (not field!) height in lines.
+.br
+\fI<fps>\fR is frames per second for progressive timings and fields per second for interlaced timings.
+.br
+\fI<rb>\fR can be 0 (no reduced blanking, default), or 1-3 for the reduced blanking version.
+.br
+If \fBinterlaced\fR is given, then this is an interlaced format.
+.br
+If \fBoverscan\fR is given, then this is an overscanned format. I.e., margins are required.
+.br
+If \fBalt\fR is given and \fI<rb>\fR=2, then report the timings
+optimized for video: 1000 / 1001 * \fI<fps>\fR.
+.br
+If \fBalt\fR is given and \fI<rb>\fR=3, then the horizontal blanking
+is 160 instead of 80 pixels.
+.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]
+.br
+Calculate the GTF timings for the given format.
+
+\fI<width>\fR is the width in pixels, \fI<height>\fR is the frame (not field!) height in lines.
+.br
+\fI<fps>\fR is frames per second for progressive timings and fields per second for interlaced timings.
+.br
+\fI<horfreq>\fR is the horizontal frequency in kHz.
+.br
+\fI<pixclk>\fR is the pixel clock frequency in MHz.
+Only one of \fBfps\fR, \fBhorfreq\fR or \fBpixclk\fR must be given.
+.br
+If \fBinterlaced\fR is given, then this is an interlaced format.
+.br
+If \fBoverscan\fR is given, then this is an overscanned format. I.e., margins are required.
+.br
+If \fBsecondary\fR is given, then the secondary GTF is used for
+reduced blanking, where \fI<c>\fR, \fI<m>\fR, \fI<k>\fR and \fI<j>\fR are parameters
+for the secondary curve. If none of the secondary curve parameters
+were set, and an EDID file is passed as command line option, then the
+secondary curve parameters are read from that EDID.
+.br
+The default secondary curve parameters are 40 for \fI<c>\fR, 600 for \fI<m>\fR,
+128 for \fI<k>\fR and 20 for \fI<j>\fR.
+These values correspond to the normal curve that GTF uses.
+.TP
+\fB\-\-list\-established\-timings\fR
+List all known Established Timings.
+.TP
+\fB\-\-list\-dmts\fR
+List all known DMTs.
+.TP
+\fB\-\-list\-vics\fR
+List all known VICs.
+.TP
+\fB\-\-list\-hdmi\-vics\fR
+List all known HDMI VICs.
+
.PP
.SH NOTES
-Not all fields are decoded, or decoded completely. Some fields' decoding
-may appear to corrupt the output (for example, detailed string sections
-have their contents printed literally).
+Not all fields are decoded, or decoded completely.
.B edid-decode
does attempt to validate its input against the relevant standards, but its
opinions have not been double-checked with the relevant standards bodies,
diff --git a/edid-decode.cpp b/edid-decode.cpp
index 2b5aafc..1470406 100644
--- a/edid-decode.cpp
+++ b/edid-decode.cpp
@@ -56,6 +56,16 @@ enum Option {
OptSkipSHA = 128,
OptHideSerialNumbers,
OptVersion,
+ OptSTD,
+ OptDMT,
+ OptVIC,
+ OptHDMIVIC,
+ OptCVT,
+ OptGTF,
+ OptListEstTimings,
+ OptListDMTs,
+ OptListVICs,
+ OptListHDMIVICs,
OptLast = 256
};
@@ -78,6 +88,16 @@ static struct option long_options[] = {
{ "xmodeline", no_argument, 0, OptXModeLineTimings },
{ "fbmode", no_argument, 0, OptFBModeTimings },
{ "v4l2-timings", no_argument, 0, OptV4L2Timings },
+ { "std", required_argument, 0, OptSTD },
+ { "dmt", required_argument, 0, OptDMT },
+ { "vic", required_argument, 0, OptVIC },
+ { "hdmi-vic", required_argument, 0, OptHDMIVIC },
+ { "cvt", required_argument, 0, OptCVT },
+ { "gtf", required_argument, 0, OptGTF },
+ { "list-established-timings", no_argument, 0, OptListEstTimings },
+ { "list-dmts", no_argument, 0, OptListDMTs },
+ { "list-vics", no_argument, 0, OptListVICs },
+ { "list-hdmi-vics", no_argument, 0, OptListHDMIVICs },
{ 0, 0, 0, 0 }
};
@@ -90,29 +110,62 @@ static void usage(void)
" if the output filename is '-'.\n"
"\nOptions:\n"
" -o, --output-format <fmt>\n"
- " if [out] is specified, then write the EDID in this format\n"
+ " If [out] is specified, then write the EDID in this format\n"
" <fmt> is one of:\n"
" hex: hex numbers in ascii text (default for stdout)\n"
" raw: binary data (default unless writing to stdout)\n"
" carray: c-program struct\n"
" xml: XML data\n"
- " -c, --check check if the EDID conforms to the standards, failures and\n"
+ " -c, --check Check if the EDID conforms to the standards, failures and\n"
" warnings are reported at the end.\n"
- " -C, --check-inline check if the EDID conforms to the standards, failures and\n"
+ " -C, --check-inline Check if the EDID conforms to the standards, failures and\n"
" warnings are reported inline.\n"
- " -n, --native-timings report the native timings\n"
- " -p, --preferred-timings report the preferred timings\n"
- " -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"
- " -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"
- " -s, --skip-hex-dump skip the initial hex dump of the EDID\n"
- " --skip-sha skip the SHA report\n"
- " --hide-serial-numbers replace serial numbers with '...'\n"
+ " -n, --native-timings Report the native timings.\n"
+ " -p, --preferred-timings Report the preferred timings.\n"
+ " -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"
+ " -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"
+ " -s, --skip-hex-dump Skip the initial hex dump of the EDID.\n"
+ " --skip-sha Skip the SHA report.\n"
+ " --hide-serial-numbers Replace serial numbers with '...'\n"
" --version show the edid-decode version (SHA)\n"
- " -h, --help display this help message\n");
+ " --std <byte1>,<byte2> Show the standard timing represented by these two bytes.\n"
+ " --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]\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"
+ " <rb> can be 0 (no reduced blanking, default), or\n"
+ " 1-3 for the reduced blanking version.\n"
+ " If 'interlaced' is given, then this is an interlaced format.\n"
+ " If 'overscan' is given, then this is an overscanned format.\n"
+ " If 'alt' is given and <rb>=2, then report the timings\n"
+ " optimized for video: 1000 / 1001 * <fps>.\n"
+ " If 'alt' is given and <rb>=3, then the horizontal blanking\n"
+ " is 160 instead of 80 pixels.\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"
+ " <fps> is frames per second for progressive timings,\n"
+ " or fields per second for interlaced timings.\n"
+ " <horfreq> is the horizontal frequency in kHz.\n"
+ " <pixclk> is the pixel clock frequency in MHz.\n"
+ " Only one of fps, horfreq or pixclk must be given.\n"
+ " If 'interlaced' is given, then this is an interlaced format.\n"
+ " If 'overscan' is given, then this is an overscanned format.\n"
+ " If 'secondary' is given, then the secondary GTF is used for\n"
+ " reduced blanking, where <c>, <m>, <k> and <j> are parameters\n"
+ " for the secondary curve.\n"
+ " --list-established-timings List all known Established Timings.\n"
+ " --list-dmts List all known DMTs.\n"
+ " --list-vics List all known VICs.\n"
+ " --list-hdmi-vics List all known HDMI VICs.\n"
+ " -h, --help Display this help message.\n");
}
static std::string s_msgs[EDID_MAX_BLOCKS + 1][2];
@@ -449,12 +502,16 @@ bool edid_state::print_timings(const char *prefix, const struct timings *t,
double refresh = (double)t->pixclk_khz * 1000.0 / (htotal * vtotal);
std::string s;
- if (t->rb) {
+ unsigned rb = t->rb & ~RB_FLAG;
+ if (rb) {
+ bool alt = t->rb & RB_FLAG;
s = "RB";
- if (t->rb == 2)
- s += "v2";
- else if (t->rb == 3)
- s += "v3";
+ // Mark RB_CVT_V3 as preliminary since CVT 1.3 has not been
+ // released yet.
+ if (rb == RB_CVT_V2)
+ s += std::string("v2") + (alt ? ",video-optimized" : "");
+ else if (rb == RB_CVT_V3)
+ s += std::string("v3-is-preliminary") + (alt ? ",h-blank-160" : "");
}
add_str(s, flags);
if (t->hsize_mm || t->vsize_mm)
@@ -470,7 +527,7 @@ bool edid_state::print_timings(const char *prefix, const struct timings *t,
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 %7.3f MHz%s\n",
+ printf("%s%s: %5ux%-5s %7.3f Hz %3u:%-3u %7.3f kHz %8.3f MHz%s\n",
prefix, type,
t->hact, buf,
refresh,
@@ -497,10 +554,12 @@ bool edid_state::print_timings(const char *prefix, const struct timings *t,
min_vert_freq_hz = min(min_vert_freq_hz, refresh);
max_vert_freq_hz = max(max_vert_freq_hz, refresh);
}
- if (pixclk_khz && (t->hact + hbl)) {
- min_hor_freq_hz = min(min_hor_freq_hz, (pixclk_khz * 1000ULL) / (t->hact + hbl));
- max_hor_freq_hz = max(max_hor_freq_hz, (pixclk_khz * 1000ULL) / (t->hact + hbl));
+ if (hor_freq_khz) {
+ min_hor_freq_hz = min(min_hor_freq_hz, hor_freq_khz * 1000.0);
+ max_hor_freq_hz = max(max_hor_freq_hz, hor_freq_khz * 1000.0);
max_pixclk_khz = max(max_pixclk_khz, pixclk_khz);
+ if (t->pos_pol_hsync && !t->pos_pol_vsync && t->vsync == 3)
+ base.max_pos_neg_hor_freq_khz = hor_freq_khz;
}
if (t->ycbcr420 && t->pixclk_khz < 590000)
@@ -1200,16 +1259,278 @@ int edid_state::parse_edid()
return failures ? -2 : 0;
}
+enum cvt_opts {
+ CVT_WIDTH = 0,
+ CVT_HEIGHT,
+ CVT_FPS,
+ CVT_INTERLACED,
+ CVT_OVERSCAN,
+ CVT_RB,
+ CVT_ALT,
+};
+
+static int parse_cvt_subopt(char **subopt_str, double *value)
+{
+ int opt;
+ char *opt_str;
+
+ static const char * const subopt_list[] = {
+ "w",
+ "h",
+ "fps",
+ "interlaced",
+ "overscan",
+ "rb",
+ "alt",
+ nullptr
+ };
+
+ opt = getsubopt(subopt_str, (char* const*) subopt_list, &opt_str);
+
+ if (opt == -1) {
+ fprintf(stderr, "Invalid suboptions specified.\n");
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+ if (opt_str == nullptr && opt != CVT_INTERLACED && opt != CVT_ALT &&
+ opt != CVT_OVERSCAN) {
+ fprintf(stderr, "No value given to suboption <%s>.\n",
+ subopt_list[opt]);
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+
+ if (opt_str)
+ *value = strtod(opt_str, nullptr);
+ return opt;
+}
+
+static void parse_cvt(char *optarg)
+{
+ unsigned w = 0, h = 0;
+ double fps = 0;
+ unsigned rb = 0;
+ bool interlaced = false;
+ bool alt = false;
+ bool overscan = false;
+
+ while (*optarg != '\0') {
+ int opt;
+ double opt_val;
+
+ opt = parse_cvt_subopt(&optarg, &opt_val);
+
+ switch (opt) {
+ case CVT_WIDTH:
+ w = round(opt_val);
+ break;
+ case CVT_HEIGHT:
+ h = round(opt_val);
+ break;
+ case CVT_FPS:
+ fps = opt_val;
+ break;
+ case CVT_RB:
+ rb = opt_val;
+ break;
+ case CVT_OVERSCAN:
+ overscan = true;
+ break;
+ case CVT_INTERLACED:
+ interlaced = opt_val;
+ break;
+ case CVT_ALT:
+ alt = opt_val;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!w || !h || !fps) {
+ fprintf(stderr, "Missing width, height and/or fps.\n");
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+ if (interlaced)
+ fps /= 2;
+ timings t = state.calc_cvt_mode(w, h, fps, rb, interlaced, overscan, alt);
+ state.print_timings("", &t, "CVT", "", true, false);
+}
+
+struct gtf_parsed_data {
+ unsigned w, h;
+ double freq;
+ double C, M, K, J;
+ bool overscan;
+ bool interlaced;
+ bool secondary;
+ bool params_from_edid;
+ enum gtf_ip_parm ip_parm;
+};
+
+enum gtf_opts {
+ GTF_WIDTH = 0,
+ GTF_HEIGHT,
+ GTF_FPS,
+ GTF_HORFREQ,
+ GTF_PIXCLK,
+ GTF_INTERLACED,
+ GTF_OVERSCAN,
+ GTF_SECONDARY,
+ GTF_C2,
+ GTF_M,
+ GTF_K,
+ GTF_J2,
+};
+
+static int parse_gtf_subopt(char **subopt_str, double *value)
+{
+ int opt;
+ char *opt_str;
+
+ static const char * const subopt_list[] = {
+ "w",
+ "h",
+ "fps",
+ "horfreq",
+ "pixclk",
+ "interlaced",
+ "overscan",
+ "secondary",
+ "C",
+ "M",
+ "K",
+ "J",
+ nullptr
+ };
+
+ opt = getsubopt(subopt_str, (char * const *)subopt_list, &opt_str);
+
+ if (opt == -1) {
+ fprintf(stderr, "Invalid suboptions specified.\n");
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+ if (opt_str == nullptr && opt != GTF_INTERLACED && opt != GTF_OVERSCAN &&
+ opt != GTF_SECONDARY) {
+ fprintf(stderr, "No value given to suboption <%s>.\n",
+ subopt_list[opt]);
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+
+ if (opt == GTF_C2 || opt == GTF_J2)
+ *value = round(2.0 * strtod(opt_str, nullptr));
+ else if (opt_str)
+ *value = strtod(opt_str, nullptr);
+ return opt;
+}
+
+static void parse_gtf(char *optarg, gtf_parsed_data &data)
+{
+ memset(&data, 0, sizeof(data));
+ data.params_from_edid = true;
+ data.C = 40;
+ data.M = 600;
+ data.K = 128;
+ data.J = 20;
+
+ while (*optarg != '\0') {
+ int opt;
+ double opt_val;
+
+ opt = parse_gtf_subopt(&optarg, &opt_val);
+
+ switch (opt) {
+ case GTF_WIDTH:
+ data.w = round(opt_val);
+ break;
+ case GTF_HEIGHT:
+ data.h = round(opt_val);
+ break;
+ case GTF_FPS:
+ data.freq = opt_val;
+ data.ip_parm = gtf_ip_vert_freq;
+ break;
+ case GTF_HORFREQ:
+ data.freq = opt_val;
+ data.ip_parm = gtf_ip_hor_freq;
+ break;
+ case GTF_PIXCLK:
+ data.freq = opt_val;
+ data.ip_parm = gtf_ip_clk_freq;
+ break;
+ case GTF_INTERLACED:
+ data.interlaced = true;
+ break;
+ case GTF_OVERSCAN:
+ data.overscan = true;
+ break;
+ case GTF_SECONDARY:
+ data.secondary = true;
+ break;
+ case GTF_C2:
+ data.C = opt_val / 2.0;
+ data.params_from_edid = false;
+ break;
+ case GTF_M:
+ data.M = round(opt_val);
+ data.params_from_edid = false;
+ break;
+ case GTF_K:
+ data.K = round(opt_val);
+ data.params_from_edid = false;
+ break;
+ case GTF_J2:
+ data.J = opt_val / 2.0;
+ data.params_from_edid = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!data.w || !data.h) {
+ fprintf(stderr, "Missing width and/or height.\n");
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+ if (!data.freq) {
+ fprintf(stderr, "One of fps, horfreq or pixclk must be given.\n");
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+ if (!data.secondary)
+ data.params_from_edid = false;
+ if (data.interlaced && data.ip_parm == gtf_ip_vert_freq)
+ data.freq /= 2;
+}
+
+static void show_gtf(gtf_parsed_data &data)
+{
+ timings t;
+
+ t = state.calc_gtf_mode(data.w, data.h, data.freq, data.interlaced,
+ data.ip_parm, data.overscan, data.secondary,
+ data.C, data.M, data.K, data.J);
+ calc_ratio(&t);
+ state.print_timings("", &t, "GTF", "", true, false);
+}
+
int main(int argc, char **argv)
{
char short_options[26 * 2 * 2 + 1];
enum output_format out_fmt = OUT_FMT_DEFAULT;
+ gtf_parsed_data gtf_data;
int ret;
while (1) {
int option_index = 0;
unsigned idx = 0;
- unsigned i;
+ unsigned i, val;
+ const timings *t;
+ char buf[16];
for (i = 0; long_options[i].name; i++) {
if (!isalpha(long_options[i].val))
@@ -1243,6 +1564,52 @@ int main(int argc, char **argv)
exit(1);
}
break;
+ case OptSTD: {
+ unsigned char byte1, byte2 = 0;
+ char *endptr;
+
+ byte1 = strtoul(optarg, &endptr, 0);
+ if (*endptr == ',')
+ byte2 = strtoul(endptr + 1, NULL, 0);
+ state.print_standard_timing("", byte1, byte2, false, true);
+ break;
+ }
+ case OptDMT:
+ val = strtoul(optarg, NULL, 0);
+ t = find_dmt_id(val);
+ if (t) {
+ sprintf(buf, "DMT 0x%02x", val);
+ state.print_timings("", t, buf, "", true, false);
+ } else {
+ fprintf(stderr, "Unknown DMT code 0x%02x.\n", val);
+ }
+ break;
+ case OptVIC:
+ val = strtoul(optarg, NULL, 0);
+ t = find_vic_id(val);
+ if (t) {
+ sprintf(buf, "VIC %3u", val);
+ state.print_timings("", t, buf, "", true, false);
+ } else {
+ fprintf(stderr, "Unknown VIC code %u.\n", val);
+ }
+ break;
+ case OptHDMIVIC:
+ val = strtoul(optarg, NULL, 0);
+ t = find_hdmi_vic_id(val);
+ if (t) {
+ sprintf(buf, "HDMI VIC %u", val);
+ state.print_timings("", t, buf, "", true, false);
+ } else {
+ fprintf(stderr, "Unknown HDMI VIC code %u.\n", val);
+ }
+ break;
+ case OptCVT:
+ parse_cvt(optarg);
+ break;
+ case OptGTF:
+ parse_gtf(optarg, gtf_data);
+ break;
case ':':
fprintf(stderr, "Option '%s' requires a value.\n",
argv[optind]);
@@ -1263,6 +1630,28 @@ int main(int argc, char **argv)
return 0;
}
+ if (options[OptListEstTimings])
+ state.list_established_timings();
+ if (options[OptListDMTs])
+ state.list_dmts();
+ if (options[OptListVICs])
+ state.cta_list_vics();
+ if (options[OptListHDMIVICs])
+ state.cta_list_hdmi_vics();
+
+ if (options[OptListEstTimings] || options[OptListDMTs] ||
+ options[OptListVICs] || options[OptListHDMIVICs])
+ return 0;
+
+ if (options[OptCVT] || options[OptDMT] || options[OptVIC] ||
+ options[OptHDMIVIC] || options[OptSTD])
+ return 0;
+
+ if (options[OptGTF] && (!gtf_data.params_from_edid || optind == argc)) {
+ show_gtf(gtf_data);
+ return 0;
+ }
+
if (optind == argc)
ret = edid_from_file("-", stdout);
else
@@ -1275,6 +1664,38 @@ int main(int argc, char **argv)
if (optind < argc - 1)
return ret ? ret : edid_to_file(argv[optind + 1], out_fmt);
+ if (options[OptGTF]) {
+ timings t;
+
+ // Find the Secondary Curve
+ state.preparse_detailed_block(edid + 0x36);
+ state.preparse_detailed_block(edid + 0x48);
+ state.preparse_detailed_block(edid + 0x5a);
+ state.preparse_detailed_block(edid + 0x6c);
+
+ t = state.calc_gtf_mode(gtf_data.w, gtf_data.h, gtf_data.freq,
+ gtf_data.interlaced, gtf_data.ip_parm,
+ gtf_data.overscan);
+ unsigned hbl = t.hfp + t.hsync + t.hbp;
+ unsigned htotal = t.hact + hbl;
+ double hor_freq_khz = htotal ? (double)t.pixclk_khz / htotal : 0;
+
+ if (state.base.supports_sec_gtf &&
+ hor_freq_khz >= state.base.sec_gtf_start_freq) {
+ t = state.calc_gtf_mode(gtf_data.w, gtf_data.h, gtf_data.freq,
+ gtf_data.interlaced, gtf_data.ip_parm,
+ gtf_data.overscan, true,
+ state.base.C, state.base.M,
+ state.base.K, state.base.J);
+ }
+ calc_ratio(&t);
+ if (t.hfp <= 0)
+ state.print_timings("", &t, "GTF", "INVALID: Hfront <= 0", true, false);
+ else
+ state.print_timings("", &t, "GTF", "", true, false);
+ return 0;
+ }
+
return ret ? ret : state.parse_edid();
}
diff --git a/edid-decode.h b/edid-decode.h
index af7af0b..cadcff4 100644
--- a/edid-decode.h
+++ b/edid-decode.h
@@ -22,24 +22,30 @@
#define EDID_PAGE_SIZE 128U
#define EDID_MAX_BLOCKS 256U
-#define RB_FLAG (1U << 7)
+#define RB_FLAG (1U << 7)
+
+#define RB_CVT_V1 (1)
+#define RB_CVT_V2 (2)
+#define RB_CVT_V3 (3)
+#define RB_GTF (4)
// Video Timings
// If interlaced is true, then the vertical blanking
// for each field is (vfp + vsync + vbp + 0.5), except for
// the VIC 39 timings that doesn't have the 0.5 constant.
struct timings {
- // Active horizontal and vertical frame height, including any
+ // Active horizontal and vertical frame height, excluding any
// borders, if present.
// Note: for interlaced formats the active field height is vact / 2
unsigned hact, vact;
unsigned hratio, vratio;
unsigned pixclk_khz;
// 0: no reduced blanking
- // 1: reduced blanking version 1
- // 2: reduced blanking version 2
- // 3: reduced blanking version 3 with a horizontal blank of 80
- // 3 | RB_FLAG: reduced blanking version 3 with a horizontal blank of 160
+ // 1: CVT reduced blanking version 1
+ // 2: CVT reduced blanking version 2
+ // 3: CVT reduced blanking version 3 with a horizontal blank of 80
+ // 3 | RB_FLAG: CVT reduced blanking version 3 with a horizontal blank of 160
+ // 4: GTF Secondary Curve
unsigned rb;
bool interlaced;
// The horizontal frontporch may be negative in GTF calculations,
@@ -90,6 +96,12 @@ struct timings_ext {
std::string flags;
};
+enum gtf_ip_parm {
+ gtf_ip_vert_freq = 1,
+ gtf_ip_hor_freq,
+ gtf_ip_clk_freq,
+};
+
typedef std::vector<timings_ext> vec_timings_ext;
struct edid_state {
@@ -113,6 +125,10 @@ struct edid_state {
base.supports_cvt = base.seen_non_detailed_descriptor =
base.has_640x480p60_est_timing = base.has_spwg =
base.preferred_is_also_native = false;
+ base.supports_sec_gtf = false;
+ base.sec_gtf_start_freq = 0;
+ base.C = base.M = base.K = base.J = 0;
+ base.max_pos_neg_hor_freq_khz = 0;
base.detailed_block_cnt = base.dtd_cnt = 0;
base.min_display_hor_freq_hz = base.max_display_hor_freq_hz =
@@ -181,6 +197,9 @@ struct edid_state {
bool has_serial_string;
bool supports_continuous_freq;
bool supports_gtf;
+ bool supports_sec_gtf;
+ unsigned sec_gtf_start_freq;
+ double C, M, K, J;
bool supports_cvt;
bool has_spwg;
unsigned detailed_block_cnt;
@@ -197,6 +216,7 @@ struct edid_state {
unsigned max_display_pixclk_khz;
unsigned max_display_width_mm;
unsigned max_display_height_mm;
+ unsigned max_pos_neg_hor_freq_khz;
} base;
// CTA-861 block state
@@ -271,12 +291,19 @@ struct edid_state {
detailed, do_checks);
};
bool match_timings(const timings &t1, const timings &t2);
+ timings calc_gtf_mode(unsigned h_pixels, unsigned v_lines,
+ double ip_freq_rqd, bool int_rqd = false,
+ enum gtf_ip_parm ip_parm = gtf_ip_vert_freq,
+ bool margins_rqd = false, bool secondary = false,
+ double C = 40, double M = 600, double K = 128, double J = 20);
void edid_gtf_mode(unsigned refresh, struct timings &t);
+ 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);
void edid_cvt_mode(unsigned refresh, struct timings &t);
- timings calc_cvt_mode(unsigned refresh, unsigned hact, unsigned vact, unsigned rb);
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, unsigned vrefresh_offset = 60);
+ bool gtf_only = false, bool show_both = false);
void detailed_display_range_limits(const unsigned char *x);
void detailed_epi(const unsigned char *x);
void detailed_timings(const char *prefix, const unsigned char *x,
@@ -285,6 +312,8 @@ struct edid_state {
void detailed_block(const unsigned char *x);
void parse_base_block(const unsigned char *x);
void check_base_block();
+ void list_dmts();
+ void list_established_timings();
void print_vic_index(const char *prefix, unsigned idx, const char *suffix, bool ycbcr420 = false);
void cta_vcdb(const unsigned char *x, unsigned length);
@@ -305,6 +334,8 @@ struct edid_state {
void cta_resolve_svr(vec_timings_ext::iterator iter);
void cta_resolve_svrs();
void check_cta_blocks();
+ void cta_list_vics();
+ void cta_list_hdmi_vics();
void parse_digital_interface(const unsigned char *x);
void parse_display_device(const unsigned char *x);
diff --git a/parse-base-block.cpp b/parse-base-block.cpp
index 550b622..7aa27bd 100644
--- a/parse-base-block.cpp
+++ b/parse-base-block.cpp
@@ -354,376 +354,51 @@ static const struct timings *find_std_id(unsigned short std_id, unsigned char &d
return NULL;
}
-/*
- * Copied from xserver/hw/xfree86/modes/xf86gtf.c
- */
-void edid_state::edid_gtf_mode(unsigned refresh, struct timings &t)
-{
-#define CELL_GRAN 8.0 /* assumed character cell granularity */
-#define MIN_PORCH 1 /* minimum front porch */
-#define V_SYNC_RQD 3 /* width of vsync in lines */
-#define H_SYNC_PERCENT 8.0 /* width of hsync as % of total line */
-#define MIN_VSYNC_PLUS_BP 550.0 /* min time of vsync + back porch (microsec) */
-#define M 600.0 /* blanking formula gradient */
-#define C 40.0 /* blanking formula offset */
-#define K 128.0 /* blanking formula scaling factor */
-#define J 20.0 /* blanking formula scaling factor */
-
- /* C' and M' are part of the Blanking Duty Cycle computation */
-
-#define C_PRIME (((C - J) * K/256.0) + J)
-#define M_PRIME (K/256.0 * M)
- double h_pixels_rnd;
- double v_lines_rnd;
- double v_field_rate_rqd;
- double h_period_est;
- double vsync_plus_bp;
- double total_v_lines;
- double v_field_rate_est;
- double h_period;
- double total_active_pixels;
- double ideal_duty_cycle;
- double h_blank;
- double total_pixels;
-
- /* 1. In order to give correct results, the number of horizontal
- * pixels requested is first processed to ensure that it is divisible
- * by the character size, by rounding it to the nearest character
- * cell boundary:
- *
- * [H PIXELS RND] = ((ROUND([H PIXELS]/[CELL GRAN RND],0))*[CELLGRAN RND])
- */
-
- h_pixels_rnd = rint((double)t.hact / CELL_GRAN) * CELL_GRAN;
-
- /* 2. If interlace is requested, the number of vertical lines assumed
- * by the calculation must be halved, as the computation calculates
- * the number of vertical lines per field. In either case, the
- * number of lines is rounded to the nearest integer.
- *
- * [V LINES RND] = IF([INT RQD?]="y", ROUND([V LINES]/2,0),
- * ROUND([V LINES],0))
- */
-
- v_lines_rnd = t.vact;
-
- /* 3. Find the frame rate required:
- *
- * [V FIELD RATE RQD] = IF([INT RQD?]="y", [I/P FREQ RQD]*2,
- * [I/P FREQ RQD])
- */
-
- v_field_rate_rqd = refresh;
-
- /* 7. Estimate the Horizontal period
- *
- * [H PERIOD EST] = ((1/[V FIELD RATE RQD]) - [MIN VSYNC+BP]/1000000) /
- * ([V LINES RND] +
- * [MIN PORCH RND]+[INTERLACE]) * 1000000
- */
-
- h_period_est = (((1.0/v_field_rate_rqd) - (MIN_VSYNC_PLUS_BP/1000000.0))
- / (v_lines_rnd + MIN_PORCH)
- * 1000000.0);
-
- /* 8. Find the number of lines in V sync + back porch:
- *
- * [V SYNC+BP] = ROUND(([MIN VSYNC+BP]/[H PERIOD EST]),0)
- */
-
- vsync_plus_bp = rint(MIN_VSYNC_PLUS_BP/h_period_est);
-
- /* 10. Find the total number of lines in Vertical field period:
- *
- * [TOTAL V LINES] = [V LINES RND] +
- * [V SYNC+BP] + [INTERLACE] +
- * [MIN PORCH RND]
- */
-
- total_v_lines = v_lines_rnd + vsync_plus_bp + MIN_PORCH;
- t.vbp = vsync_plus_bp - V_SYNC_RQD;
- t.vsync = V_SYNC_RQD;
- t.vfp = MIN_PORCH;
-
- /* 11. Estimate the Vertical field frequency:
- *
- * [V FIELD RATE EST] = 1 / [H PERIOD EST] / [TOTAL V LINES] * 1000000
- */
-
- v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0;
-
- /* 12. Find the actual horizontal period:
- *
- * [H PERIOD] = [H PERIOD EST] / ([V FIELD RATE RQD] / [V FIELD RATE EST])
- */
-
- h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est);
-
- /* 17. Find total number of active pixels in image
- *
- * [TOTAL ACTIVE PIXELS] = [H PIXELS RND]
- */
-
- total_active_pixels = h_pixels_rnd;
-
- /* 18. Find the ideal blanking duty cycle from the blanking duty cycle
- * equation:
- *
- * [IDEAL DUTY CYCLE] = [C'] - ([M']*[H PERIOD]/1000)
- */
-
- ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0);
-
- /* 19. Find the number of pixels in the blanking time to the nearest
- * double character cell:
- *
- * [H BLANK (PIXELS)] = (ROUND(([TOTAL ACTIVE PIXELS] *
- * [IDEAL DUTY CYCLE] /
- * (100-[IDEAL DUTY CYCLE]) /
- * (2*[CELL GRAN RND])), 0))
- * * (2*[CELL GRAN RND])
- */
-
- h_blank = rint(total_active_pixels *
- ideal_duty_cycle /
- (100.0 - ideal_duty_cycle) /
- (2.0 * CELL_GRAN)) * (2.0 * CELL_GRAN);
-
- /* 20. Find total number of pixels:
- *
- * [TOTAL PIXELS] = [TOTAL ACTIVE PIXELS] + [H BLANK (PIXELS)]
- */
-
- total_pixels = total_active_pixels + h_blank;
-
- /* 21. Find pixel clock frequency:
- *
- * [PIXEL FREQ] = [TOTAL PIXELS] / [H PERIOD]
- */
-
- t.pixclk_khz = (int)(1000.0 * total_pixels / h_period + 0.5);
-
- /* Stage 1 computations are now complete; I should really pass
- the results to another function and do the Stage 2
- computations, but I only need a few more values so I'll just
- append the computations here for now */
- /* 17. Find the number of pixels in the horizontal sync period:
- *
- * [H SYNC (PIXELS)] =(ROUND(([H SYNC%] / 100 * [TOTAL PIXELS] /
- * [CELL GRAN RND]),0))*[CELL GRAN RND]
- */
- t.hsync = rint(H_SYNC_PERCENT / 100.0 * total_pixels / CELL_GRAN) * CELL_GRAN;
- /* 18. Find the number of pixels in the horizontal front porch period:
- *
- * [H FRONT PORCH (PIXELS)] = ([H BLANK (PIXELS)]/2)-[H SYNC (PIXELS)]
- */
- t.hfp = (h_blank / 2.0) - t.hsync;
- /* 19. Find the number of pixels in the horizontal back porch period:
- *
- * [H BACK PORCH (PIXELS)] = [H FRONT PORCH (PIXELS)]+[H SYNC (PIXELS)]
- */
- t.hbp = t.hfp + t.hsync;
- t.pos_pol_hsync = false;
- t.pos_pol_vsync = true;
- t.interlaced = false;
- t.rb = 0;
-}
-
-/*
- * Copied from xserver/hw/xfree86/modes/xf86cvt.c
- */
-void edid_state::edid_cvt_mode(unsigned refresh, struct timings &t)
+void edid_state::list_established_timings()
{
- int HDisplay = t.hact;
- int VDisplay = t.vact;
-
- /* 2) character cell horizontal granularity (pixels) - default 8 */
-#define CVT_H_GRANULARITY 8
-
- /* 4) Minimum vertical porch (lines) - default 3 */
-#define CVT_MIN_V_PORCH 3
-
- /* 4) Minimum number of vertical back porch lines - default 6 */
-#define CVT_MIN_V_BPORCH 6
-
- /* Pixel Clock step (kHz) */
-#define CVT_CLOCK_STEP 250
-
- double HPeriod;
- int VDisplayRnd, VSync;
- double VFieldRate = refresh;
- int HTotal, VTotal, Clock, HSyncStart, HSyncEnd, VSyncStart, VSyncEnd;
-
- /* 2. Horizontal pixels */
- HDisplay = HDisplay - (HDisplay % CVT_H_GRANULARITY);
-
- /* 5. Find number of lines per field */
- VDisplayRnd = VDisplay;
-
- /* Determine VSync Width from aspect ratio */
- if ((VDisplay * 4 / 3) == HDisplay)
- VSync = 4;
- else if ((VDisplay * 16 / 9) == HDisplay)
- VSync = 5;
- else if ((VDisplay * 16 / 10) == HDisplay)
- VSync = 6;
- else if (!(VDisplay % 4) && ((VDisplay * 5 / 4) == HDisplay))
- VSync = 7;
- else if ((VDisplay * 15 / 9) == HDisplay)
- VSync = 7;
- else /* Custom */
- VSync = 10;
-
- if (!t.rb) { /* simplified GTF calculation */
- /* 4) Minimum time of vertical sync + back porch interval (µs)
- * default 550.0 */
-#define CVT_MIN_VSYNC_BP 550.0
-
- /* 3) Nominal HSync width (% of line period) - default 8 */
-#define CVT_HSYNC_PERCENTAGE 8
-
- double HBlankPercentage;
- int VSyncAndBackPorch;
- int HBlank;
-
- /* 8. Estimated Horizontal period */
- HPeriod = ((double) (1000000.0 / VFieldRate - CVT_MIN_VSYNC_BP)) /
- (VDisplayRnd + CVT_MIN_V_PORCH);
-
- /* 9. Find number of lines in sync + backporch */
- if (((int) (CVT_MIN_VSYNC_BP / HPeriod) + 1) <
- (VSync + CVT_MIN_V_BPORCH))
- VSyncAndBackPorch = VSync + CVT_MIN_V_BPORCH;
- else
- VSyncAndBackPorch = (int) (CVT_MIN_VSYNC_BP / HPeriod) + 1;
-
- VTotal = VDisplayRnd + VSyncAndBackPorch + CVT_MIN_V_PORCH;
-
- /* 5) Definition of Horizontal blanking time limitation */
- /* Gradient (%/kHz) - default 600 */
-#define CVT_M_FACTOR 600.0
-
- /* Offset (%) - default 40 */
-#define CVT_C_FACTOR 40.0
-
- /* Blanking time scaling factor - default 128 */
-#define CVT_K_FACTOR 128.0
-
- /* Scaling factor weighting - default 20 */
-#define CVT_J_FACTOR 20.0
-
-#define CVT_M_PRIME (CVT_M_FACTOR * CVT_K_FACTOR / 256.0)
-#define CVT_C_PRIME ((CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256.0 + \
- CVT_J_FACTOR)
-
- /* 12. Find ideal blanking duty cycle from formula */
- HBlankPercentage = CVT_C_PRIME - CVT_M_PRIME * HPeriod / 1000.0;
-
- /* 13. Blanking time */
- if (HBlankPercentage < 20)
- HBlankPercentage = 20;
-
- HBlank = (double)HDisplay * HBlankPercentage / (100.0 - HBlankPercentage) / (2.0 * CVT_H_GRANULARITY);
- HBlank *= 2 * CVT_H_GRANULARITY;
-
- /* 14. Find total number of pixels in a line. */
- HTotal = HDisplay + HBlank;
-
- int HSync = (HTotal * CVT_HSYNC_PERCENTAGE) / 100.0 + 0.0;
- //printf("%d %d %d\n", HTotal, HBlank, HSync);
- HSync -= HSync % CVT_H_GRANULARITY;
-
- /* Fill in HSync values */
- HSyncEnd = HTotal - HBlank / 2;
-
- HSyncStart = HSyncEnd - HSync;
- VSyncStart = VDisplayRnd + CVT_MIN_V_PORCH;
- VSyncEnd = VSyncStart + VSync;
+ printf("Established Timings I & II:\n\n");
+ for (unsigned i = 0; i < ARRAY_SIZE(established_timings12); i++) {
+ unsigned char dmt_id = established_timings12[i].dmt_id;
+ const struct timings *t;
+ char type[16];
- /* 15/13. Find pixel clock frequency (kHz) */
- Clock = ((double)HTotal / HPeriod) * 1000.0;
- Clock -= Clock % CVT_CLOCK_STEP;
+ if (dmt_id) {
+ sprintf(type, "DMT 0x%02x", dmt_id);
+ t = find_dmt_id(dmt_id);
+ } else {
+ t = &established_timings12[i].t;
+ sprintf(type, "%-8s", established_timings12[i].type);
+ }
+ print_timings("", t, type, "", false, false);
}
- else { /* Reduced blanking */
- /* Minimum vertical blanking interval time (µs) - default 460 */
-#define CVT_RB_MIN_VBLANK 460.0
-
- /* Fixed number of clocks for horizontal sync */
-#define CVT_RB_H_SYNC 32.0
-
- /* Fixed number of clocks for horizontal blanking */
-#define CVT_RB1_H_BLANK 160.0 // RB1 & RB3 with RB_FLAG set
-#define CVT_RB2_H_BLANK 80.0 // RB2 & RB3 with RB_FLAG cleared
-
- /* Fixed number of lines for vertical front porch - default 3 */
-#define CVT_RB1_V_FPORCH 3
-#define CVT_RB2_V_FPORCH 1
-#define CVT_RB3_V_FIELD_RATE_PPM_ADJ 350.0
-#define CVT_RB2_CLOCK_STEP 1
-
- int VBILines;
- double h_blank = (t.rb & ~RB_FLAG) == 1 ? CVT_RB1_H_BLANK : CVT_RB2_H_BLANK;
- int v_fporch = t.rb == 1 ? CVT_RB1_V_FPORCH : CVT_RB2_V_FPORCH;
- unsigned clock_step = t.rb == 1 ? CVT_CLOCK_STEP : CVT_RB2_CLOCK_STEP;
-
- if (t.rb == 3)
- VFieldRate += VFieldRate * (CVT_RB3_V_FIELD_RATE_PPM_ADJ / 1000000.0);
-
- /* 8. Estimate Horizontal period. */
- HPeriod = ((double) (1000000.0 / VFieldRate - CVT_RB_MIN_VBLANK)) / VDisplayRnd;
-
- /* 9. Find number of lines in vertical blanking */
- VBILines = ((double) CVT_RB_MIN_VBLANK) / HPeriod;
- VBILines++;
-
- /* 10. Check if vertical blanking is sufficient */
- if (VBILines < (v_fporch + VSync + CVT_MIN_V_BPORCH))
- VBILines = v_fporch + VSync + CVT_MIN_V_BPORCH;
-
- /* 11. Find total number of lines in vertical field */
- VTotal = VDisplayRnd + VBILines;
-
- /* 12. Find total number of pixels in a line */
- HTotal = HDisplay + h_blank;
-
- /* Fill in HSync values */
- HSyncEnd = HDisplay + h_blank / 2;
- HSyncStart = HSyncEnd - CVT_RB_H_SYNC;
-
- /* Fill in VSync values */
- VSyncStart = VDisplay + v_fporch;
- VSyncEnd = VSyncStart + VSync;
+ printf("\nEstablished timings III:\n\n");
+ for (unsigned i = 0; i < ARRAY_SIZE(established_timings3_dmt_ids); i++) {
+ unsigned char dmt_id = established_timings3_dmt_ids[i];
+ char type[16];
- /* 15/13. Find pixel clock frequency (kHz) */
- double clk_khz = ((double)VFieldRate * VTotal * HTotal) / 1000.0;
- if (t.rb < 3)
- Clock = clock_step * floor(clk_khz / clock_step);
- else
- Clock = clock_step * ceil(clk_khz / clock_step);
+ sprintf(type, "DMT 0x%02x", dmt_id);
+ print_timings("", find_dmt_id(dmt_id), type, "", false, false);
}
- t.pixclk_khz = Clock;
-
- t.pos_pol_hsync = t.rb;
- t.pos_pol_vsync = !t.rb;
- t.vfp = VSyncStart - VDisplay;
- t.vsync = VSyncEnd - VSyncStart;
- t.vbp = VTotal - VSyncEnd;
- t.hfp = HSyncStart - HDisplay;
- t.hsync = HSyncEnd - HSyncStart;
- t.hbp = HTotal - HSyncEnd;
- t.interlaced = false;
}
-timings edid_state::calc_cvt_mode(unsigned refresh, unsigned hact, unsigned vact, unsigned rb)
+void edid_state::list_dmts()
{
- timings t = {};
-
- t.hact = hact;
- t.vact = vact;
- t.rb = rb;
- calc_ratio(&t);
- edid_cvt_mode(refresh, t);
- return t;
+ char type[16];
+
+ for (unsigned i = 0; i < ARRAY_SIZE(dmt_timings); i++) {
+ sprintf(type, "DMT 0x%02x", dmt_timings[i].dmt_id);
+ std::string flags;
+ if (dmt_timings[i].std_id)
+ flags += std::string("STD: ") +
+ utohex(dmt_timings[i].std_id >> 8) + " " +
+ utohex(dmt_timings[i].std_id & 0xff);
+ if (dmt_timings[i].cvt_id)
+ add_str(flags, std::string("CVT: ") +
+ utohex(dmt_timings[i].cvt_id >> 16) + " " +
+ utohex((dmt_timings[i].cvt_id >> 8) & 0xff) + " " +
+ utohex(dmt_timings[i].cvt_id & 0xff));
+ print_timings("", &dmt_timings[i].t, type, flags.c_str(), false, false);
+ }
}
void edid_state::detailed_cvt_descriptor(const char *prefix, const unsigned char *x, bool first)
@@ -794,7 +469,7 @@ void edid_state::detailed_cvt_descriptor(const char *prefix, const unsigned char
print_timings(prefix, &cvt_t, "CVT", preferred == 3 ? s_pref : "");
}
if (x[2] & 0x01) {
- cvt_t.rb = 1;
+ cvt_t.rb = RB_CVT_V1;
edid_cvt_mode(60, cvt_t);
print_timings(prefix, &cvt_t, "CVT", preferred == 4 ? s_pref : "");
}
@@ -838,7 +513,7 @@ char *extract_string(const unsigned char *x, unsigned len)
}
void edid_state::print_standard_timing(const char *prefix, unsigned char b1, unsigned char b2,
- bool gtf_only, unsigned vrefresh_offset)
+ bool gtf_only, bool show_both)
{
const struct timings *t;
struct timings formula = {};
@@ -862,7 +537,7 @@ void edid_state::print_standard_timing(const char *prefix, unsigned char b1, uns
hact = (b1 + 31) * 8;
switch ((b2 >> 6) & 0x3) {
case 0x00:
- if (gtf_only || base.edid_minor >= 3) {
+ if (gtf_only || show_both || base.edid_minor >= 3) {
hratio = 16;
vratio = 10;
} else {
@@ -885,17 +560,18 @@ void edid_state::print_standard_timing(const char *prefix, unsigned char b1, uns
}
vact = (double)hact * vratio / hratio;
vact = 8 * ((vact + 7) / 8);
- refresh = vrefresh_offset + (b2 & 0x3f);
+ refresh = (b2 & 0x3f) + 60;
formula.hact = hact;
formula.vact = vact;
formula.hratio = hratio;
formula.vratio = vratio;
- if (!gtf_only && base.edid_minor >= 4) {
- if (base.supports_cvt) {
+ if (!gtf_only && (show_both || base.edid_minor >= 4)) {
+ if (show_both || base.supports_cvt) {
edid_cvt_mode(refresh, formula);
- print_timings(prefix, &formula, "CVT ", "EDID 1.4 source");
+ print_timings(prefix, &formula, "CVT ",
+ show_both ? "" : "EDID 1.4 source");
}
/*
* An EDID 1.3 source will assume GTF, so both GTF and CVT
@@ -1380,8 +1056,16 @@ void edid_state::preparse_detailed_block(const unsigned char *x)
switch (x[10]) {
case 0x00: /* default gtf */
+ base.supports_gtf = true;
+ break;
case 0x02: /* secondary gtf curve */
base.supports_gtf = true;
+ base.supports_sec_gtf = !memchk(x + 12, 6);
+ base.sec_gtf_start_freq = x[12] * 2;
+ base.C = x[13] / 2.0;
+ base.M = (x[15] << 8) | x[14];
+ base.K = x[16];
+ base.J = x[17] / 2.0;
break;
case 0x04: /* cvt */
if (base.edid_minor >= 4) {
@@ -1446,7 +1130,7 @@ void edid_state::detailed_block(const unsigned char *x)
case 0xf7:
data_block = "Established timings III";
printf(" %s:\n", data_block.c_str());
- for (i = 0; i < 44; i++)
+ for (i = 0; i < ARRAY_SIZE(established_timings3_dmt_ids); i++)
if (x[6 + i / 8] & (1 << (7 - i % 8))) {
unsigned char dmt_id = established_timings3_dmt_ids[i];
char type[16];
@@ -1579,7 +1263,7 @@ void edid_state::parse_base_block(const unsigned char *x)
{
time_t the_time;
struct tm *ptm;
- int analog, i;
+ int analog;
unsigned col_x, col_y;
bool has_preferred_timing = false;
@@ -1813,7 +1497,7 @@ void edid_state::parse_base_block(const unsigned char *x)
data_block = "Established Timings I & II";
if (x[0x23] || x[0x24] || x[0x25]) {
printf(" %s:\n", data_block.c_str());
- for (i = 0; i < 17; i++) {
+ for (unsigned i = 0; i < ARRAY_SIZE(established_timings12); i++) {
if (x[0x23 + i / 8] & (1 << (7 - i % 8))) {
unsigned char dmt_id = established_timings12[i].dmt_id;
const struct timings *t;
@@ -1845,7 +1529,7 @@ void edid_state::parse_base_block(const unsigned char *x)
data_block = "Standard Timings";
bool found = false;
- for (i = 0; i < 8; i++) {
+ for (unsigned i = 0; i < 8; i++) {
if (x[0x26 + i * 2] != 0x01 || x[0x26 + i * 2 + 1] != 0x01) {
found = true;
break;
@@ -1853,7 +1537,7 @@ void edid_state::parse_base_block(const unsigned char *x)
}
if (found) {
printf(" %s:\n", data_block.c_str());
- for (i = 0; i < 8; i++)
+ for (unsigned i = 0; i < 8; i++)
print_standard_timing(" ", x[0x26 + i * 2], x[0x26 + i * 2 + 1]);
} else {
printf(" %s: none\n", data_block.c_str());
@@ -1973,6 +1657,12 @@ void edid_state::check_base_block()
dtd_max_hsize_mm <= 2559 && dtd_max_vsize_mm <= 2559) {
fail("The DTD image sizes all fit inside 255x255cm, but no display size was set.\n");
}
+ // Secondary GTF curves start at a specific frequency. Any legacy timings
+ // that have a positive hsync and negative vsync must be less than that
+ // frequency to avoid confusion.
+ if (base.supports_sec_gtf && base.max_pos_neg_hor_freq_khz >= base.sec_gtf_start_freq)
+ fail("Second GTF start frequency %u is less than the highest P/N frequency %u.\n",
+ base.sec_gtf_start_freq, base.max_pos_neg_hor_freq_khz);
if (base.edid_minor == 3 && num_blocks > 2 && !block_map.saw_block_1)
fail("EDID 1.3 requires a Block Map Extension in Block 1 if there are more than 2 blocks in the EDID.\n");
if (base.edid_minor == 3 && num_blocks > 128 && !block_map.saw_block_128)
diff --git a/parse-cta-block.cpp b/parse-cta-block.cpp
index e3f0dfd..f881a2b 100644
--- a/parse-cta-block.cpp
+++ b/parse-cta-block.cpp
@@ -201,7 +201,7 @@ const struct timings *find_vic_id(unsigned char vic)
{
if (vic > 0 && vic <= ARRAY_SIZE(edid_cta_modes1))
return edid_cta_modes1 + vic - 1;
- if (vic >= 193 && vic <= ARRAY_SIZE(edid_cta_modes2) + 193)
+ if (vic >= 193 && vic < ARRAY_SIZE(edid_cta_modes2) + 193)
return edid_cta_modes2 + vic - 193;
return NULL;
}
@@ -213,6 +213,30 @@ const struct timings *find_hdmi_vic_id(unsigned char hdmi_vic)
return NULL;
}
+void edid_state::cta_list_vics()
+{
+ char type[16];
+ for (unsigned vic = 1; vic <= ARRAY_SIZE(edid_cta_modes1); vic++) {
+ sprintf(type, "VIC %3u", vic);
+ print_timings("", &edid_cta_modes1[vic - 1], type, "", false, false);
+ }
+ for (unsigned vic = 193; vic < ARRAY_SIZE(edid_cta_modes2) + 193; vic++) {
+ sprintf(type, "VIC %3u", vic);
+ print_timings("", &edid_cta_modes2[vic - 193], type, "", false, false);
+ }
+}
+
+void edid_state::cta_list_hdmi_vics()
+{
+ for (unsigned i = 0; i < ARRAY_SIZE(edid_hdmi_mode_map); i++) {
+ unsigned vic = edid_hdmi_mode_map[i];
+ char type[16];
+
+ sprintf(type, "HDMI VIC %u", i + 1);
+ print_timings("", find_vic_id(vic), type, "", false, false);
+ }
+}
+
static std::string audio_ext_format(unsigned char x)
{
if (x >= 1 && x <= 3)
diff --git a/parse-displayid-block.cpp b/parse-displayid-block.cpp
index ca3e73d..a1c0eee 100644
--- a/parse-displayid-block.cpp
+++ b/parse-displayid-block.cpp
@@ -336,14 +336,15 @@ void edid_state::parse_displayid_type_1_7_timing(const unsigned char *x,
unsigned vtot = t.vact + t.vfp + t.vsync + t.vbp;
unsigned refresh = (t.pixclk_khz * 1000ULL) / (htot * vtot);
- for (unsigned rb = 0; rb <= 3; rb++) {
- timings cvt_t = calc_cvt_mode(refresh, t.hact, t.vact, rb);
+ for (unsigned rb = 0; rb <= RB_CVT_V3; rb++) {
+ timings cvt_t = calc_cvt_mode(t.hact, t.vact, refresh, rb);
if (match_timings(t, cvt_t)) {
fail("This T7VTDB can be represented as a T10VTDB.\n");
return;
}
}
- timings cvt_t = calc_cvt_mode(refresh, t.hact, t.vact, 3 | RB_FLAG);
+ timings cvt_t = calc_cvt_mode(t.hact, t.vact, refresh, RB_CVT_V3,
+ false, false, true);
if (match_timings(t, cvt_t))
fail("This T7VTDB can be represented as a T10VTDB.\n");
}
@@ -461,7 +462,7 @@ void edid_state::parse_displayid_type_3_timing(const unsigned char *x)
break;
}
- t.rb = ((x[0] & 0x70) >> 4) == 1;
+ t.rb = ((x[0] & 0x70) >> 4) == 1 ? RB_CVT_V1 : 0;
t.hact = 8 + 8 * x[1];
t.vact = t.hact * t.vratio / t.hratio;
@@ -939,7 +940,7 @@ void edid_state::parse_displayid_type_5_timing(const unsigned char *x)
if (x[0] & 0x10)
s += ", refresh rate * (1000/1001) supported";
- t.rb = 2;
+ t.rb = RB_CVT_V2;
if ((x[0] & 0x03) == 1)
warn("Unexpected use of 'custom reduced blanking'.\n");
else if ((x[0] & 0x03) > 1)
@@ -1223,8 +1224,8 @@ void edid_state::parse_displayid_type_9_timing(const unsigned char *x)
s += ", refresh rate * (1000/1001) supported";
switch (x[0] & 0x07) {
- case 1: t.rb = 1; break;
- case 2: t.rb = 2; break;
+ case 1: t.rb = RB_CVT_V1; break;
+ case 2: t.rb = RB_CVT_V2; break;
default: break;
}
@@ -1397,16 +1398,17 @@ void edid_state::parse_displayid_type_10_timing(const unsigned char *x, bool is_
}
switch (x[0] & 0x07) {
- case 1: t.rb = 1; break;
- case 2: t.rb = 2; break;
- case 3: t.rb = 3; break;
+ case 1: t.rb = RB_CVT_V1; break;
+ case 2: t.rb = RB_CVT_V2; break;
+ case 3: t.rb = RB_CVT_V3; break;
default: break;
}
if (x[0] & 0x10) {
- if (t.rb == 2) {
+ if (t.rb == RB_CVT_V2) {
s += ", refresh rate * (1000/1001) supported";
- } else if (t.rb == 3) {
+ t.rb |= RB_FLAG;
+ } else if (t.rb == RB_CVT_V3) {
s += ", hblank is 160 pixels";
t.rb |= RB_FLAG;
} else {
diff --git a/parse-vtb-ext-block.cpp b/parse-vtb-ext-block.cpp
index 4936f1e..05d54f4 100644
--- a/parse-vtb-ext-block.cpp
+++ b/parse-vtb-ext-block.cpp
@@ -29,12 +29,14 @@ void edid_state::parse_vtb_ext_block(const unsigned char *x)
detailed_cvt_descriptor(" ", x, false);
}
if (num_st) {
+ // Note: the VTB-EXT standard has a mistake in the example EDID
+ // that it provides: there the refresh rate (bits 5-0 of the
+ // second byte) is set to 60 for 60 Hz, but this should be 0
+ // since the actual refresh rate is the value + 60.
+ //
+ // The documentation itself is correct, though.
printf(" Standard Timings:\n");
- for (unsigned i = 0; i < num_st; i++, x += 2) {
- if ((x[1] & 0x3f) >= 60)
- print_standard_timing(" ", x[0], x[1] - 60, true);
- else
- print_standard_timing(" ", x[0], x[1], true, 0);
- }
+ for (unsigned i = 0; i < num_st; i++, x += 2)
+ print_standard_timing(" ", x[0], x[1], true);
}
}

Privacy Policy