blob: 7e1d759ce0013a18c1e7b944dd1d95f6dd8b6e94 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* v4l2-ctl-modes.cpp - functions to calculate cvt and gtf mode timings.
*
* Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights
* reserved.
*/
#include "v4l2-ctl.h"
static bool valid_params(int width, int height, int refresh_rate)
{
if (width <= 0) {
fprintf(stderr, "Invalid value %d for width\n", width);
return false;
}
if (height <= 0) {
fprintf(stderr, "Invalid value %d for height\n", height);
return false;
}
if (refresh_rate <= 0) {
fprintf(stderr, "Invalid value %d for refresh rate\n",
refresh_rate);
return false;
}
return true;
}
#define HV_FACTOR (1000)
#define CVT_MARGIN_PERCENT (18)
#define CVT_HSYNC_PERCENT (8)
/*
* CVT defines Based on
* Coordinated Video Timings Standard Ver 1.2 Feb 08, 2013
*/
#define CVT_PXL_CLK_GRAN (250000) /* pixel clock granularity */
#define CVT_PXL_CLK_GRAN_RB_V2 (1000) /* granularity for reduced blanking v2*/
/* Normal blanking */
#define CVT_MIN_V_BPORCH (7) /* lines */
#define CVT_MIN_V_PORCH_RND (3) /* lines */
#define CVT_MIN_VSYNC_BP (550) /* time v_sync + back porch (us)*/
/* Normal blanking for CVT uses GTF
* to calculate horizontal blanking */
#define CVT_CELL_GRAN (8) /* character cell granularity */
#define CVT_M (600) /* blanking formula gradient */
#define CVT_C (40) /* blanking formula offset */
#define CVT_K (128) /* blanking formula scale factor */
#define CVT_J (20) /* blanking formula scale factor */
#define CVT_C_PRIME (((CVT_C - CVT_J) * CVT_K / 256) + CVT_J)
#define CVT_M_PRIME (CVT_K * CVT_M / 256)
/* Reduced Blanking */
#define CVT_RB_MIN_V_BPORCH (7) /* lines */
#define CVT_RB_V_FPORCH (3) /* lines */
#define CVT_RB_MIN_V_BLANK (460) /* us */
#define CVT_RB_H_SYNC (32) /* pixels */
#define CVT_RB_H_BPORCH (80) /* pixels */
#define CVT_RB_H_BLANK (160) /* pixels */
/* Reduce blanking Version 2 */
#define CVT_RB_V2_H_BLANK 80 /* pixels */
#define CVT_RB_MIN_V_FPORCH 3 /* lines */
#define CVT_RB_V2_MIN_V_FPORCH 1 /* lines */
#define CVT_RB_V_BPORCH 6 /* lines */
static int v_sync_from_aspect_ratio(int width, int height)
{
if (((height * 4 / 3) / CVT_CELL_GRAN) * CVT_CELL_GRAN == width)
return 4;
if (((height * 16 / 9) / CVT_CELL_GRAN) * CVT_CELL_GRAN == width)
return 5;
if (((height * 16 / 10) / CVT_CELL_GRAN) * CVT_CELL_GRAN == width)
return 6;
if (((height * 5 / 4) / CVT_CELL_GRAN) * CVT_CELL_GRAN == width)
return 7;
if (((height * 15 / 9) / CVT_CELL_GRAN) * CVT_CELL_GRAN == width)
return 7;
/* custom aspect ratio */
fprintf(stderr, "Warning! Aspect ratio is not CVT Standard\n");
return 10;
}
/**
* calc_cvt_modeline - calculate modeline based on CVT algorithm
*
* This function is called to generate the timings according to CVT
* algorithm. Timing calculation is based on VESA(TM) Coordinated
* Video Timing Generator Rev 1.2 by Graham Loveridge May 28, 2013
* which can be downloaded from -
* http://www.vesa.org/vesa-standards/free-standards/
*
* Input Parameters:
* @image_width
* @image_height
* @refresh_rate
* @reduced_blanking: This value, if greater than 0, indicates that
* reduced blanking is to be used and value indicates the version.
* @interlaced: whether to compute an interlaced mode
* @reduced_fps: set the V4L2_DV_FL_REDUCED_FPS flag indicating that
* the fps should be reduced by a factor of 1000 / 1001
* @cvt: stores results of cvt timing calculation
*
* Returns:
* true, if cvt timings are calculated and filled in cvt modeline.
* false, for any error
*/
bool 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)
{
int h_sync;
int v_sync;
int h_fp;
int h_bp;
int v_fp;
int v_bp;
int h_pixel;
int v_lines;
int h_pixel_rnd;
int v_lines_rnd;
int active_h_pixel;
int active_v_lines;
int total_h_pixel;
int total_v_lines;
int h_blank;
int v_blank;
int h_period;
int interlace;
int v_refresh;
int pixel_clock;
int clk_gran;
bool use_rb;
bool rb_v2;
if (!valid_params(image_width, image_height, refresh_rate))
return false;
use_rb = reduced_blanking > 0;
rb_v2 = reduced_blanking == 2;
clk_gran = rb_v2 ? CVT_PXL_CLK_GRAN_RB_V2 : CVT_PXL_CLK_GRAN;
h_pixel = image_width;
v_lines = image_height;
if (!refresh_rate)
v_refresh = 60;
else
v_refresh = refresh_rate;
if (v_refresh != 50 && v_refresh != 60 &&
v_refresh != 75 && v_refresh != 85)
fprintf(stderr, "Warning! Refresh rate is not CVT standard\n");
if (interlaced) {
interlace = 1;
v_lines_rnd = v_lines / 2;
v_refresh = v_refresh * 2;
if ((v_lines_rnd * 2) != v_lines)
fprintf(stderr,
"Warning! Vertical lines rounded to nearest integer\n");
} else {
interlace = 0;
v_lines_rnd = v_lines;
}
h_pixel_rnd = h_pixel - (h_pixel % CVT_CELL_GRAN);
if (h_pixel_rnd != h_pixel)
fprintf(stderr,
"Warning! Horizontal pixels rounded to nearest character cell\n");
active_h_pixel = h_pixel_rnd;
active_v_lines = v_lines_rnd;
v_sync = rb_v2 ? 8 : v_sync_from_aspect_ratio(h_pixel, v_lines);
if (!use_rb) {
int tmp1, tmp2;
int ideal_blank_duty_cycle;
int v_sync_bp;
/* estimate the horizontal period */
tmp1 = HV_FACTOR * 1000000 -
CVT_MIN_VSYNC_BP * HV_FACTOR * v_refresh;
tmp2 = (active_v_lines + CVT_MIN_V_PORCH_RND) * 2 + interlace;
h_period = tmp1 * 2 / (tmp2 * v_refresh);
tmp1 = CVT_MIN_VSYNC_BP * HV_FACTOR / h_period + 1;
if (tmp1 < (v_sync + CVT_MIN_V_PORCH_RND))
v_sync_bp = v_sync + CVT_MIN_V_PORCH_RND;
else
v_sync_bp = tmp1;
v_bp = v_sync_bp - v_sync;
v_fp = CVT_MIN_V_PORCH_RND;
ideal_blank_duty_cycle = (CVT_C_PRIME * HV_FACTOR) -
CVT_M_PRIME * h_period / 1000;
if (ideal_blank_duty_cycle < 20 * HV_FACTOR)
ideal_blank_duty_cycle = 20 * HV_FACTOR;
h_blank = active_h_pixel * static_cast<long long>(ideal_blank_duty_cycle) /
(100 * HV_FACTOR - ideal_blank_duty_cycle);
h_blank -= h_blank % (2 * CVT_CELL_GRAN);
v_blank = v_sync_bp + CVT_MIN_V_PORCH_RND;
total_h_pixel = active_h_pixel + h_blank;
h_sync = (total_h_pixel * CVT_HSYNC_PERCENT) / 100;
h_sync -= h_sync % CVT_CELL_GRAN;
h_bp = h_blank / 2;
h_fp = h_blank - h_bp - h_sync;
pixel_clock = (static_cast<long long>(total_h_pixel) * HV_FACTOR * 1000000)
/ h_period;
pixel_clock -= pixel_clock % clk_gran;
} else {
/* Reduced blanking */
int vbi_lines;
int tmp1, tmp2;
int min_vbi_lines;
/* estimate horizontal period. */
tmp1 = HV_FACTOR * 1000000 -
CVT_RB_MIN_V_BLANK * HV_FACTOR * v_refresh;
tmp2 = active_v_lines;
h_period = tmp1 / (tmp2 * v_refresh);
vbi_lines = CVT_RB_MIN_V_BLANK * HV_FACTOR / h_period + 1;
if (rb_v2)
min_vbi_lines = CVT_RB_V2_MIN_V_FPORCH + v_sync + CVT_RB_V_BPORCH;
else
min_vbi_lines = CVT_RB_V_FPORCH + v_sync + CVT_MIN_V_BPORCH;
if (vbi_lines < min_vbi_lines)
vbi_lines = min_vbi_lines;
h_blank = rb_v2 ? CVT_RB_V2_H_BLANK : CVT_RB_H_BLANK;
v_blank = vbi_lines;
total_h_pixel = active_h_pixel + h_blank;
total_v_lines = active_v_lines + v_blank;
h_sync = CVT_RB_H_SYNC;
h_bp = h_blank / 2;
h_fp = h_blank - h_bp - h_sync;
if (rb_v2) {
v_bp = CVT_RB_V_BPORCH;
v_fp = v_blank - v_bp - v_sync;
} else {
v_fp = CVT_RB_V_FPORCH;
v_bp = v_blank - v_fp - v_sync;
}
pixel_clock = v_refresh * total_h_pixel *
(2 * total_v_lines + interlace) / 2;
pixel_clock -= pixel_clock % clk_gran;
}
cvt->standards = V4L2_DV_BT_STD_CVT;
cvt->width = h_pixel;
cvt->hfrontporch = h_fp;
cvt->hsync = h_sync;
cvt->hbackporch = h_bp;
cvt->height = v_lines;
cvt->vfrontporch = v_fp;
cvt->vsync = v_sync;
cvt->vbackporch = v_bp;
cvt->pixelclock = pixel_clock;
cvt->interlaced = interlaced == 1 ?
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
if (cvt->interlaced == V4L2_DV_INTERLACED) {
cvt->il_vfrontporch = v_fp;
cvt->il_vsync = v_sync;
cvt->il_vbackporch = v_bp;
/* Add 1 to vbackporch of even field and set the half line
* flag (V4L2_DV_FL_HALF_LINE)
* For interlaced format, the half line flag indicates to the
* driver to add a half-line to the vfrontporch of the odd
* field and subtract a half-line from the vbackporch of the
* even field */
cvt->flags |= V4L2_DV_FL_HALF_LINE;
cvt->il_vbackporch += 1;
}
if (use_rb) {
cvt->polarities = V4L2_DV_HSYNC_POS_POL;
cvt->flags |= V4L2_DV_FL_REDUCED_BLANKING;
} else {
cvt->polarities = V4L2_DV_VSYNC_POS_POL;
}
if (rb_v2 && reduced_fps && v_refresh % 6 == 0)
cvt->flags |= V4L2_DV_FL_REDUCED_FPS;
return true;
}
/*
* GTF defines Based on Generalized Timing Formula Standard
* Version 1.1 September 2, 1999
*/
#define GTF_MARGIN_PERCENT (18)
#define GTF_HSYNC_PERCENT (8)
#define GTF_PXL_CLK_GRAN (250000) /* pixel clock granularity */
#define GTF_V_SYNC_RQD (3) /* v sync required (lines) */
#define GTF_MIN_VSYNC_BP (550) /* min time vsync + back porch (us) */
#define GTF_MIN_PORCH (1) /* vertical front porch (lines) */
#define GTF_CELL_GRAN (8) /* character cell granularity */
/* Default */
#define GTF_D_M (600) /* blanking formula gradient */
#define GTF_D_C (40) /* blanking formula offset */
#define GTF_D_K (128) /* blanking formula scaling factor */
#define GTF_D_J (20) /* blanking formula scaling factor */
#define GTF_D_C_PRIME ((((GTF_D_C - GTF_D_J) * GTF_D_K) / 256) + GTF_D_J)
#define GTF_D_M_PRIME ((GTF_D_K * GTF_D_M) / 256)
/* Secondary */
#define GTF_S_M (3600) /* blanking formula gradient */
#define GTF_S_C (40) /* blanking formula offset */
#define GTF_S_K (128) /* blanking formula scaling factor */
#define GTF_S_J (35) /* blanking formula scaling factor */
#define GTF_S_C_PRIME ((((GTF_S_C - GTF_S_J) * GTF_S_K) / 256) + GTF_S_J)
#define GTF_S_M_PRIME ((GTF_S_K * GTF_S_M) / 256)
/**
* calc_gtf_modeline - calculate modeline based on GTF algorithm
*
* This function is called to generate the timings according to GTF
* algorithm. Timing calculation is based on VESA(TM) Generalized
* Timing Formula Standard Version 1.1 September 2, 1999
* which can be downloaded from -
* http://www.vesa.org/vesa-standards/free-standards/
*
* Input Parameters:
* @image_width
* @image_height
* @refresh_rate
* @reduced_blanking: This value, if greater than 0, indicates that
* reduced blanking is to be used.
* @interlaced: whether to compute an interlaced mode
* @gtf: stores results of gtf timing calculation
*
* Returns:
* true, if gtf timings are calculated and filled in gtf modeline.
* false, for any error.
*/
bool calc_gtf_modeline(int image_width, int image_height,
int refresh_rate, bool reduced_blanking,
bool interlaced, struct v4l2_bt_timings *gtf)
{
int h_sync;
int v_sync;
int h_fp;
int h_bp;
int v_fp;
int v_bp;
int h_pixel;
int v_lines;
int h_pixel_rnd;
int v_lines_rnd;
int active_h_pixel;
int active_v_lines;
int total_h_pixel;
int total_v_lines;
int h_blank;
int v_blank;
int h_period;
int h_period_est;
int interlace;
int v_refresh;
int v_refresh_est;
int pixel_clock;
int v_sync_bp;
int tmp1, tmp2;
int ideal_blank_duty_cycle;
if (!gtf) {
fprintf(stderr, "Null pointer to gtf modeline structure\n");
return false;
}
if (!valid_params(image_width, image_height, refresh_rate))
return false;
h_pixel = image_width;
v_lines = image_height;
if (!refresh_rate)
v_refresh = 60;
else
v_refresh = refresh_rate;
if (interlaced) {
interlace = 1;
v_lines_rnd = (v_lines + 1) / 2;
v_refresh = v_refresh * 2;
} else {
interlace = 0;
v_lines_rnd = v_lines;
}
h_pixel_rnd = (h_pixel + GTF_CELL_GRAN / 2);
h_pixel_rnd -= h_pixel_rnd % GTF_CELL_GRAN;
active_h_pixel = h_pixel_rnd;
active_v_lines = v_lines_rnd;
/* estimate the horizontal period */
tmp1 = HV_FACTOR * 1000000 -
GTF_MIN_VSYNC_BP * HV_FACTOR * v_refresh;
tmp2 = 2 * (active_v_lines + GTF_MIN_PORCH) + interlace;
h_period_est = 2 * tmp1 / (tmp2 * v_refresh);
v_sync_bp = GTF_MIN_VSYNC_BP * HV_FACTOR * 100 / h_period_est;
v_sync_bp = (v_sync_bp + 50) / 100;
v_sync = GTF_V_SYNC_RQD;
v_bp = v_sync_bp - v_sync;
v_fp = GTF_MIN_PORCH;
v_blank = v_sync + v_bp + v_fp;
total_v_lines = active_v_lines + v_blank;
v_refresh_est = (2 * HV_FACTOR * static_cast<long long>(1000000)) /
(h_period_est * (2 * total_v_lines + interlace) / HV_FACTOR);
h_period = (static_cast<long long>(h_period_est) * v_refresh_est) /
(v_refresh * HV_FACTOR);
if (!reduced_blanking)
ideal_blank_duty_cycle = (GTF_D_C_PRIME * HV_FACTOR) -
GTF_D_M_PRIME * h_period / 1000;
else
ideal_blank_duty_cycle = (GTF_S_C_PRIME * HV_FACTOR) -
GTF_S_M_PRIME * h_period / 1000;
h_blank = active_h_pixel * static_cast<long long>(ideal_blank_duty_cycle) /
(100 * HV_FACTOR - ideal_blank_duty_cycle);
h_blank = ((h_blank + GTF_CELL_GRAN) / (2 * GTF_CELL_GRAN))
* (2 * GTF_CELL_GRAN);
total_h_pixel = active_h_pixel + h_blank;
h_sync = (total_h_pixel * GTF_HSYNC_PERCENT) / 100;
h_sync = ((h_sync + GTF_CELL_GRAN / 2) / GTF_CELL_GRAN) * GTF_CELL_GRAN;
h_fp = h_blank / 2 - h_sync;
h_bp = h_fp + h_sync;
pixel_clock = (static_cast<long long>(total_h_pixel) * HV_FACTOR * 1000000)
/ h_period;
/* Not sure if clock value needs to be truncated to multiple
* of 25000. The formula given in standard does not indicate
* truncation
* */
/*pixel_clock -= pixel_clock % GTF_PXL_CLK_GRAN;*/
gtf->standards = V4L2_DV_BT_STD_GTF;
gtf->width = h_pixel;
gtf->hfrontporch = h_fp;
gtf->hsync = h_sync;
gtf->hbackporch = h_bp;
gtf->height = v_lines;
gtf->vfrontporch = v_fp;
gtf->vsync = v_sync;
gtf->vbackporch = v_bp;
gtf->pixelclock = pixel_clock;
gtf->interlaced = interlaced == 1 ?
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
if (gtf->interlaced == V4L2_DV_INTERLACED) {
gtf->il_vfrontporch = v_fp;
gtf->il_vsync = v_sync;
gtf->il_vbackporch = v_bp;
/* Add 1 to vbackporch of even field and set the half line
* flag (V4L2_DV_FL_HALF_LINE)
* For interlaced format, the half line flag indicates to the
* driver to add a half-line to the vfrontporch of the odd
* field and subtract a half-line from the vbackporch of the
* even field */
gtf->flags |= V4L2_DV_FL_HALF_LINE;
gtf->il_vbackporch += 1;
}
if (reduced_blanking) {
gtf->polarities = V4L2_DV_HSYNC_POS_POL;
gtf->flags |= V4L2_DV_FL_REDUCED_BLANKING;
} else
gtf->polarities = V4L2_DV_VSYNC_POS_POL;
return true;
}