edid-decode.1: document the --infoframe option
[edid-decode.git] / calc-gtf-cvt.cpp
blob9bf31437b96d482527d2ec6eb2b1fbd45327845f
1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright 2006-2012 Red Hat, Inc.
4 * Copyright 2018-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6 * Author: Adam Jackson <ajax@nwnk.net>
7 * Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
8 */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <math.h>
13 #include <time.h>
15 #include "edid-decode.h"
17 #define CELL_GRAN 8.0
18 #define MARGIN_PERC 1.8
19 #define GTF_MIN_PORCH 1.0
20 #define GTF_V_SYNC_RQD 3.0
21 #define GTF_H_SYNC_PERC 8.0
22 #define GTF_MIN_VSYNC_BP 550.0
24 timings edid_state::calc_gtf_mode(unsigned h_pixels, unsigned v_lines,
25 double ip_freq_rqd, bool int_rqd,
26 enum gtf_ip_parm ip_parm, bool margins_rqd,
27 bool secondary, double C, double M, double K, double J)
29 timings t = {};
30 /* C' and M' are part of the Blanking Duty Cycle computation */
31 double C_PRIME = ((C - J) * K / 256.0) + J;
32 double M_PRIME = K / 256.0 * M;
34 double h_pixels_rnd = round(h_pixels / CELL_GRAN) * CELL_GRAN;
35 double v_lines_rnd = int_rqd ? round(v_lines / 2.0) : v_lines;
36 unsigned hor_margin = margins_rqd ?
37 round(h_pixels_rnd * MARGIN_PERC / 100.0 / CELL_GRAN) * CELL_GRAN : 0;
38 unsigned vert_margin = margins_rqd ? round(MARGIN_PERC / 100.0 * v_lines_rnd) : 0;
39 double interlace = int_rqd ? 0.5 : 0;
40 double total_active_pixels = h_pixels_rnd + hor_margin * 2;
42 t.hact = h_pixels_rnd;
43 t.vact = v_lines;
44 t.interlaced = int_rqd;
46 double pixel_freq;
47 double h_blank_pixels;
48 double total_pixels;
49 double v_sync_bp;
51 if (ip_parm == gtf_ip_vert_freq) {
52 // vertical frame frequency (Hz)
53 double v_field_rate_rqd = int_rqd ? ip_freq_rqd * 2 : ip_freq_rqd;
54 double h_period_est = ((1.0 / v_field_rate_rqd) - GTF_MIN_VSYNC_BP / 1000000.0) /
55 (v_lines_rnd + vert_margin * 2 + GTF_MIN_PORCH + interlace) * 1000000.0;
56 v_sync_bp = round(GTF_MIN_VSYNC_BP / h_period_est);
57 double total_v_lines = v_lines_rnd + vert_margin * 2 +
58 v_sync_bp + interlace + GTF_MIN_PORCH;
59 double v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0;
60 double h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est);
61 double ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0);
62 h_blank_pixels = round(total_active_pixels * ideal_duty_cycle /
63 (100.0 - ideal_duty_cycle) /
64 (2 * CELL_GRAN)) * 2 * CELL_GRAN;
65 total_pixels = total_active_pixels + h_blank_pixels;
66 pixel_freq = total_pixels / h_period;
67 } else if (ip_parm == gtf_ip_hor_freq) {
68 // horizontal frequency (kHz)
69 double h_freq = ip_freq_rqd;
70 v_sync_bp = round(GTF_MIN_VSYNC_BP * h_freq / 1000.0);
71 double ideal_duty_cycle = C_PRIME - (M_PRIME / h_freq);
72 h_blank_pixels = round(total_active_pixels * ideal_duty_cycle /
73 (100.0 - ideal_duty_cycle) /
74 (2 * CELL_GRAN)) * 2 * CELL_GRAN;
75 total_pixels = total_active_pixels + h_blank_pixels;
76 pixel_freq = total_pixels * h_freq / 1000.0;
77 } else {
78 // pixel clock rate (MHz)
79 pixel_freq = ip_freq_rqd;
80 double ideal_h_period =
81 ((C_PRIME - 100.0) +
82 sqrt(((100.0 - C_PRIME) * (100.0 - C_PRIME) +
83 (0.4 * M_PRIME * (total_active_pixels + hor_margin * 2) /
84 pixel_freq)))) / 2.0 / M_PRIME * 1000.0;
85 double ideal_duty_cycle = C_PRIME - (M_PRIME * ideal_h_period) / 1000.0;
86 h_blank_pixels = round(total_active_pixels * ideal_duty_cycle /
87 (100.0 - ideal_duty_cycle) /
88 (2 * CELL_GRAN)) * 2 * CELL_GRAN;
89 total_pixels = total_active_pixels + h_blank_pixels;
90 double h_freq = pixel_freq / total_pixels * 1000.0;
91 v_sync_bp = round(GTF_MIN_VSYNC_BP * h_freq / 1000.0);
94 double v_back_porch = v_sync_bp - GTF_V_SYNC_RQD;
96 t.vbp = v_back_porch;
97 t.vsync = GTF_V_SYNC_RQD;
98 t.vfp = GTF_MIN_PORCH;
99 t.pixclk_khz = round(1000.0 * pixel_freq);
100 t.hsync = round(GTF_H_SYNC_PERC / 100.0 * total_pixels / CELL_GRAN) * CELL_GRAN;
101 t.hfp = (h_blank_pixels / 2.0) - t.hsync;
102 t.hbp = t.hfp + t.hsync;
103 t.hborder = hor_margin;
104 t.vborder = vert_margin;
105 t.pos_pol_hsync = secondary;
106 t.pos_pol_vsync = !secondary;
107 t.rb = secondary ? RB_GTF : RB_NONE;
108 return t;
111 void edid_state::edid_gtf_mode(unsigned refresh, struct timings &t)
113 unsigned hratio = t.hratio;
114 unsigned vratio = t.vratio;
115 t = calc_gtf_mode(t.hact, t.vact, refresh, t.interlaced);
116 t.hratio = hratio;
117 t.vratio = vratio;
120 #define CVT_MIN_VSYNC_BP 550.0
121 #define CVT_MIN_V_PORCH 3
122 /* Minimum vertical backporch for CVT and CVT RBv1 */
123 #define CVT_MIN_V_BPORCH 7
124 /* Fixed vertical backporch for CVT RBv2 and RBv3 */
125 #define CVT_FIXED_V_BPORCH 6
126 #define CVT_C_PRIME 30.0
127 #define CVT_M_PRIME 300.0
128 #define CVT_RB_MIN_VBLANK 460.0
129 #define CVT_RB_ALT_MIN_VBLANK 300.0
131 // If rb == RB_CVT_V2, then alt means video-optimized (i.e. 59.94 instead of 60 Hz, etc.).
132 // If rb == RB_CVT_V3, then alt means that rb_h_blank is 160 instead of 80.
133 timings edid_state::calc_cvt_mode(unsigned h_pixels, unsigned v_lines,
134 double ip_freq_rqd, unsigned rb, bool int_rqd,
135 bool margins_rqd, bool alt, unsigned rb_h_blank,
136 unsigned rb_v_blank, bool early_vsync_rqd)
138 timings t = {};
140 t.hact = h_pixels;
141 t.vact = v_lines;
142 t.interlaced = int_rqd;
144 if (rb == RB_CVT_V3) {
145 if (rb_v_blank < CVT_RB_ALT_MIN_VBLANK)
146 rb_v_blank = CVT_RB_ALT_MIN_VBLANK;
147 else if (rb_v_blank > CVT_RB_ALT_MIN_VBLANK + 140 &&
148 rb_v_blank < CVT_RB_MIN_VBLANK)
149 rb_v_blank = CVT_RB_MIN_VBLANK;
150 else if (rb_v_blank > CVT_RB_MIN_VBLANK + 460)
151 rb_v_blank = CVT_RB_MIN_VBLANK + 460;
154 double cell_gran = rb == RB_CVT_V2 ? 1 : CELL_GRAN;
155 double h_pixels_rnd = floor(h_pixels / cell_gran) * cell_gran;
156 double v_lines_rnd = int_rqd ? floor(v_lines / 2.0) : v_lines;
157 unsigned hor_margin = margins_rqd ?
158 floor((h_pixels_rnd * MARGIN_PERC / 100.0) / cell_gran) * cell_gran : 0;
159 unsigned vert_margin = margins_rqd ? floor(MARGIN_PERC / 100.0 * v_lines_rnd) : 0;
160 double interlace = int_rqd ? 0.5 : 0;
161 double total_active_pixels = h_pixels_rnd + hor_margin * 2;
162 double v_field_rate_rqd = int_rqd ? ip_freq_rqd * 2 : ip_freq_rqd;
163 double clock_step = rb >= RB_CVT_V2 ? 0.001 : 0.25;
164 double h_blank = (rb == RB_CVT_V1 || (rb == RB_CVT_V3 && alt)) ? 160 : 80;
165 double rb_v_fporch = rb == RB_CVT_V1 ? 3 : 1;
166 double refresh_multiplier = (rb == RB_CVT_V2 && alt) ? 1000.0 / 1001.0 : 1;
167 double rb_min_vblank = rb == RB_CVT_V3 ? rb_v_blank : CVT_RB_MIN_VBLANK;
168 double h_sync = 32;
170 double v_sync;
171 double pixel_freq;
172 double v_blank;
173 double v_sync_bp;
175 if (rb == RB_CVT_V3 && rb_h_blank) {
176 h_blank = rb_h_blank & ~7;
177 if (h_blank < 80)
178 h_blank = 80;
179 else if (h_blank > 200)
180 h_blank = 200;
183 /* Determine VSync Width from aspect ratio */
184 if ((t.vact * 4 / 3) == t.hact)
185 v_sync = 4;
186 else if ((t.vact * 16 / 9) == t.hact)
187 v_sync = 5;
188 else if ((t.vact * 16 / 10) == t.hact)
189 v_sync = 6;
190 else if (!(t.vact % 4) && ((t.vact * 5 / 4) == t.hact))
191 v_sync = 7;
192 else if ((t.vact * 15 / 9) == t.hact)
193 v_sync = 7;
194 else /* Custom */
195 v_sync = 10;
197 if (rb >= RB_CVT_V2)
198 v_sync = 8;
200 if (rb == RB_NONE) {
201 double h_period_est = ((1.0 / v_field_rate_rqd) - CVT_MIN_VSYNC_BP / 1000000.0) /
202 (v_lines_rnd + vert_margin * 2 + CVT_MIN_V_PORCH + interlace) * 1000000.0;
203 v_sync_bp = floor(CVT_MIN_VSYNC_BP / h_period_est) + 1;
204 if (v_sync_bp < v_sync + CVT_MIN_V_BPORCH)
205 v_sync_bp = v_sync + CVT_MIN_V_BPORCH;
206 v_blank = v_sync_bp + CVT_MIN_V_PORCH;
207 double ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * h_period_est / 1000.0);
208 if (ideal_duty_cycle < 20)
209 ideal_duty_cycle = 20;
210 h_blank = floor(total_active_pixels * ideal_duty_cycle /
211 (100.0 - ideal_duty_cycle) /
212 (2 * CELL_GRAN)) * 2 * CELL_GRAN;
213 double total_pixels = total_active_pixels + h_blank;
214 h_sync = floor(total_pixels * 0.08 / CELL_GRAN) * CELL_GRAN;
215 pixel_freq = floor((total_pixels / h_period_est) / clock_step) * clock_step;
216 } else {
217 double h_period_est = ((1000000.0 / v_field_rate_rqd) - rb_min_vblank) /
218 (v_lines_rnd + vert_margin * 2);
219 double vbi_lines = floor(rb_min_vblank / h_period_est) + 1;
220 double rb_v_bporch = (rb == RB_CVT_V1 ? CVT_MIN_V_BPORCH : CVT_FIXED_V_BPORCH);
221 double rb_min_vbi = rb_v_fporch + v_sync + rb_v_bporch;
222 v_blank = vbi_lines < rb_min_vbi ? rb_min_vbi : vbi_lines;
223 double total_v_lines = v_blank + v_lines_rnd + vert_margin * 2 + interlace;
224 if (rb == RB_CVT_V3 && early_vsync_rqd) {
225 rb_v_bporch = floor(vbi_lines / 2.0);
226 if (v_blank - rb_v_bporch - v_sync < rb_v_fporch)
227 rb_v_bporch = v_blank - v_sync - rb_v_fporch;
229 if (rb == RB_CVT_V1)
230 v_sync_bp = v_blank - rb_v_fporch;
231 else
232 v_sync_bp = v_sync + rb_v_bporch;
233 double total_pixels = h_blank + total_active_pixels;
234 double freq = v_field_rate_rqd * total_v_lines * total_pixels * refresh_multiplier;
235 if (rb == RB_CVT_V3)
236 pixel_freq = ceil((freq / 1000000.0) / clock_step) * clock_step;
237 else
238 pixel_freq = floor((freq / 1000000.0) / clock_step) * clock_step;
241 t.vbp = v_sync_bp - v_sync;
242 t.vsync = v_sync;
243 t.vfp = v_blank - t.vbp - t.vsync;
244 t.pixclk_khz = round(1000.0 * pixel_freq);
245 t.hsync = h_sync;
246 if (rb >= RB_CVT_V2)
247 t.hfp = 8;
248 else
249 t.hfp = (h_blank / 2.0) - t.hsync;
250 t.hbp = h_blank - t.hfp - t.hsync;
251 t.hborder = hor_margin;
252 t.vborder = vert_margin;
253 t.rb = rb;
254 if (alt && (rb == RB_CVT_V2 || rb == RB_CVT_V3))
255 t.rb |= RB_ALT;
256 t.pos_pol_hsync = t.rb;
257 t.pos_pol_vsync = !t.rb;
258 calc_ratio(&t);
259 return t;
262 void edid_state::edid_cvt_mode(unsigned refresh, struct timings &t, unsigned rb_h_blank,
263 unsigned rb_v_blank, bool early_vsync_rqd)
265 unsigned hratio = t.hratio;
266 unsigned vratio = t.vratio;
268 t = calc_cvt_mode(t.hact, t.vact, refresh, t.rb & ~RB_ALT, t.interlaced,
269 false, t.rb & RB_ALT, rb_h_blank, rb_v_blank, early_vsync_rqd);
270 t.hratio = hratio;
271 t.vratio = vratio;