diff mbox series

TDA1997x: replace video detection routine

Message ID m31r8r5s7y.fsf@t19.piap.pl
State Superseded
Headers show
Series TDA1997x: replace video detection routine | expand

Commit Message

Krzysztof Hałasa June 24, 2021, 1:04 p.m. UTC
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.

I propose to replace this scheme with a direct read of geometry
registers. This way all modes recognized by the chip will be supported.

How does this change look like from the user's perspective?

v4l2-ctl -d $(media-ctl -e "tda19971 2-0048") --query-dv-timings

Original code - known mode:           Patched:
Active width: 1152                    Active width: 1152
Active height: 864		      Active height: 864
Total width: 1600		      Total width: 1600
Total height: 900		      Total height: 900
Frame format: progressive	      Frame format: progressive
Polarities: +vsync +hsync	      Polarities: -vsync -hsync
Pixelclock: 108000000 Hz (75.00 FPS)  Pixelclock: 107998200 Hz (75.00 FPS)
Horizontal frontporch: 64	      Horizontal frontporch: 64
Horizontal sync: 128		      Horizontal sync: 128
Horizontal backporch: 256	      Horizontal backporch: 256
Vertical frontporch: 1		      Vertical frontporch: 1
Vertical sync: 3		      Vertical sync: 3
Vertical backporch: 32		      Vertical backporch: 32
Standards: DMT			      Standards:
Flags:				      Flags:

Original code - "unknown" mode:       Patched:
Active width: 0                       Active width: 1400
Active height: 0		      Active height: 1050
Total width: 0			      Total width: 1560
Total height: 0			      Total height: 1080
Frame format: progressive	      Frame format: progressive
Polarities: -vsync -hsync	      Polarities: -vsync -hsync
Pixelclock: 0 Hz		      Pixelclock: 79998593 Hz (47.48 FPS)
Horizontal frontporch: 0	      Horizontal frontporch: 48
Horizontal sync: 0		      Horizontal sync: 32
Horizontal backporch: 0		      Horizontal backporch: 80
Vertical frontporch: 0		      Vertical frontporch: 3
Vertical sync: 0		      Vertical sync: 4
Vertical backporch: 0		      Vertical backporch: 23
Standards:			      Standards:
Flags:				      Flags:

In the kernel logs - original code:
Signal Timings: 360006/399/31
Detected format: 1152x864p75.00 (1600x900)

Signal Timings: 568630/525/9
no resolution match for timings: 568630/525/9

Patched:
Signal Timings: 360006/399/31
Geometry: 1600 1152 64 128 256   900 864 1 0 3 32 0
Detected format: 1152x864p74.99 (1600x900)

Signal Timings: 568646/525/9
Geometry: 1560 1400 48 32 80   1080 1050 3 0 4 23 0
Detected format: 1400x1050p47.48 (1560x1080)

Note the differences. The original code uses values in
v4l2_dv_timings_presets[], while with the patch it reads the data from
the chip. Thus 108000000 Hz changes into (this time) 107998200 Hz.

Also, the flags. This is HDMI receiver, so I don't exactly think H and V
sync signals have any polarity (they are symbols embedded in the HDMI
stream).
I don't know if the "VESA Discrete Monitor Timings" (DMT) flag makes any
difference.

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.

I have checked the register values in interlaced mode, but currently
can't test such a setup (I only have remote access to a device working
in interlaced mode). Perhaps this will change in time.

All tests have been performed on Gateworks' Ventana GW54xx board, with
a TDA19971 chip.

Signed-off-by: Krzysztof Hałasa <khalasa@piap.pl>
---
... also testing UTF-8 path :-)
(Please note that I may be "unplugged" for the next 3 weeks)

Krzysztof "Chris" Hałasa

Sieć Badawcza Łukasiewicz
Przemysłowy Instytut Automatyki i Pomiarów PIAP
Al. Jerozolimskie 202, 02-486 Warszawa
diff mbox series

Patch

--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -1092,66 +1094,71 @@  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;
+	u32 vper = io_read24(sd, REG_V_PER) & MASK_VPER;
+	u16 hper = io_read16(sd, REG_H_PER) & MASK_HPER;
+	u16 hswidth = io_read16(sd, REG_HS_WIDTH);
+	u16 hsper = hswidth & MASK_HSWIDTH;
+	u16 htot, hact, hfront, hsync, hback;
+	u16 vtot, vact, vfront1, vfront2, vsync, vback1, vback2;
+
 	v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper);
 	if (!vper || !hper || !hsper)
 		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;
-
-		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;
-
-		/* 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;
-		}
+	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);
+
+	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: %u %u %u %u %u   %u %u %u %u %u %u %u\n",
+		 htot, hact, hfront, hsync, hback,
+		 vtot, vact, vfront1, vfront2, vsync, vback1, vback2);
+
+	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 = hswidth & MASK_HSWIDTH_INTERLACED ?
+		V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
+
+	timings->bt.pixelclock = (u64)htot * vtot * 27000000;
+	if (timings->bt.interlaced == V4L2_DV_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_print_dv_timings(sd->name, "Detected format: ", timings, false);
+	return 0;
 }
 
 /* some sort of errata workaround for chip revision 0 (N1) */
--- a/drivers/media/i2c/tda1997x_regs.h
+++ b/drivers/media/i2c/tda1997x_regs.h
@@ -120,6 +120,7 @@ 
 #define MASK_VHREF		0x3fff
 #define MASK_HPER		0x0fff
 #define MASK_HSWIDTH		0x03ff
+#define MASK_HSWIDTH_INTERLACED	0x8000
 
 /* HPD Detection */
 #define DETECT_UTIL		BIT(7)	/* utility of HDMI level */