1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * v4l2-ctl-modes.cpp - functions to calculate cvt and gtf mode timings.
4 *
5 * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights
6 * reserved.
7 */
8
9 #include <stdio.h>
10 #include <stdbool.h>
11 #include "v4l2-ctl.h"
12
valid_params(int width,int height,int refresh_rate)13 static bool valid_params(int width, int height, int refresh_rate)
14 {
15 if (width <= 0) {
16 fprintf(stderr, "Invalid value %d for width\n", width);
17 return false;
18 }
19
20 if (height <= 0) {
21 fprintf(stderr, "Invalid value %d for height\n", height);
22 return false;
23 }
24
25 if (refresh_rate <= 0) {
26 fprintf(stderr, "Invalid value %d for refresh rate\n",
27 refresh_rate);
28 return false;
29 }
30 return true;
31 }
32
33 #define HV_FACTOR (1000)
34 #define CVT_MARGIN_PERCENT (18)
35 #define CVT_HSYNC_PERCENT (8)
36
37 /*
38 * CVT defines Based on
39 * Coordinated Video Timings Standard Ver 1.2 Feb 08, 2013
40 */
41
42 #define CVT_PXL_CLK_GRAN (250000) /* pixel clock granularity */
43 #define CVT_PXL_CLK_GRAN_RB_V2 (1000) /* granularity for reduced blanking v2*/
44
45 /* Normal blanking */
46 #define CVT_MIN_V_BPORCH (7) /* lines */
47 #define CVT_MIN_V_PORCH_RND (3) /* lines */
48 #define CVT_MIN_VSYNC_BP (550) /* time v_sync + back porch (us)*/
49
50 /* Normal blanking for CVT uses GTF
51 * to calculate horizontal blanking */
52 #define CVT_CELL_GRAN (8) /* character cell granularity */
53 #define CVT_M (600) /* blanking formula gradient */
54 #define CVT_C (40) /* blanking formula offset */
55 #define CVT_K (128) /* blanking formula scale factor */
56 #define CVT_J (20) /* blanking formula scale factor */
57
58 #define CVT_C_PRIME (((CVT_C - CVT_J) * CVT_K / 256) + CVT_J)
59 #define CVT_M_PRIME (CVT_K * CVT_M / 256)
60
61 /* Reduced Blanking */
62 #define CVT_RB_MIN_V_BPORCH (7) /* lines */
63 #define CVT_RB_V_FPORCH (3) /* lines */
64 #define CVT_RB_MIN_V_BLANK (460) /* us */
65 #define CVT_RB_H_SYNC (32) /* pixels */
66 #define CVT_RB_H_BPORCH (80) /* pixels */
67 #define CVT_RB_H_BLANK (160) /* pixels */
68
69 /* Reduce blanking Version 2 */
70 #define CVT_RB_V2_H_BLANK 80 /* pixels */
71 #define CVT_RB_MIN_V_FPORCH 3 /* lines */
72 #define CVT_RB_V2_MIN_V_FPORCH 1 /* lines */
73 #define CVT_RB_V_BPORCH 6 /* lines */
74
v_sync_from_aspect_ratio(int width,int height)75 static int v_sync_from_aspect_ratio(int width, int height)
76 {
77 if (((height * 4 / 3) / CVT_CELL_GRAN) * CVT_CELL_GRAN == width)
78 return 4;
79
80 if (((height * 16 / 9) / CVT_CELL_GRAN) * CVT_CELL_GRAN == width)
81 return 5;
82
83 if (((height * 16 / 10) / CVT_CELL_GRAN) * CVT_CELL_GRAN == width)
84 return 6;
85
86 if (((height * 5 / 4) / CVT_CELL_GRAN) * CVT_CELL_GRAN == width)
87 return 7;
88
89 if (((height * 15 / 9) / CVT_CELL_GRAN) * CVT_CELL_GRAN == width)
90 return 7;
91
92 /* custom aspect ratio */
93 fprintf(stderr, "Warning! Aspect ratio is not CVT Standard\n");
94 return 10;
95 }
96
97 /**
98 * calc_cvt_modeline - calculate modeline based on CVT algorithm
99 *
100 * This function is called to generate the timings according to CVT
101 * algorithm. Timing calculation is based on VESA(TM) Coordinated
102 * Video Timing Generator Rev 1.2 by Graham Loveridge May 28, 2013
103 * which can be downloaded from -
104 * http://www.vesa.org/vesa-standards/free-standards/
105 *
106 * Input Parameters:
107 * @image_width
108 * @image_height
109 * @refresh_rate
110 * @reduced_blanking: This value, if greater than 0, indicates that
111 * reduced blanking is to be used and value indicates the version.
112 * @interlaced: whether to compute an interlaced mode
113 * @reduced_fps: set the V4L2_DV_FL_REDUCED_FPS flag indicating that
114 * the fps should be reduced by a factor of 1000 / 1001
115 * @cvt: stores results of cvt timing calculation
116 *
117 * Returns:
118 * true, if cvt timings are calculated and filled in cvt modeline.
119 * false, for any error
120 */
121
calc_cvt_modeline(int image_width,int image_height,int refresh_rate,int reduced_blanking,bool interlaced,bool reduced_fps,struct v4l2_bt_timings * cvt)122 bool calc_cvt_modeline(int image_width, int image_height,
123 int refresh_rate, int reduced_blanking,
124 bool interlaced, bool reduced_fps,
125 struct v4l2_bt_timings *cvt)
126 {
127 int h_sync;
128 int v_sync;
129 int h_fp;
130 int h_bp;
131 int v_fp;
132 int v_bp;
133
134 int h_pixel;
135 int v_lines;
136 int h_pixel_rnd;
137 int v_lines_rnd;
138 int active_h_pixel;
139 int active_v_lines;
140 int total_h_pixel;
141 int total_v_lines;
142
143 int h_blank;
144 int v_blank;
145
146 int h_period;
147
148 int interlace;
149 int v_refresh;
150 int pixel_clock;
151 int clk_gran;
152 bool use_rb;
153 bool rb_v2;
154
155 if (!valid_params(image_width, image_height, refresh_rate))
156 return false;
157
158 use_rb = reduced_blanking > 0;
159 rb_v2 = reduced_blanking == 2;
160
161 clk_gran = rb_v2 ? CVT_PXL_CLK_GRAN_RB_V2 : CVT_PXL_CLK_GRAN;
162
163 h_pixel = image_width;
164 v_lines = image_height;
165
166 if (!refresh_rate)
167 v_refresh = 60;
168 else
169 v_refresh = refresh_rate;
170
171 if (v_refresh != 50 && v_refresh != 60 &&
172 v_refresh != 75 && v_refresh != 85)
173 fprintf(stderr, "Warning! Refresh rate is not CVT standard\n");
174
175 if (interlaced) {
176 interlace = 1;
177 v_lines_rnd = v_lines / 2;
178 v_refresh = v_refresh * 2;
179
180 if ((v_lines_rnd * 2) != v_lines)
181 fprintf(stderr,
182 "Warning! Vertical lines rounded to nearest integer\n");
183 } else {
184 interlace = 0;
185 v_lines_rnd = v_lines;
186 }
187
188 h_pixel_rnd = h_pixel - (h_pixel % CVT_CELL_GRAN);
189
190 if (h_pixel_rnd != h_pixel)
191 fprintf(stderr,
192 "Warning! Horizontal pixels rounded to nearest character cell\n");
193
194 active_h_pixel = h_pixel_rnd;
195 active_v_lines = v_lines_rnd;
196
197 v_sync = rb_v2 ? 8 : v_sync_from_aspect_ratio(h_pixel, v_lines);
198
199 if (!use_rb) {
200 int tmp1, tmp2;
201 int ideal_blank_duty_cycle;
202 int v_sync_bp;
203
204 /* estimate the horizontal period */
205 tmp1 = HV_FACTOR * 1000000 -
206 CVT_MIN_VSYNC_BP * HV_FACTOR * v_refresh;
207 tmp2 = (active_v_lines + CVT_MIN_V_PORCH_RND) * 2 + interlace;
208
209 h_period = tmp1 * 2 / (tmp2 * v_refresh);
210
211 tmp1 = CVT_MIN_VSYNC_BP * HV_FACTOR / h_period + 1;
212
213 if (tmp1 < (v_sync + CVT_MIN_V_PORCH_RND))
214 v_sync_bp = v_sync + CVT_MIN_V_PORCH_RND;
215 else
216 v_sync_bp = tmp1;
217
218 v_bp = v_sync_bp - v_sync;
219 v_fp = CVT_MIN_V_PORCH_RND;
220
221 ideal_blank_duty_cycle = (CVT_C_PRIME * HV_FACTOR) -
222 CVT_M_PRIME * h_period / 1000;
223
224 if (ideal_blank_duty_cycle < 20 * HV_FACTOR)
225 ideal_blank_duty_cycle = 20 * HV_FACTOR;
226
227 h_blank = active_h_pixel * static_cast<long long>(ideal_blank_duty_cycle) /
228 (100 * HV_FACTOR - ideal_blank_duty_cycle);
229 h_blank -= h_blank % (2 * CVT_CELL_GRAN);
230
231 v_blank = v_sync_bp + CVT_MIN_V_PORCH_RND;
232
233 total_h_pixel = active_h_pixel + h_blank;
234
235 h_sync = (total_h_pixel * CVT_HSYNC_PERCENT) / 100;
236 h_sync -= h_sync % CVT_CELL_GRAN;
237
238 h_bp = h_blank / 2;
239 h_fp = h_blank - h_bp - h_sync;
240
241 pixel_clock = (static_cast<long long>(total_h_pixel) * HV_FACTOR * 1000000)
242 / h_period;
243 pixel_clock -= pixel_clock % clk_gran;
244 } else {
245 /* Reduced blanking */
246
247 int vbi_lines;
248 int tmp1, tmp2;
249 int min_vbi_lines;
250
251 /* estimate horizontal period. */
252 tmp1 = HV_FACTOR * 1000000 -
253 CVT_RB_MIN_V_BLANK * HV_FACTOR * v_refresh;
254 tmp2 = active_v_lines;
255
256 h_period = tmp1 / (tmp2 * v_refresh);
257
258 vbi_lines = CVT_RB_MIN_V_BLANK * HV_FACTOR / h_period + 1;
259
260 if (rb_v2)
261 min_vbi_lines = CVT_RB_V2_MIN_V_FPORCH + v_sync + CVT_RB_V_BPORCH;
262 else
263 min_vbi_lines = CVT_RB_V_FPORCH + v_sync + CVT_MIN_V_BPORCH;
264
265 if (vbi_lines < min_vbi_lines)
266 vbi_lines = min_vbi_lines;
267
268 h_blank = rb_v2 ? CVT_RB_V2_H_BLANK : CVT_RB_H_BLANK;
269 v_blank = vbi_lines;
270
271 total_h_pixel = active_h_pixel + h_blank;
272 total_v_lines = active_v_lines + v_blank;
273
274 h_sync = CVT_RB_H_SYNC;
275
276 h_bp = h_blank / 2;
277 h_fp = h_blank - h_bp - h_sync;
278
279 if (rb_v2) {
280 v_bp = CVT_RB_V_BPORCH;
281 v_fp = v_blank - v_bp - v_sync;
282 } else {
283 v_fp = CVT_RB_V_FPORCH;
284 v_bp = v_blank - v_fp - v_sync;
285 }
286
287 pixel_clock = v_refresh * total_h_pixel *
288 (2 * total_v_lines + interlace) / 2;
289 pixel_clock -= pixel_clock % clk_gran;
290 }
291
292 cvt->standards = V4L2_DV_BT_STD_CVT;
293
294 cvt->width = h_pixel;
295 cvt->hfrontporch = h_fp;
296 cvt->hsync = h_sync;
297 cvt->hbackporch = h_bp;
298
299 cvt->height = v_lines;
300 cvt->vfrontporch = v_fp;
301 cvt->vsync = v_sync;
302 cvt->vbackporch = v_bp;
303
304 cvt->pixelclock = pixel_clock;
305 cvt->interlaced = interlaced == 1 ?
306 V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
307
308 if (cvt->interlaced == V4L2_DV_INTERLACED) {
309 cvt->il_vfrontporch = v_fp;
310 cvt->il_vsync = v_sync;
311 cvt->il_vbackporch = v_bp;
312 /* Add 1 to vbackporch of even field and set the half line
313 * flag (V4L2_DV_FL_HALF_LINE)
314 * For interlaced format, the half line flag indicates to the
315 * driver to add a half-line to the vfrontporch of the odd
316 * field and subtract a half-line from the vbackporch of the
317 * even field */
318 cvt->flags |= V4L2_DV_FL_HALF_LINE;
319 cvt->il_vbackporch += 1;
320 }
321 if (use_rb) {
322 cvt->polarities = V4L2_DV_HSYNC_POS_POL;
323 cvt->flags |= V4L2_DV_FL_REDUCED_BLANKING;
324 } else {
325 cvt->polarities = V4L2_DV_VSYNC_POS_POL;
326 }
327 if (rb_v2 && reduced_fps && v_refresh % 6 == 0)
328 cvt->flags |= V4L2_DV_FL_REDUCED_FPS;
329
330 return true;
331 }
332
333
334 /*
335 * GTF defines Based on Generalized Timing Formula Standard
336 * Version 1.1 September 2, 1999
337 */
338
339 #define GTF_MARGIN_PERCENT (18)
340 #define GTF_HSYNC_PERCENT (8)
341
342 #define GTF_PXL_CLK_GRAN (250000) /* pixel clock granularity */
343
344 #define GTF_V_SYNC_RQD (3) /* v sync required (lines) */
345 #define GTF_MIN_VSYNC_BP (550) /* min time vsync + back porch (us) */
346 #define GTF_MIN_PORCH (1) /* vertical front porch (lines) */
347 #define GTF_CELL_GRAN (8) /* character cell granularity */
348
349 /* Default */
350 #define GTF_D_M (600) /* blanking formula gradient */
351 #define GTF_D_C (40) /* blanking formula offset */
352 #define GTF_D_K (128) /* blanking formula scaling factor */
353 #define GTF_D_J (20) /* blanking formula scaling factor */
354 #define GTF_D_C_PRIME ((((GTF_D_C - GTF_D_J) * GTF_D_K) / 256) + GTF_D_J)
355 #define GTF_D_M_PRIME ((GTF_D_K * GTF_D_M) / 256)
356
357 /* Secondary */
358 #define GTF_S_M (3600) /* blanking formula gradient */
359 #define GTF_S_C (40) /* blanking formula offset */
360 #define GTF_S_K (128) /* blanking formula scaling factor */
361 #define GTF_S_J (35) /* blanking formula scaling factor */
362 #define GTF_S_C_PRIME ((((GTF_S_C - GTF_S_J) * GTF_S_K) / 256) + GTF_S_J)
363 #define GTF_S_M_PRIME ((GTF_S_K * GTF_S_M) / 256)
364
365 /**
366 * calc_gtf_modeline - calculate modeline based on GTF algorithm
367 *
368 * This function is called to generate the timings according to GTF
369 * algorithm. Timing calculation is based on VESA(TM) Generalized
370 * Timing Formula Standard Version 1.1 September 2, 1999
371 * which can be downloaded from -
372 * http://www.vesa.org/vesa-standards/free-standards/
373 *
374 * Input Parameters:
375 * @image_width
376 * @image_height
377 * @refresh_rate
378 * @reduced_blanking: This value, if greater than 0, indicates that
379 * reduced blanking is to be used.
380 * @interlaced: whether to compute an interlaced mode
381 * @gtf: stores results of gtf timing calculation
382 *
383 * Returns:
384 * true, if gtf timings are calculated and filled in gtf modeline.
385 * false, for any error.
386 */
387
calc_gtf_modeline(int image_width,int image_height,int refresh_rate,bool reduced_blanking,bool interlaced,struct v4l2_bt_timings * gtf)388 bool calc_gtf_modeline(int image_width, int image_height,
389 int refresh_rate, bool reduced_blanking,
390 bool interlaced, struct v4l2_bt_timings *gtf)
391 {
392 int h_sync;
393 int v_sync;
394 int h_fp;
395 int h_bp;
396 int v_fp;
397 int v_bp;
398
399 int h_pixel;
400 int v_lines;
401 int h_pixel_rnd;
402 int v_lines_rnd;
403 int active_h_pixel;
404 int active_v_lines;
405 int total_h_pixel;
406 int total_v_lines;
407
408 int h_blank;
409 int v_blank;
410
411 int h_period;
412 int h_period_est;
413
414 int interlace;
415 int v_refresh;
416 int v_refresh_est;
417 int pixel_clock;
418
419 int v_sync_bp;
420 int tmp1, tmp2;
421 int ideal_blank_duty_cycle;
422
423 if (!gtf) {
424 fprintf(stderr, "Null pointer to gtf modeline structure\n");
425 return false;
426 }
427
428 if (!valid_params(image_width, image_height, refresh_rate))
429 return false;
430
431 h_pixel = image_width;
432 v_lines = image_height;
433
434 if (!refresh_rate)
435 v_refresh = 60;
436 else
437 v_refresh = refresh_rate;
438
439 if (interlaced) {
440 interlace = 1;
441 v_lines_rnd = (v_lines + 1) / 2;
442 v_refresh = v_refresh * 2;
443 } else {
444 interlace = 0;
445 v_lines_rnd = v_lines;
446 }
447
448 h_pixel_rnd = (h_pixel + GTF_CELL_GRAN / 2);
449 h_pixel_rnd -= h_pixel_rnd % GTF_CELL_GRAN;
450
451 active_h_pixel = h_pixel_rnd;
452 active_v_lines = v_lines_rnd;
453
454 /* estimate the horizontal period */
455 tmp1 = HV_FACTOR * 1000000 -
456 GTF_MIN_VSYNC_BP * HV_FACTOR * v_refresh;
457 tmp2 = 2 * (active_v_lines + GTF_MIN_PORCH) + interlace;
458
459 h_period_est = 2 * tmp1 / (tmp2 * v_refresh);
460
461 v_sync_bp = GTF_MIN_VSYNC_BP * HV_FACTOR * 100 / h_period_est;
462 v_sync_bp = (v_sync_bp + 50) / 100;
463
464 v_sync = GTF_V_SYNC_RQD;
465 v_bp = v_sync_bp - v_sync;
466 v_fp = GTF_MIN_PORCH;
467
468 v_blank = v_sync + v_bp + v_fp;
469 total_v_lines = active_v_lines + v_blank;
470
471 v_refresh_est = (2 * HV_FACTOR * static_cast<long long>(1000000)) /
472 (h_period_est * (2 * total_v_lines + interlace) / HV_FACTOR);
473
474 h_period = (static_cast<long long>(h_period_est) * v_refresh_est) /
475 (v_refresh * HV_FACTOR);
476
477 if (!reduced_blanking)
478 ideal_blank_duty_cycle = (GTF_D_C_PRIME * HV_FACTOR) -
479 GTF_D_M_PRIME * h_period / 1000;
480 else
481 ideal_blank_duty_cycle = (GTF_S_C_PRIME * HV_FACTOR) -
482 GTF_S_M_PRIME * h_period / 1000;
483
484 h_blank = active_h_pixel * static_cast<long long>(ideal_blank_duty_cycle) /
485 (100 * HV_FACTOR - ideal_blank_duty_cycle);
486 h_blank = ((h_blank + GTF_CELL_GRAN) / (2 * GTF_CELL_GRAN))
487 * (2 * GTF_CELL_GRAN);
488 total_h_pixel = active_h_pixel + h_blank;
489
490 h_sync = (total_h_pixel * GTF_HSYNC_PERCENT) / 100;
491 h_sync = ((h_sync + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN) * GTF_CELL_GRAN;
492
493 h_fp = h_blank / 2 - h_sync;
494 h_bp = h_fp + h_sync;
495
496 pixel_clock = (static_cast<long long>(total_h_pixel) * HV_FACTOR * 1000000)
497 / h_period;
498 /* Not sure if clock value needs to be truncated to multiple
499 * of 25000. The formula given in standard does not indicate
500 * truncation
501 * */
502 /*pixel_clock -= pixel_clock % GTF_PXL_CLK_GRAN;*/
503 gtf->standards = V4L2_DV_BT_STD_GTF;
504
505 gtf->width = h_pixel;
506 gtf->hfrontporch = h_fp;
507 gtf->hsync = h_sync;
508 gtf->hbackporch = h_bp;
509
510 gtf->height = v_lines;
511 gtf->vfrontporch = v_fp;
512 gtf->vsync = v_sync;
513 gtf->vbackporch = v_bp;
514
515 gtf->pixelclock = pixel_clock;
516 gtf->interlaced = interlaced == 1 ?
517 V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
518
519 if (gtf->interlaced == V4L2_DV_INTERLACED) {
520 gtf->il_vfrontporch = v_fp;
521 gtf->il_vsync = v_sync;
522 gtf->il_vbackporch = v_bp;
523 /* Add 1 to vbackporch of even field and set the half line
524 * flag (V4L2_DV_FL_HALF_LINE)
525 * For interlaced format, the half line flag indicates to the
526 * driver to add a half-line to the vfrontporch of the odd
527 * field and subtract a half-line from the vbackporch of the
528 * even field */
529 gtf->flags |= V4L2_DV_FL_HALF_LINE;
530 gtf->il_vbackporch += 1;
531 }
532 if (reduced_blanking) {
533 gtf->polarities = V4L2_DV_HSYNC_POS_POL;
534 gtf->flags |= V4L2_DV_FL_REDUCED_BLANKING;
535 } else
536 gtf->polarities = V4L2_DV_VSYNC_POS_POL;
537
538 return true;
539 }
540