aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil-cisco@xs4all.nl>2020-11-28 09:38:52 +0100
committerHans Verkuil <hverkuil-cisco@xs4all.nl>2021-02-15 13:25:51 +0100
commitaf996e6391676750d8d37b1ff7690f0936e54b3d (patch)
tree7e16bae06ef6685edae375411fb803e9ed876db2
parent7b02132df3e9fb0d947e7097a2f99973662cfe44 (diff)
edid-decode: show or calculate timings
Add new options to show all Established Timings, DMTs, VICs and HDMI VICs. Add new options to show the timings for specific Standard Timing codes, or DMT, VIC and HDMI VIC codes. Add new options to calculate GTF and CVT timings, fully implementing the GTF and CVT standards, including interlaced, overscan, reduced blanking and support for the GTF Secondary Curve. Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
-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