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