media: TDA1997x: replace video detection routine
The TDA1997x (HDMI receiver) driver currently uses a specific video format detection scheme. The frame (or field in interlaced mode), line and HSync pulse durations are compared to those of known, standard video modes. If a match is found, the mode is assumed to be detected, otherwise -ERANGE is returned (then possibly ignored). This means that: - another mode with similar timings will be detected incorrectly (e.g. 2x faster clock and lines twice as long) - non-standard modes will not work. This patch replaces this scheme with a direct read of geometry registers. This way all modes recognized by the chip are supported. In interlaced modes, the code assumes the V sync signal has the same duration for both fields. While this may be not necessarily true, I can't see any way to get the "other" V sync width. This is most probably harmless. All tests have been performed on Gateworks' Ventana GW54xx board, with a TDA19971 chip. Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl> Tested-by: Tim Harvey <tharvey@gateworks.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
Родитель
3ae5c3bc07
Коммит
d64a7709a8
|
@ -1092,67 +1092,82 @@ tda1997x_detect_std(struct tda1997x_state *state,
|
|||
struct v4l2_dv_timings *timings)
|
||||
{
|
||||
struct v4l2_subdev *sd = &state->sd;
|
||||
u32 vper;
|
||||
u16 hper;
|
||||
u16 hsper;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Read the FMT registers
|
||||
* REG_V_PER: Period of a frame (or two fields) in MCLK(27MHz) cycles
|
||||
* REG_H_PER: Period of a line in MCLK(27MHz) cycles
|
||||
* REG_HS_WIDTH: Period of horiz sync pulse in MCLK(27MHz) cycles
|
||||
* REG_V_PER: Period of a frame (or field) in MCLK (27MHz) cycles
|
||||
* REG_H_PER: Period of a line in MCLK (27MHz) cycles
|
||||
* REG_HS_WIDTH: Period of horiz sync pulse in MCLK (27MHz) cycles
|
||||
*/
|
||||
vper = io_read24(sd, REG_V_PER) & MASK_VPER;
|
||||
hper = io_read16(sd, REG_H_PER) & MASK_HPER;
|
||||
hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH;
|
||||
v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper);
|
||||
u32 vper, vsync_pos;
|
||||
u16 hper, hsync_pos, hsper, interlaced;
|
||||
u16 htot, hact, hfront, hsync, hback;
|
||||
u16 vtot, vact, vfront1, vfront2, vsync, vback1, vback2;
|
||||
|
||||
if (!state->input_detect[0] && !state->input_detect[1])
|
||||
return -ENOLINK;
|
||||
|
||||
for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) {
|
||||
const struct v4l2_bt_timings *bt;
|
||||
u32 lines, width, _hper, _hsper;
|
||||
u32 vmin, vmax, hmin, hmax, hsmin, hsmax;
|
||||
bool vmatch, hmatch, hsmatch;
|
||||
vper = io_read24(sd, REG_V_PER);
|
||||
hper = io_read16(sd, REG_H_PER);
|
||||
hsper = io_read16(sd, REG_HS_WIDTH);
|
||||
vsync_pos = vper & MASK_VPER_SYNC_POS;
|
||||
hsync_pos = hper & MASK_HPER_SYNC_POS;
|
||||
interlaced = hsper & MASK_HSWIDTH_INTERLACED;
|
||||
vper &= MASK_VPER;
|
||||
hper &= MASK_HPER;
|
||||
hsper &= MASK_HSWIDTH;
|
||||
v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper);
|
||||
|
||||
bt = &v4l2_dv_timings_presets[i].bt;
|
||||
width = V4L2_DV_BT_FRAME_WIDTH(bt);
|
||||
lines = V4L2_DV_BT_FRAME_HEIGHT(bt);
|
||||
_hper = (u32)bt->pixelclock / width;
|
||||
if (bt->interlaced)
|
||||
lines /= 2;
|
||||
/* vper +/- 0.7% */
|
||||
vmin = ((27000000 / 1000) * 993) / _hper * lines;
|
||||
vmax = ((27000000 / 1000) * 1007) / _hper * lines;
|
||||
/* hper +/- 1.0% */
|
||||
hmin = ((27000000 / 100) * 99) / _hper;
|
||||
hmax = ((27000000 / 100) * 101) / _hper;
|
||||
/* hsper +/- 2 (take care to avoid 32bit overflow) */
|
||||
_hsper = 27000 * bt->hsync / ((u32)bt->pixelclock/1000);
|
||||
hsmin = _hsper - 2;
|
||||
hsmax = _hsper + 2;
|
||||
htot = io_read16(sd, REG_FMT_H_TOT);
|
||||
hact = io_read16(sd, REG_FMT_H_ACT);
|
||||
hfront = io_read16(sd, REG_FMT_H_FRONT);
|
||||
hsync = io_read16(sd, REG_FMT_H_SYNC);
|
||||
hback = io_read16(sd, REG_FMT_H_BACK);
|
||||
|
||||
/* vmatch matches the framerate */
|
||||
vmatch = ((vper <= vmax) && (vper >= vmin)) ? 1 : 0;
|
||||
/* hmatch matches the width */
|
||||
hmatch = ((hper <= hmax) && (hper >= hmin)) ? 1 : 0;
|
||||
/* hsmatch matches the hswidth */
|
||||
hsmatch = ((hsper <= hsmax) && (hsper >= hsmin)) ? 1 : 0;
|
||||
if (hmatch && vmatch && hsmatch) {
|
||||
v4l2_print_dv_timings(sd->name, "Detected format: ",
|
||||
&v4l2_dv_timings_presets[i],
|
||||
false);
|
||||
if (timings)
|
||||
*timings = v4l2_dv_timings_presets[i];
|
||||
return 0;
|
||||
}
|
||||
vtot = io_read16(sd, REG_FMT_V_TOT);
|
||||
vact = io_read16(sd, REG_FMT_V_ACT);
|
||||
vfront1 = io_read(sd, REG_FMT_V_FRONT_F1);
|
||||
vfront2 = io_read(sd, REG_FMT_V_FRONT_F2);
|
||||
vsync = io_read(sd, REG_FMT_V_SYNC);
|
||||
vback1 = io_read(sd, REG_FMT_V_BACK_F1);
|
||||
vback2 = io_read(sd, REG_FMT_V_BACK_F2);
|
||||
|
||||
v4l2_dbg(1, debug, sd, "Geometry: H %u %u %u %u %u Sync%c V %u %u %u %u %u %u %u Sync%c\n",
|
||||
htot, hact, hfront, hsync, hback, hsync_pos ? '+' : '-',
|
||||
vtot, vact, vfront1, vfront2, vsync, vback1, vback2, vsync_pos ? '+' : '-');
|
||||
|
||||
if (!timings)
|
||||
return 0;
|
||||
|
||||
timings->type = V4L2_DV_BT_656_1120;
|
||||
timings->bt.width = hact;
|
||||
timings->bt.hfrontporch = hfront;
|
||||
timings->bt.hsync = hsync;
|
||||
timings->bt.hbackporch = hback;
|
||||
timings->bt.height = vact;
|
||||
timings->bt.vfrontporch = vfront1;
|
||||
timings->bt.vsync = vsync;
|
||||
timings->bt.vbackporch = vback1;
|
||||
timings->bt.interlaced = interlaced ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
|
||||
timings->bt.polarities = vsync_pos ? V4L2_DV_VSYNC_POS_POL : 0;
|
||||
timings->bt.polarities |= hsync_pos ? V4L2_DV_HSYNC_POS_POL : 0;
|
||||
|
||||
timings->bt.pixelclock = (u64)htot * vtot * 27000000;
|
||||
if (interlaced) {
|
||||
timings->bt.il_vfrontporch = vfront2;
|
||||
timings->bt.il_vsync = timings->bt.vsync;
|
||||
timings->bt.il_vbackporch = vback2;
|
||||
do_div(timings->bt.pixelclock, vper * 2 /* full frame */);
|
||||
} else {
|
||||
timings->bt.il_vfrontporch = 0;
|
||||
timings->bt.il_vsync = 0;
|
||||
timings->bt.il_vbackporch = 0;
|
||||
do_div(timings->bt.pixelclock, vper);
|
||||
}
|
||||
|
||||
v4l_err(state->client, "no resolution match for timings: %d/%d/%d\n",
|
||||
vper, hper, hsper);
|
||||
return -ERANGE;
|
||||
v4l2_find_dv_timings_cap(timings, &tda1997x_dv_timings_cap,
|
||||
(u32)timings->bt.pixelclock / 500, NULL, NULL);
|
||||
v4l2_print_dv_timings(sd->name, "Detected format: ", timings, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* some sort of errata workaround for chip revision 0 (N1) */
|
||||
|
|
|
@ -117,9 +117,12 @@
|
|||
#define REG_CURPAGE_00H 0xFF
|
||||
|
||||
#define MASK_VPER 0x3fffff
|
||||
#define MASK_VPER_SYNC_POS 0x800000
|
||||
#define MASK_VHREF 0x3fff
|
||||
#define MASK_HPER 0x0fff
|
||||
#define MASK_HPER_SYNC_POS 0x8000
|
||||
#define MASK_HSWIDTH 0x03ff
|
||||
#define MASK_HSWIDTH_INTERLACED 0x8000
|
||||
|
||||
/* HPD Detection */
|
||||
#define DETECT_UTIL BIT(7) /* utility of HDMI level */
|
||||
|
|
Загрузка…
Ссылка в новой задаче