All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
@ 2022-02-24  9:42 Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 01/27] media: ov5640: Add pixel rate to modes Jacopo Mondi
                   ` (29 more replies)
  0 siblings, 30 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

v1:
https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
v2:
https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
v3:
https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
v4:
https://patchwork.linuxtv.org/project/linux-media/list/?series=7389

A branch for testing based on the most recent media-master is available at
https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5

v5 (Sakari):
- Stay strictly in 80 cols
- use clamp_t to avoid explicit cast
- use ov5640_timings() where possible

v4:
- Very minor update. Added tags and reworked enum_mbus_format as suggested
  by Laurent.

v3:
The series has now grown by 4 patches and the driver is now even larger
being the formats and the timings for DVP and CSI-2 defined separately.

Tested in CSI-2 mode with UYVY, RGB565, SBGGR and RGB24 in all supported modes.

Tested format and sizes enumeration with the new formats definition.

Tested frame rate handling:

	vblank = ( duration msec * pixe_rate MHz / htot - height)

  640x480 YUYV 15FPS (default 30 FPS)

	duration = 666666 msec
	pixel_rate = 48 Mhz
	htot = 1600
	vtot = 1999
	vblank = vtot - height = 1519

	$ v4l2-ctl -d /dev/v4l-subdev4 -c 0x009e0901=1519
	$ yavta -f YUYV -s 640x480 -c100 --skip 7 /dev/video0
	...
	10 (2) [-] any 11 614400 B 2189.317617 2189.317629 15.244 fps ts mono/EoF
	11 (3) [-] any 12 614400 B 2189.383212 2189.383224 15.245 fps ts mono/EoF
	12 (4) [-] any 13 614400 B 2189.448810 2189.448821 15.244 fps ts mono/EoF
	13 (5) [-] any 14 614400 B 2189.514405 2189.514417 15.245 fps ts mono/EoF
	14 (6) [-] any 15 614400 B 2189.580002 2189.580015 15.245 fps ts mono/EoF
	..

  2592x1944 YUVV 15 FPS (default)
	$ yavta -f YUYV -s 2592x1944 -c100 --skip 7 /dev/video0
	...
	6 (6) [-] any 7 10077696 B 2438.377592 2438.377605 15.009 fps ts mono/EoF
	7 (7) [-] any 8 10077696 B 2438.444219 2438.444233 15.009 fps ts mono/EoF
	8 (0) [-] any 9 10077696 B 2438.510846 2438.510860 15.009 fps ts mono/EoF
	9 (1) [-] any 10 10077696 B 2438.577474 2438.577488 15.009 fps ts mono/EoF
	10 (2) [-] any 11 10077696 B 2438.644101 2438.644116 15.009 fps ts mono/EoF
	11 (3) [-] any 12 10077696 B 2438.710727 2438.710740 15.009 fps ts mono/EoF
	12 (4) [-] any 13 10077696 B 2438.777358 2438.777370 15.008 fps ts mono/EoF
	13 (5) [-] any 14 10077696 B 2438.843984 2438.843998 15.009 fps ts mono/EoF
	14 (6) [-] any 15 10077696 B 2438.910611 2438.910623 15.009 fps ts mono/EoF
	15 (7) [-] any 16 10077696 B 2438.977238 2438.977252 15.009 fps ts mono/EoF
	16 (0) [-] any 17 10077696 B 2439.043865 2439.043877 15.009 fps ts mono
	...


To enable higher FPS the LINK_FREQ control should be made writable to increase
the pixel rate

  640x480 YUYV 60 FPS (pixel_rate = 96 Mhz)

	$ yavta -f YUYV -s 640x480 -c100 --skip 7 /dev/video0
 	...
	9 (1) [-] any 10 614400 B 57.098649 57.098667 59.995 fps ts mono/EoF
	10 (2) [-] any 11 614400 B 57.115314 57.115332 60.006 fps ts mono/EoF
	11 (3) [-] any 12 614400 B 57.131978 57.131994 60.010 fps ts mono/EoF
	12 (4) [-] any 13 614400 B 57.148645 57.148664 59.999 fps ts mono/EoF
	13 (5) [-] any 14 614400 B 57.165310 57.165328 60.006 fps ts mono/EoF
	14 (6) [-] any 15 614400 B 57.181977 57.181996 59.999 fps ts mono/EoF
	15 (7) [-] any 16 614400 B 57.198642 57.198660 60.006 fps ts mono/EoF

Changelog:

v2->v3:

- Eugen (thanks) reported regression in DVP mode :(
  To maintain the DVP timings un-changed in this version the mode definition now
  looks like

		/* 640x480 */
		.id		= OV5640_MODE_VGA_640_480,
		.dn_mode	= SUBSAMPLING,
		.pixel_rate	= OV5640_PIXEL_RATE_48M,
		.width		= 640,
		.height		= 480,
		.dvp_timings = {
			.analog_crop = {
				.left	= 0,
				.top	= 4,
				.width	= 2624,
				.height	= 1944,
			},
			.crop = {
				.left	= 16,
				.top	= 6,
				.width	= 640,
				.height	= 480,
			},
			.htot		= 1896,
			.vblank_def	= 600,
			.max_fps	= OV5640_60_FPS
		},
		.csi2_timings = {
			.analog_crop = {
				/* Feed the full valid pixel array to the ISP. */
				.left	= OV5640_PIXEL_ARRAY_LEFT,
				.top	= OV5640_PIXEL_ARRAY_TOP,
				.width	= OV5640_PIXEL_ARRAY_WIDTH,
				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
			},
			.crop = {
				/* Maintain a minimum digital crop processing margins. */
				.left	= 2,
				.top	= 4,
				.width	= 640,
				.height	= 480,
			},
			.htot		= 1600,
			.vblank_def	= 520,
		},
		.reg_data	= ov5640_setting_low_res,
		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),

  with a .dvp_timings and a .csi2_timings members to separate the two.
  Is it nice ? No it's not, but it should help maintaining DVP users happy.

  Eugen: if you are willing to run another test round to confirm if this version
  does not regress DVP it would be great :)

- Split image formats between CSI-2 and DVP
- Remove RGB888 as per the CSIS discussion with Laurent
- Removed register tables for modes < 720 as they're all equal
- Minor fixes on Laurent's comments
- Add Adam's tag

v1 -> v2:
- rework the modes definition to process the full pixel array
- rework get_selection to report the correct BOUND and DEFAULT targets
- implement init_cfg
- minor style changes as suggested by Laurent
- test with 1 data lane

Jacopo Mondi (27):
  media: ov5640: Add pixel rate to modes
  media: ov5604: Re-arrange modes definition
  media: ov5640: Add ov5640_is_csi2() function
  media: ov5640: Associate bpp with formats
  media: ov5640: Add LINK_FREQ control
  media: ov5640: Update pixel_rate and link_freq
  media: ov5640: Rework CSI-2 clock tree
  media: ov5640: Rework timings programming
  media: ov5640: Fix 720x480 in RGB888 mode
  media: ov5640: Split DVP and CSI-2 timings
  media: ov5640: Provide timings accessor
  media: ov5640: Re-sort per-mode register tables
  media: ov5640: Remove duplicated mode settings
  media: ov5640: Remove ov5640_mode_init_data
  media: ov5640: Add HBLANK control
  media: ov5640: Add VBLANK control
  media: ov5640: Change CSI-2 timings to comply with FPS
  media: ov5640: Implement init_cfg
  media: ov5640: Implement get_selection
  media: ov5640: Limit frame_interval to DVP mode only
  media: ov5640: Register device properties
  media: ov5640: Add RGB565_1X16 format
  media: ov5640: Add BGR888 format
  media: ov5640: Restrict sizes to mbus code
  media: ov5640: Adjust format to bpp in s_fmt
  media: ov5640: Split DVP and CSI-2 formats
  media: ov5640: Move format mux config in format

 drivers/media/i2c/ov5640.c | 1615 ++++++++++++++++++++++++++----------
 1 file changed, 1160 insertions(+), 455 deletions(-)

--
2.35.0


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v5 01/27] media: ov5640: Add pixel rate to modes
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 02/27] media: ov5604: Re-arrange modes definition Jacopo Mondi
                   ` (28 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Add to each mode supported by the sensor the ideal pixel rate, as
defined by Table 2.1 in the chip manual.

The ideal pixel rate will be used to compute the MIPI CSI-2 clock tree.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Adam Ford <aford173@gmail.com> #imx8mm-beacon-kit
---
 drivers/media/i2c/ov5640.c | 44 +++++++++++++++++++++++++++++++++++---
 1 file changed, 41 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index db5a19babe67..504700984fa0 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -118,6 +118,29 @@ enum ov5640_frame_rate {
 	OV5640_NUM_FRAMERATES,
 };
 
+enum ov5640_pixel_rate_id {
+	OV5640_PIXEL_RATE_168M,
+	OV5640_PIXEL_RATE_148M,
+	OV5640_PIXEL_RATE_124M,
+	OV5640_PIXEL_RATE_96M,
+	OV5640_PIXEL_RATE_48M,
+	OV5640_NUM_PIXEL_RATES,
+};
+
+/*
+ * The chip manual suggests 24/48/96/192 MHz pixel clocks.
+ *
+ * 192MHz exceeds the sysclk limits; use 168MHz as maximum pixel rate for
+ * full resolution mode @15 FPS.
+ */
+static const u32 ov5640_pixel_rates[] = {
+	[OV5640_PIXEL_RATE_168M] = 168000000,
+	[OV5640_PIXEL_RATE_148M] = 148000000,
+	[OV5640_PIXEL_RATE_124M] = 124000000,
+	[OV5640_PIXEL_RATE_96M] = 96000000,
+	[OV5640_PIXEL_RATE_48M] = 48000000,
+};
+
 enum ov5640_format_mux {
 	OV5640_FMT_MUX_YUV422 = 0,
 	OV5640_FMT_MUX_RGB,
@@ -189,6 +212,7 @@ struct reg_value {
 struct ov5640_mode_info {
 	enum ov5640_mode_id id;
 	enum ov5640_downsize_mode dn_mode;
+	enum ov5640_pixel_rate_id pixel_rate;
 	u32 hact;
 	u32 htot;
 	u32 vact;
@@ -565,7 +589,9 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
 
 /* power-on sensor init reg table */
 static const struct ov5640_mode_info ov5640_mode_init_data = {
-	0, SUBSAMPLING, 640, 1896, 480, 984,
+	0, SUBSAMPLING,
+	OV5640_PIXEL_RATE_96M,
+	640, 1896, 480, 984,
 	ov5640_init_setting_30fps_VGA,
 	ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
 	OV5640_30_FPS,
@@ -574,51 +600,61 @@ static const struct ov5640_mode_info ov5640_mode_init_data = {
 static const struct ov5640_mode_info
 ov5640_mode_data[OV5640_NUM_MODES] = {
 	{OV5640_MODE_QQVGA_160_120, SUBSAMPLING,
+	 OV5640_PIXEL_RATE_48M,
 	 160, 1896, 120, 984,
 	 ov5640_setting_QQVGA_160_120,
 	 ARRAY_SIZE(ov5640_setting_QQVGA_160_120),
 	 OV5640_30_FPS},
 	{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
+	 OV5640_PIXEL_RATE_48M,
 	 176, 1896, 144, 984,
 	 ov5640_setting_QCIF_176_144,
 	 ARRAY_SIZE(ov5640_setting_QCIF_176_144),
 	 OV5640_30_FPS},
 	{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
+	 OV5640_PIXEL_RATE_48M,
 	 320, 1896, 240, 984,
 	 ov5640_setting_QVGA_320_240,
 	 ARRAY_SIZE(ov5640_setting_QVGA_320_240),
 	 OV5640_30_FPS},
 	{OV5640_MODE_VGA_640_480, SUBSAMPLING,
+	 OV5640_PIXEL_RATE_48M,
 	 640, 1896, 480, 1080,
 	 ov5640_setting_VGA_640_480,
 	 ARRAY_SIZE(ov5640_setting_VGA_640_480),
 	 OV5640_60_FPS},
 	{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
+	 OV5640_PIXEL_RATE_96M,
 	 720, 1896, 480, 984,
 	 ov5640_setting_NTSC_720_480,
 	 ARRAY_SIZE(ov5640_setting_NTSC_720_480),
 	OV5640_30_FPS},
 	{OV5640_MODE_PAL_720_576, SUBSAMPLING,
+	 OV5640_PIXEL_RATE_96M,
 	 720, 1896, 576, 984,
 	 ov5640_setting_PAL_720_576,
 	 ARRAY_SIZE(ov5640_setting_PAL_720_576),
 	 OV5640_30_FPS},
 	{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
+	 OV5640_PIXEL_RATE_96M,
 	 1024, 1896, 768, 1080,
 	 ov5640_setting_XGA_1024_768,
 	 ARRAY_SIZE(ov5640_setting_XGA_1024_768),
 	 OV5640_30_FPS},
 	{OV5640_MODE_720P_1280_720, SUBSAMPLING,
+	 OV5640_PIXEL_RATE_124M,
 	 1280, 1892, 720, 740,
 	 ov5640_setting_720P_1280_720,
 	 ARRAY_SIZE(ov5640_setting_720P_1280_720),
 	 OV5640_30_FPS},
 	{OV5640_MODE_1080P_1920_1080, SCALING,
+	 OV5640_PIXEL_RATE_148M,
 	 1920, 2500, 1080, 1120,
 	 ov5640_setting_1080P_1920_1080,
 	 ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
 	 OV5640_30_FPS},
 	{OV5640_MODE_QSXGA_2592_1944, SCALING,
+	 OV5640_PIXEL_RATE_168M,
 	 2592, 2844, 1944, 1968,
 	 ov5640_setting_QSXGA_2592_1944,
 	 ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
@@ -2743,6 +2779,7 @@ static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
 
 static int ov5640_init_controls(struct ov5640_dev *sensor)
 {
+	const struct ov5640_mode_info *mode = sensor->current_mode;
 	const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
 	struct ov5640_ctrls *ctrls = &sensor->ctrls;
 	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
@@ -2755,8 +2792,9 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
 
 	/* Clock related controls */
 	ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
-					      0, INT_MAX, 1,
-					      ov5640_calc_pixel_rate(sensor));
+			      ov5640_pixel_rates[OV5640_NUM_PIXEL_RATES - 1],
+			      ov5640_pixel_rates[0], 1,
+			      ov5640_pixel_rates[mode->pixel_rate]);
 
 	/* Auto/manual white balance */
 	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 02/27] media: ov5604: Re-arrange modes definition
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 01/27] media: ov5640: Add pixel rate to modes Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 03/27] media: ov5640: Add ov5640_is_csi2() function Jacopo Mondi
                   ` (27 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

The array of supported modes is close to unreadable.
Re-arrange it giving it some room to breath.

Cosmetic change only.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 141 +++++++++++++++++++++----------------
 1 file changed, 81 insertions(+), 60 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 504700984fa0..1f3cb84c284e 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -599,66 +599,87 @@ static const struct ov5640_mode_info ov5640_mode_init_data = {
 
 static const struct ov5640_mode_info
 ov5640_mode_data[OV5640_NUM_MODES] = {
-	{OV5640_MODE_QQVGA_160_120, SUBSAMPLING,
-	 OV5640_PIXEL_RATE_48M,
-	 160, 1896, 120, 984,
-	 ov5640_setting_QQVGA_160_120,
-	 ARRAY_SIZE(ov5640_setting_QQVGA_160_120),
-	 OV5640_30_FPS},
-	{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
-	 OV5640_PIXEL_RATE_48M,
-	 176, 1896, 144, 984,
-	 ov5640_setting_QCIF_176_144,
-	 ARRAY_SIZE(ov5640_setting_QCIF_176_144),
-	 OV5640_30_FPS},
-	{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
-	 OV5640_PIXEL_RATE_48M,
-	 320, 1896, 240, 984,
-	 ov5640_setting_QVGA_320_240,
-	 ARRAY_SIZE(ov5640_setting_QVGA_320_240),
-	 OV5640_30_FPS},
-	{OV5640_MODE_VGA_640_480, SUBSAMPLING,
-	 OV5640_PIXEL_RATE_48M,
-	 640, 1896, 480, 1080,
-	 ov5640_setting_VGA_640_480,
-	 ARRAY_SIZE(ov5640_setting_VGA_640_480),
-	 OV5640_60_FPS},
-	{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
-	 OV5640_PIXEL_RATE_96M,
-	 720, 1896, 480, 984,
-	 ov5640_setting_NTSC_720_480,
-	 ARRAY_SIZE(ov5640_setting_NTSC_720_480),
-	OV5640_30_FPS},
-	{OV5640_MODE_PAL_720_576, SUBSAMPLING,
-	 OV5640_PIXEL_RATE_96M,
-	 720, 1896, 576, 984,
-	 ov5640_setting_PAL_720_576,
-	 ARRAY_SIZE(ov5640_setting_PAL_720_576),
-	 OV5640_30_FPS},
-	{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
-	 OV5640_PIXEL_RATE_96M,
-	 1024, 1896, 768, 1080,
-	 ov5640_setting_XGA_1024_768,
-	 ARRAY_SIZE(ov5640_setting_XGA_1024_768),
-	 OV5640_30_FPS},
-	{OV5640_MODE_720P_1280_720, SUBSAMPLING,
-	 OV5640_PIXEL_RATE_124M,
-	 1280, 1892, 720, 740,
-	 ov5640_setting_720P_1280_720,
-	 ARRAY_SIZE(ov5640_setting_720P_1280_720),
-	 OV5640_30_FPS},
-	{OV5640_MODE_1080P_1920_1080, SCALING,
-	 OV5640_PIXEL_RATE_148M,
-	 1920, 2500, 1080, 1120,
-	 ov5640_setting_1080P_1920_1080,
-	 ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
-	 OV5640_30_FPS},
-	{OV5640_MODE_QSXGA_2592_1944, SCALING,
-	 OV5640_PIXEL_RATE_168M,
-	 2592, 2844, 1944, 1968,
-	 ov5640_setting_QSXGA_2592_1944,
-	 ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
-	 OV5640_15_FPS},
+	{
+		/* 160x120 */
+		OV5640_MODE_QQVGA_160_120, SUBSAMPLING,
+		OV5640_PIXEL_RATE_48M,
+		160, 1896, 120, 984,
+		ov5640_setting_QQVGA_160_120,
+		ARRAY_SIZE(ov5640_setting_QQVGA_160_120),
+		OV5640_30_FPS
+	}, {
+		/* 176x144 */
+		OV5640_MODE_QCIF_176_144, SUBSAMPLING,
+		OV5640_PIXEL_RATE_48M,
+		176, 1896, 144, 984,
+		ov5640_setting_QCIF_176_144,
+		ARRAY_SIZE(ov5640_setting_QCIF_176_144),
+		OV5640_30_FPS
+	}, {
+		/* 320x240 */
+		OV5640_MODE_QVGA_320_240, SUBSAMPLING,
+		OV5640_PIXEL_RATE_48M,
+		320, 1896, 240, 984,
+		ov5640_setting_QVGA_320_240,
+		ARRAY_SIZE(ov5640_setting_QVGA_320_240),
+		OV5640_30_FPS
+	}, {
+		/* 640x480 */
+		OV5640_MODE_VGA_640_480, SUBSAMPLING,
+		OV5640_PIXEL_RATE_48M,
+		640, 1896, 480, 1080,
+		ov5640_setting_VGA_640_480,
+		ARRAY_SIZE(ov5640_setting_VGA_640_480),
+		OV5640_60_FPS
+	}, {
+		/* 720x480 */
+		OV5640_MODE_NTSC_720_480, SUBSAMPLING,
+		OV5640_PIXEL_RATE_96M,
+		720, 1896, 480, 984,
+		ov5640_setting_NTSC_720_480,
+		ARRAY_SIZE(ov5640_setting_NTSC_720_480),
+		OV5640_30_FPS
+	}, {
+		/* 720x576 */
+		OV5640_MODE_PAL_720_576, SUBSAMPLING,
+		OV5640_PIXEL_RATE_96M,
+		720, 1896, 576, 984,
+		ov5640_setting_PAL_720_576,
+		ARRAY_SIZE(ov5640_setting_PAL_720_576),
+		OV5640_30_FPS
+	}, {
+		/* 1024x768 */
+		OV5640_MODE_XGA_1024_768, SUBSAMPLING,
+		OV5640_PIXEL_RATE_96M,
+		1024, 1896, 768, 1080,
+		ov5640_setting_XGA_1024_768,
+		ARRAY_SIZE(ov5640_setting_XGA_1024_768),
+		OV5640_30_FPS
+	}, {
+		/* 1280x720 */
+		OV5640_MODE_720P_1280_720, SUBSAMPLING,
+		OV5640_PIXEL_RATE_124M,
+		1280, 1892, 720, 740,
+		ov5640_setting_720P_1280_720,
+		ARRAY_SIZE(ov5640_setting_720P_1280_720),
+		OV5640_30_FPS
+	}, {
+		/* 1920x1080 */
+		OV5640_MODE_1080P_1920_1080, SCALING,
+		OV5640_PIXEL_RATE_148M,
+		1920, 2500, 1080, 1120,
+		ov5640_setting_1080P_1920_1080,
+		ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
+		OV5640_30_FPS
+	}, {
+		/* 2592x1944 */
+		OV5640_MODE_QSXGA_2592_1944, SCALING,
+		OV5640_PIXEL_RATE_168M,
+		2592, 2844, 1944, 1968,
+		ov5640_setting_QSXGA_2592_1944,
+		ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
+		OV5640_15_FPS
+	},
 };
 
 static int ov5640_init_slave_id(struct ov5640_dev *sensor)
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 03/27] media: ov5640: Add ov5640_is_csi2() function
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 01/27] media: ov5640: Add pixel rate to modes Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 02/27] media: ov5604: Re-arrange modes definition Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 04/27] media: ov5640: Associate bpp with formats Jacopo Mondi
                   ` (26 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Checking if the sensor is used in DVP or MIPI CSI-2 mode is a repeated
pattern which is about to be repeated more often.

Provide an inline function to shortcut that.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 1f3cb84c284e..0697c9bf03ed 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -294,6 +294,11 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
 			     ctrls.handler)->sd;
 }
 
+static inline bool ov5640_is_csi2(const struct ov5640_dev *sensor)
+{
+	return sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY;
+}
+
 /*
  * FIXME: all of these register tables are likely filled with
  * entries that set the register to their power-on default values,
@@ -1208,7 +1213,7 @@ static int ov5640_load_regs(struct ov5640_dev *sensor,
 		/* remain in power down mode for DVP */
 		if (regs->reg_addr == OV5640_REG_SYS_CTRL0 &&
 		    val == OV5640_REG_SYS_CTRL0_SW_PWUP &&
-		    sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY)
+		    !ov5640_is_csi2(sensor))
 			continue;
 
 		if (mask)
@@ -1843,7 +1848,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
 	 * the same rate than YUV, so we can just use 16 bpp all the time.
 	 */
 	rate = ov5640_calc_pixel_rate(sensor) * 16;
-	if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
+	if (ov5640_is_csi2(sensor)) {
 		rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes;
 		ret = ov5640_set_mipi_pclk(sensor, rate);
 	} else {
@@ -3020,7 +3025,7 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
 			sensor->pending_fmt_change = false;
 		}
 
-		if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY)
+		if (ov5640_is_csi2(sensor))
 			ret = ov5640_set_stream_mipi(sensor, enable);
 		else
 			ret = ov5640_set_stream_dvp(sensor, enable);
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 04/27] media: ov5640: Associate bpp with formats
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (2 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 03/27] media: ov5640: Add ov5640_is_csi2() function Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 05/27] media: ov5640: Add LINK_FREQ control Jacopo Mondi
                   ` (25 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Associate the bit depth to each format supported by the sensor.

The bpp will be used to calculate the line length.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 73 ++++++++++++++++++++++++++++++--------
 1 file changed, 59 insertions(+), 14 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 0697c9bf03ed..1b2c7623fb14 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -150,24 +150,69 @@ enum ov5640_format_mux {
 	OV5640_FMT_MUX_RAW_CIP,
 };
 
-struct ov5640_pixfmt {
+static const struct ov5640_pixfmt {
 	u32 code;
 	u32 colorspace;
+	u8 bpp;
+} ov5640_formats[] = {
+	{
+		.code = MEDIA_BUS_FMT_JPEG_1X8,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.bpp = 16,
+	}, {
+		.code = MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 16,
+	}, {
+		.code = MEDIA_BUS_FMT_UYVY8_1X16,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 16,
+	}, {
+		.code = MEDIA_BUS_FMT_YUYV8_2X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 16,
+	}, {
+		.code = MEDIA_BUS_FMT_YUYV8_1X16,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 16,
+	}, {
+		.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 16,
+	}, {
+		.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 16,
+	}, {
+		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 8,
+	}, {
+		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 8
+	}, {
+		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 8,
+	}, {
+		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 8,
+	},
 };
 
-static const struct ov5640_pixfmt ov5640_formats[] = {
-	{ MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, },
-	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, },
-	{ MEDIA_BUS_FMT_UYVY8_1X16, V4L2_COLORSPACE_SRGB, },
-	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, },
-	{ MEDIA_BUS_FMT_YUYV8_1X16, V4L2_COLORSPACE_SRGB, },
-	{ MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, },
-	{ MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, },
-	{ MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, },
-	{ MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_COLORSPACE_SRGB, },
-	{ MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_COLORSPACE_SRGB, },
-	{ MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB, },
-};
+static u32 ov5640_code_to_bpp(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ov5640_formats); ++i) {
+		if (ov5640_formats[i].code == code)
+			return ov5640_formats[i].bpp;
+	}
+
+	return 0;
+}
 
 /*
  * FIXME: remove this when a subdev API becomes available
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 05/27] media: ov5640: Add LINK_FREQ control
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (3 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 04/27] media: ov5640: Associate bpp with formats Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 06/27] media: ov5640: Update pixel_rate and link_freq Jacopo Mondi
                   ` (24 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Add the V4L2_CID_LINK_FREQ control to the ov5640 driver.
Make the control read-only for the moment.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 1b2c7623fb14..f9763edf2422 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -141,6 +141,24 @@ static const u32 ov5640_pixel_rates[] = {
 	[OV5640_PIXEL_RATE_48M] = 48000000,
 };
 
+/*
+ * MIPI CSI-2 link frequencies.
+ *
+ * Derived from the above defined pixel rate for bpp = (8, 16, 24) and
+ * data_lanes = (1, 2)
+ *
+ * link_freq = (pixel_rate * bpp) / (2 * data_lanes)
+ */
+static const s64 ov5640_csi2_link_freqs[] = {
+	992000000, 888000000, 768000000, 744000000, 672000000, 672000000,
+	592000000, 592000000, 576000000, 576000000, 496000000, 496000000,
+	384000000, 384000000, 384000000, 336000000, 296000000, 288000000,
+	248000000, 192000000, 192000000, 192000000, 96000000,
+};
+
+/* Link freq for default mode: UYVY 16 bpp, 2 data lanes. */
+#define OV5640_DEFAULT_LINK_FREQ	13
+
 enum ov5640_format_mux {
 	OV5640_FMT_MUX_YUV422 = 0,
 	OV5640_FMT_MUX_RGB,
@@ -270,6 +288,7 @@ struct ov5640_mode_info {
 struct ov5640_ctrls {
 	struct v4l2_ctrl_handler handler;
 	struct v4l2_ctrl *pixel_rate;
+	struct v4l2_ctrl *link_freq;
 	struct {
 		struct v4l2_ctrl *auto_exp;
 		struct v4l2_ctrl *exposure;
@@ -2867,6 +2886,12 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
 			      ov5640_pixel_rates[0], 1,
 			      ov5640_pixel_rates[mode->pixel_rate]);
 
+	ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops,
+					V4L2_CID_LINK_FREQ,
+					ARRAY_SIZE(ov5640_csi2_link_freqs) - 1,
+					OV5640_DEFAULT_LINK_FREQ,
+					ov5640_csi2_link_freqs);
+
 	/* Auto/manual white balance */
 	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
 					   V4L2_CID_AUTO_WHITE_BALANCE,
@@ -2915,6 +2940,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
 	}
 
 	ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 	ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
 	ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
 
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 06/27] media: ov5640: Update pixel_rate and link_freq
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (4 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 05/27] media: ov5640: Add LINK_FREQ control Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-04-07 16:25   ` Hugues FRUCHET - FOSS
  2022-02-24  9:42 ` [PATCH v5 07/27] media: ov5640: Rework CSI-2 clock tree Jacopo Mondi
                   ` (23 subsequent siblings)
  29 siblings, 1 reply; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

After having set a new format re-calculate the pixel_rate and link_freq
control values and update them when in MIPI mode.

Take into account the limitation of the link frequency having to be
strictly smaller than 1GHz when computing the desired link_freq, and
adjust the resulting pixel_rate acounting for the clock tree
configuration.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 66 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 64 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index f9763edf2422..791694bfed41 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -31,6 +31,8 @@
 
 #define OV5640_DEFAULT_SLAVE_ID 0x3c
 
+#define OV5640_LINK_RATE_MAX		490000000U
+
 #define OV5640_REG_SYS_RESET02		0x3002
 #define OV5640_REG_SYS_CLOCK_ENABLE02	0x3006
 #define OV5640_REG_SYS_CTRL0		0x3008
@@ -2412,6 +2414,66 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
+{
+	const struct ov5640_mode_info *mode = sensor->current_mode;
+	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
+	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
+	unsigned int i = 0;
+	u32 pixel_rate;
+	s64 link_freq;
+	u32 num_lanes;
+	u32 bpp;
+
+	/*
+	 * Update the pixel rate control value.
+	 *
+	 * For DVP mode, maintain the pixel rate calculation using fixed FPS.
+	 */
+	if (!ov5640_is_csi2(sensor)) {
+		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
+					 ov5640_calc_pixel_rate(sensor));
+
+		return 0;
+	}
+
+	/*
+	 * The MIPI CSI-2 link frequency should comply with the CSI-2
+	 * specification and be lower than 1GHz.
+	 *
+	 * Start from the suggested pixel_rate for the current mode and
+	 * progressively slow it down if it exceeds 1GHz.
+	 */
+	num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes;
+	bpp = ov5640_code_to_bpp(fmt->code);
+	do {
+		pixel_rate = ov5640_pixel_rates[pixel_rate_id];
+		link_freq = pixel_rate * bpp / (2 * num_lanes);
+	} while (link_freq >= 1000000000U &&
+		 ++pixel_rate_id < OV5640_NUM_PIXEL_RATES);
+
+	/*
+	 * Higher link rates require the clock tree to be programmed with
+	 * 'mipi_div' = 1; this has the effect of halving the actual output
+	 * pixel rate in the MIPI domain.
+	 *
+	 * Adjust the pixel rate control value to report it correctly to
+	 * userspace.
+	 */
+	if (link_freq > OV5640_LINK_RATE_MAX)
+		pixel_rate /= 2;
+
+	for (i = 0; i < ARRAY_SIZE(ov5640_csi2_link_freqs); ++i) {
+		if (ov5640_csi2_link_freqs[i] == link_freq)
+			break;
+	}
+
+	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, pixel_rate);
+	__v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, i);
+
+	return 0;
+}
+
 static int ov5640_set_fmt(struct v4l2_subdev *sd,
 			  struct v4l2_subdev_state *sd_state,
 			  struct v4l2_subdev_format *format)
@@ -2451,8 +2513,8 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
 	/* update format even if code is unchanged, resolution might change */
 	sensor->fmt = *mbus_fmt;
 
-	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
-				 ov5640_calc_pixel_rate(sensor));
+	ov5640_update_pixel_rate(sensor);
+
 out:
 	mutex_unlock(&sensor->lock);
 	return ret;
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 07/27] media: ov5640: Rework CSI-2 clock tree
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (5 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 06/27] media: ov5640: Update pixel_rate and link_freq Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 08/27] media: ov5640: Rework timings programming Jacopo Mondi
                   ` (22 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Re-work the ov5640_set_mipi_pclk() function to calculate the
PLL configuration using the pixel_rate and link_freq values set at
s_fmt time.

Rework the DVP clock mode settings to calculate the pixel clock
internally and remove the assumption on the 16bpp format.

Tested in MIPI CSI-2 mode with 1 and 2 data lanes with:
- all the sensor supported resolutions in UYVY and RGB565 formats.
- resolutions >= 1280x720 in RAW Bayer format.
- resolutions < 1280x720 in RGB888 format.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 192 ++++++++++++++++++-------------------
 1 file changed, 92 insertions(+), 100 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 791694bfed41..09748fa84348 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -90,6 +90,7 @@
 #define OV5640_REG_POLARITY_CTRL00	0x4740
 #define OV5640_REG_MIPI_CTRL00		0x4800
 #define OV5640_REG_DEBUG_MODE		0x4814
+#define OV5640_REG_PCLK_PERIOD		0x4837
 #define OV5640_REG_ISP_FORMAT_MUX_CTRL	0x501f
 #define OV5640_REG_PRE_ISP_TEST_SET1	0x503d
 #define OV5640_REG_SDE_CTRL0		0x5580
@@ -925,20 +926,10 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
  *                                +-----+-----+
  *                                       +------------> PCLK
  *
- * This is deviating from the datasheet at least for the register
- * 0x3108, since it's said here that the PCLK would be clocked from
- * the PLL.
- *
- * There seems to be also (unverified) constraints:
+ * There seems to be also constraints:
  *  - the PLL pre-divider output rate should be in the 4-27MHz range
  *  - the PLL multiplier output rate should be in the 500-1000MHz range
  *  - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
- *
- * In the two latter cases, these constraints are met since our
- * factors are hardcoded. If we were to change that, we would need to
- * take this into account. The only varying parts are the PLL
- * multiplier and the system clock divider, which are shared between
- * all these clocks so won't cause any issue.
  */
 
 /*
@@ -957,13 +948,6 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
 #define OV5640_SYSDIV_MIN	1
 #define OV5640_SYSDIV_MAX	16
 
-/*
- * Hardcode these values for scaler and non-scaler modes.
- * FIXME: to be re-calcualted for 1 data lanes setups
- */
-#define OV5640_MIPI_DIV_PCLK	2
-#define OV5640_MIPI_DIV_SCLK	1
-
 /*
  * This is supposed to be ranging from 1 to 2, but the value is always
  * set to 2 in the vendor kernels.
@@ -1073,70 +1057,79 @@ static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
 /*
  * ov5640_set_mipi_pclk() - Calculate the clock tree configuration values
  *			    for the MIPI CSI-2 output.
- *
- * @rate: The requested bandwidth per lane in bytes per second.
- *	  'Bandwidth Per Lane' is calculated as:
- *	  bpl = HTOT * VTOT * FPS * bpp / num_lanes;
- *
- * This function use the requested bandwidth to calculate:
- * - sample_rate = bpl / (bpp / num_lanes);
- *	         = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes);
- *
- * - mipi_sclk   = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR)
- *
- * with these fixed parameters:
- *	PLL_RDIV	= 2;
- *	BIT_DIVIDER	= 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
- *	PCLK_DIV	= 1;
- *
- * The MIPI clock generation differs for modes that use the scaler and modes
- * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI
- * BIT CLk, and thus:
- *
- * - mipi_sclk = bpl / MIPI_DIV / 2;
- *   MIPI_DIV = 1;
- *
- * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated
- * from the pixel clock, and thus:
- *
- * - sample_rate = bpl / (bpp / num_lanes);
- *	         = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes);
- *		 = bpl / (4 * MIPI_DIV / num_lanes);
- * - MIPI_DIV	 = bpp / (4 * num_lanes);
- *
- * FIXME: this have been tested with 16bpp and 2 lanes setup only.
- * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
- * above formula for setups with 1 lane or image formats with different bpp.
- *
- * FIXME: this deviates from the sensor manual documentation which is quite
- * thin on the MIPI clock tree generation part.
  */
-static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor,
-				unsigned long rate)
+static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor)
 {
-	const struct ov5640_mode_info *mode = sensor->current_mode;
+	u8 bit_div, mipi_div, pclk_div, sclk_div, sclk2x_div, root_div;
+	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
 	u8 prediv, mult, sysdiv;
-	u8 mipi_div;
+	unsigned long sysclk;
+	unsigned long sample_rate;
+	u8 pclk_period;
+	s64 link_freq;
 	int ret;
 
+	/* Use the link freq computed at ov5640_update_pixel_rate() time. */
+	link_freq = ov5640_csi2_link_freqs[sensor->ctrls.link_freq->cur.val];
+
 	/*
-	 * 1280x720 is reported to use 'SUBSAMPLING' only,
-	 * but according to the sensor manual it goes through the
-	 * scaler before subsampling.
+	 * - mipi_div - Additional divider for the MIPI lane clock.
+	 *
+	 * Higher link frequencies would make sysclk > 1GHz.
+	 * Keep the sysclk low and do not divide in the MIPI domain.
 	 */
-	if (mode->dn_mode == SCALING ||
-	   (mode->id == OV5640_MODE_720P_1280_720))
-		mipi_div = OV5640_MIPI_DIV_SCLK;
+	if (link_freq > OV5640_LINK_RATE_MAX)
+		mipi_div = 1;
 	else
-		mipi_div = OV5640_MIPI_DIV_PCLK;
+		mipi_div = 2;
 
-	ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv);
+	sysclk = link_freq * mipi_div;
+	ov5640_calc_sys_clk(sensor, sysclk, &prediv, &mult, &sysdiv);
 
-	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
-			     0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT);
+	/*
+	 * Adjust PLL parameters to maintain the MIPI_SCLK-to-PCLK ratio.
+	 *
+	 * - root_div = 2 (fixed)
+	 * - bit_div : MIPI 8-bit = 2; MIPI 10-bit = 2.5
+	 * - pclk_div = 1 (fixed)
+	 * - p_div  = (2 lanes ? mipi_div : 2 * mipi_div)
+	 *
+	 * This results in the following MIPI_SCLK depending on the number
+	 * of lanes:
+	 *
+	 * - 2 lanes: MIPI_SCLK = (4 or 5) * PCLK
+	 * - 1 lanes: MIPI_SCLK = (8 or 10) * PCLK
+	 */
+	root_div = OV5640_PLL_CTRL3_PLL_ROOT_DIV_2;
+	bit_div =  OV5640_PLL_CTRL0_MIPI_MODE_8BIT;
+	pclk_div = ilog2(OV5640_PCLK_ROOT_DIV);
 
-	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
-			     0xff, sysdiv << 4 | mipi_div);
+	/*
+	 * Scaler clock:
+	 * - YUV: PCLK >= 2 * SCLK
+	 * - RAW or JPEG: PCLK >= SCLK
+	 * - sclk2x_div = sclk_div / 2
+	 *
+	 * TODO: test with JPEG.
+	 */
+	sclk_div = ilog2(OV5640_SCLK_ROOT_DIV);
+	sclk2x_div = ilog2(OV5640_SCLK2X_ROOT_DIV);
+
+	/*
+	 * Set the sample period expressed in ns with 1-bit decimal
+	 * (0x01=0.5ns).
+	 */
+	sample_rate = ov5640_pixel_rates[sensor->current_mode->pixel_rate]
+		    * (ov5640_code_to_bpp(fmt->code) / 8);
+	pclk_period = 2000000000U / sample_rate;
+
+	/* Program the clock tree registers. */
+	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, 0x0f, bit_div);
+	if (ret)
+		return ret;
+
+	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, 0xff,
+			     (sysdiv << 4) | mipi_div);
 	if (ret)
 		return ret;
 
@@ -1144,13 +1137,27 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor,
 	if (ret)
 		return ret;
 
-	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
-			     0x1f, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv);
+	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0x1f,
+			     root_div | prediv);
 	if (ret)
 		return ret;
 
-	return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER,
-			      0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS);
+	ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
+			     (pclk_div << 4) | (sclk2x_div << 2) | sclk_div);
+	if (ret)
+		return ret;
+
+	return ov5640_write_reg(sensor, OV5640_REG_PCLK_PERIOD, pclk_period);
+}
+
+static u32 ov5640_calc_pixel_rate(struct ov5640_dev *sensor)
+{
+	u32 rate;
+
+	rate = sensor->current_mode->vtot * sensor->current_mode->htot;
+	rate *= ov5640_framerates[sensor->current_fr];
+
+	return rate;
 }
 
 static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
@@ -1170,11 +1177,16 @@ static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
 	return _rate / *pll_rdiv / *bit_div / *pclk_div;
 }
 
-static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
+static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor)
 {
 	u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
+	u32 rate;
 	int ret;
 
+	rate = ov5640_calc_pixel_rate(sensor);
+	rate *= ov5640_code_to_bpp(sensor->fmt.code);
+	rate /= sensor->ep.bus.parallel.bus_width;
+
 	ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
 			 &bit_div, &pclk_div);
 
@@ -1699,16 +1711,6 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
 	return mode;
 }
 
-static u64 ov5640_calc_pixel_rate(struct ov5640_dev *sensor)
-{
-	u64 rate;
-
-	rate = sensor->current_mode->vtot * sensor->current_mode->htot;
-	rate *= ov5640_framerates[sensor->current_fr];
-
-	return rate;
-}
-
 /*
  * sensor changes between scaling and subsampling, go through
  * exposure calculation
@@ -1890,7 +1892,6 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
 	enum ov5640_downsize_mode dn_mode, orig_dn_mode;
 	bool auto_gain = sensor->ctrls.auto_gain->val == 1;
 	bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
-	unsigned long rate;
 	int ret;
 
 	dn_mode = mode->dn_mode;
@@ -1909,19 +1910,10 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
 			goto restore_auto_gain;
 	}
 
-	/*
-	 * All the formats we support have 16 bits per pixel, seems to require
-	 * the same rate than YUV, so we can just use 16 bpp all the time.
-	 */
-	rate = ov5640_calc_pixel_rate(sensor) * 16;
-	if (ov5640_is_csi2(sensor)) {
-		rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes;
-		ret = ov5640_set_mipi_pclk(sensor, rate);
-	} else {
-		rate = rate / sensor->ep.bus.parallel.bus_width;
-		ret = ov5640_set_dvp_pclk(sensor, rate);
-	}
-
+	if (ov5640_is_csi2(sensor))
+		ret = ov5640_set_mipi_pclk(sensor);
+	else
+		ret = ov5640_set_dvp_pclk(sensor);
 	if (ret < 0)
 		return 0;
 
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 08/27] media: ov5640: Rework timings programming
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (6 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 07/27] media: ov5640: Rework CSI-2 clock tree Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 09/27] media: ov5640: Fix 720x480 in RGB888 mode Jacopo Mondi
                   ` (21 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

The current definition of a sensor mode defines timings as follows:

- hact, vact: Visible width and height
- htot, vtot: Total sizes including blankings

This makes difficult to clearly separate the visible sizes from the
blankings and to make the vertical blanking programmable.

Rework the sensor modes sizes definition to:
- Report the analog crop sizes
- Report the visible crop size
- Report the total pixels per line as HBLANK is fixed
- Report the VBLANK value to make it programmable

Also modify the ov5640_set_timings() function to program all the
windowing registers are remove them from the per-mode register-value
tables.

Do not change the timing values from the ones reported in the register
tables to maintain bisectability.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 445 +++++++++++++++++++++++++------------
 1 file changed, 300 insertions(+), 145 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 09748fa84348..a3d15e5bcb7d 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -61,10 +61,16 @@
 #define OV5640_REG_AEC_PK_MANUAL	0x3503
 #define OV5640_REG_AEC_PK_REAL_GAIN	0x350a
 #define OV5640_REG_AEC_PK_VTS		0x350c
+#define OV5640_REG_TIMING_HS		0x3800
+#define OV5640_REG_TIMING_VS		0x3802
+#define OV5640_REG_TIMING_HW		0x3804
+#define OV5640_REG_TIMING_VH		0x3806
 #define OV5640_REG_TIMING_DVPHO		0x3808
 #define OV5640_REG_TIMING_DVPVO		0x380a
 #define OV5640_REG_TIMING_HTS		0x380c
 #define OV5640_REG_TIMING_VTS		0x380e
+#define OV5640_REG_TIMING_HOFFS		0x3810
+#define OV5640_REG_TIMING_VOFFS		0x3812
 #define OV5640_REG_TIMING_TC_REG20	0x3820
 #define OV5640_REG_TIMING_TC_REG21	0x3821
 #define OV5640_REG_AEC_CTRL00		0x3a00
@@ -279,12 +285,17 @@ struct ov5640_mode_info {
 	enum ov5640_mode_id id;
 	enum ov5640_downsize_mode dn_mode;
 	enum ov5640_pixel_rate_id pixel_rate;
-	u32 hact;
+	/* Analog crop rectangle. */
+	struct v4l2_rect analog_crop;
+	/* Visibile crop: from analog crop top-left corner. */
+	struct v4l2_rect crop;
+	/* Total pixels per line: crop.width + fixed hblank. */
 	u32 htot;
-	u32 vact;
-	u32 vtot;
+	/* Default vertical blanking: frame height = crop.height + vblank. */
+	u32 vblank_def;
 	const struct reg_value *reg_data;
 	u32 reg_data_size;
+	/* DVP only; ignored in MIPI mode. */
 	u32 max_fps;
 };
 
@@ -390,11 +401,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
 	{0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3815, 0x31, 0, 0},
 	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
 	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
 	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -462,11 +469,7 @@ static const struct reg_value ov5640_setting_VGA_640_480[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3815, 0x31, 0, 0},
 	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
 	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
 	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -481,11 +484,7 @@ static const struct reg_value ov5640_setting_XGA_1024_768[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3815, 0x31, 0, 0},
 	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
 	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
 	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -500,11 +499,7 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3815, 0x31, 0, 0},
 	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
 	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
 	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -519,11 +514,7 @@ static const struct reg_value ov5640_setting_QQVGA_160_120[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3815, 0x31, 0, 0},
 	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
 	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
 	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -537,11 +528,7 @@ static const struct reg_value ov5640_setting_QCIF_176_144[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3815, 0x31, 0, 0},
 	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
 	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
 	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -556,11 +543,7 @@ static const struct reg_value ov5640_setting_NTSC_720_480[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
+	{0x3815, 0x31, 0, 0},
 	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
 	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
 	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -575,11 +558,7 @@ static const struct reg_value ov5640_setting_PAL_720_576[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3815, 0x31, 0, 0},
 	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
 	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
 	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -594,11 +573,7 @@ static const struct reg_value ov5640_setting_720P_1280_720[] = {
 	{0x3c07, 0x07, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3815, 0x31, 0, 0},
 	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
 	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
 	{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
@@ -613,11 +588,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x11, 0, 0},
-	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3815, 0x11, 0, 0},
 	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
 	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
 	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -628,9 +599,6 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
 	{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
-	{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
-	{0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0},
 	{0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
 	{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
 	{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
@@ -644,11 +612,7 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x11, 0, 0},
-	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3815, 0x11, 0, 0},
 	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
 	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
 	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
@@ -661,96 +625,250 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
 
 /* power-on sensor init reg table */
 static const struct ov5640_mode_info ov5640_mode_init_data = {
-	0, SUBSAMPLING,
-	OV5640_PIXEL_RATE_96M,
-	640, 1896, 480, 984,
-	ov5640_init_setting_30fps_VGA,
-	ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
-	OV5640_30_FPS,
+		.id		= 0,
+		.dn_mode	= SUBSAMPLING,
+		.pixel_rate	= OV5640_PIXEL_RATE_96M,
+		.analog_crop = {
+			.left	= 0,
+			.top	= 4,
+			.width	= 2624,
+			.height	= 1944,
+		},
+		.crop = {
+			.left	= 16,
+			.top	= 6,
+			.width	= 640,
+			.height	= 480,
+		},
+		.htot		= 1896,
+		.vblank_def	= 504,
+		.reg_data	= ov5640_init_setting_30fps_VGA,
+		.reg_data_size	= ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
+		.max_fps	= OV5640_30_FPS
 };
 
 static const struct ov5640_mode_info
 ov5640_mode_data[OV5640_NUM_MODES] = {
 	{
 		/* 160x120 */
-		OV5640_MODE_QQVGA_160_120, SUBSAMPLING,
-		OV5640_PIXEL_RATE_48M,
-		160, 1896, 120, 984,
-		ov5640_setting_QQVGA_160_120,
-		ARRAY_SIZE(ov5640_setting_QQVGA_160_120),
-		OV5640_30_FPS
+		.id		= OV5640_MODE_QQVGA_160_120,
+		.dn_mode	= SUBSAMPLING,
+		.pixel_rate	= OV5640_PIXEL_RATE_48M,
+		.analog_crop = {
+			.left	= 0,
+			.top	= 4,
+			.width	= 2624,
+			.height	= 1944,
+		},
+		.crop = {
+			.left	= 16,
+			.top	= 6,
+			.width	= 160,
+			.height	= 120,
+		},
+		.htot		= 1896,
+		.vblank_def	= 864,
+		.reg_data	= ov5640_setting_QQVGA_160_120,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QQVGA_160_120),
+		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 176x144 */
-		OV5640_MODE_QCIF_176_144, SUBSAMPLING,
-		OV5640_PIXEL_RATE_48M,
-		176, 1896, 144, 984,
-		ov5640_setting_QCIF_176_144,
-		ARRAY_SIZE(ov5640_setting_QCIF_176_144),
-		OV5640_30_FPS
+		.id		= OV5640_MODE_QCIF_176_144,
+		.dn_mode	= SUBSAMPLING,
+		.pixel_rate	= OV5640_PIXEL_RATE_48M,
+		.analog_crop = {
+			.left	= 0,
+			.top	= 4,
+			.width	= 2624,
+			.height	= 1944,
+		},
+		.crop = {
+			.left	= 16,
+			.top	= 6,
+			.width	= 176,
+			.height	= 144,
+		},
+		.htot		= 1896,
+		.vblank_def	= 840,
+		.reg_data	= ov5640_setting_QCIF_176_144,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QCIF_176_144),
+		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 320x240 */
-		OV5640_MODE_QVGA_320_240, SUBSAMPLING,
-		OV5640_PIXEL_RATE_48M,
-		320, 1896, 240, 984,
-		ov5640_setting_QVGA_320_240,
-		ARRAY_SIZE(ov5640_setting_QVGA_320_240),
-		OV5640_30_FPS
+		.id		= OV5640_MODE_QVGA_320_240,
+		.dn_mode	= SUBSAMPLING,
+		.pixel_rate	= OV5640_PIXEL_RATE_48M,
+		.analog_crop = {
+			.left	= 0,
+			.top	= 4,
+			.width	= 2624,
+			.height	= 1944,
+		},
+		.crop = {
+			.left	= 16,
+			.top	= 6,
+			.width	= 320,
+			.height	= 240,
+		},
+		.htot		= 1896,
+		.vblank_def	= 744,
+		.reg_data	= ov5640_setting_QVGA_320_240,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QVGA_320_240),
+		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 640x480 */
-		OV5640_MODE_VGA_640_480, SUBSAMPLING,
-		OV5640_PIXEL_RATE_48M,
-		640, 1896, 480, 1080,
-		ov5640_setting_VGA_640_480,
-		ARRAY_SIZE(ov5640_setting_VGA_640_480),
-		OV5640_60_FPS
+		.id		= OV5640_MODE_VGA_640_480,
+		.dn_mode	= SUBSAMPLING,
+		.pixel_rate	= OV5640_PIXEL_RATE_48M,
+		.analog_crop = {
+			.left	= 0,
+			.top	= 4,
+			.width	= 2624,
+			.height	= 1944,
+		},
+		.crop = {
+			.left	= 16,
+			.top	= 6,
+			.width	= 640,
+			.height	= 480,
+		},
+		.htot		= 1896,
+		.vblank_def	= 600,
+		.reg_data	= ov5640_setting_VGA_640_480,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_VGA_640_480),
+		.max_fps	= OV5640_60_FPS
 	}, {
 		/* 720x480 */
-		OV5640_MODE_NTSC_720_480, SUBSAMPLING,
-		OV5640_PIXEL_RATE_96M,
-		720, 1896, 480, 984,
-		ov5640_setting_NTSC_720_480,
-		ARRAY_SIZE(ov5640_setting_NTSC_720_480),
-		OV5640_30_FPS
+		.id		= OV5640_MODE_NTSC_720_480,
+		.dn_mode	= SUBSAMPLING,
+		.pixel_rate	= OV5640_PIXEL_RATE_96M,
+		.analog_crop = {
+			.left	= 0,
+			.top	= 4,
+			.width	= 2624,
+			.height	= 1944,
+		},
+		.crop = {
+			.left	= 16,
+			.top	= 60,
+			.width	= 720,
+			.height	= 480,
+		},
+		.htot		= 1896,
+		.vblank_def	= 504,
+		.reg_data	= ov5640_setting_NTSC_720_480,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_NTSC_720_480),
+		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 720x576 */
-		OV5640_MODE_PAL_720_576, SUBSAMPLING,
-		OV5640_PIXEL_RATE_96M,
-		720, 1896, 576, 984,
-		ov5640_setting_PAL_720_576,
-		ARRAY_SIZE(ov5640_setting_PAL_720_576),
-		OV5640_30_FPS
+		.id		= OV5640_MODE_PAL_720_576,
+		.dn_mode	= SUBSAMPLING,
+		.pixel_rate	= OV5640_PIXEL_RATE_96M,
+		.analog_crop = {
+			.left	= 0,
+			.top	= 4,
+			.width	= 2624,
+			.height	= 1944,
+		},
+		.crop = {
+			.left	= 56,
+			.top	= 6,
+			.width	= 720,
+			.height	= 576,
+		},
+		.htot		= 1896,
+		.vblank_def	= 408,
+		.reg_data	= ov5640_setting_PAL_720_576,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_PAL_720_576),
+		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 1024x768 */
-		OV5640_MODE_XGA_1024_768, SUBSAMPLING,
-		OV5640_PIXEL_RATE_96M,
-		1024, 1896, 768, 1080,
-		ov5640_setting_XGA_1024_768,
-		ARRAY_SIZE(ov5640_setting_XGA_1024_768),
-		OV5640_30_FPS
+		.id		= OV5640_MODE_XGA_1024_768,
+		.dn_mode	= SUBSAMPLING,
+		.pixel_rate	= OV5640_PIXEL_RATE_96M,
+		.analog_crop = {
+			.left	= 0,
+			.top	= 4,
+			.width	= 2624,
+			.height	= 1944,
+		},
+		.crop = {
+			.left	= 16,
+			.top	= 6,
+			.width	= 1024,
+			.height	= 768,
+		},
+		.htot		= 1896,
+		.vblank_def	= 312,
+		.reg_data	= ov5640_setting_XGA_1024_768,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_XGA_1024_768),
+		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 1280x720 */
-		OV5640_MODE_720P_1280_720, SUBSAMPLING,
-		OV5640_PIXEL_RATE_124M,
-		1280, 1892, 720, 740,
-		ov5640_setting_720P_1280_720,
-		ARRAY_SIZE(ov5640_setting_720P_1280_720),
-		OV5640_30_FPS
+		.id		= OV5640_MODE_720P_1280_720,
+		.dn_mode	= SUBSAMPLING,
+		.pixel_rate	= OV5640_PIXEL_RATE_124M,
+		.analog_crop = {
+			.left	= 0,
+			.top	= 250,
+			.width	= 2624,
+			.height	= 1456,
+		},
+		.crop = {
+			.left	= 16,
+			.top	= 4,
+			.width	= 1280,
+			.height	= 720,
+		},
+		.htot		= 1892,
+		.vblank_def	= 20,
+		.reg_data	= ov5640_setting_720P_1280_720,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_720P_1280_720),
+		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 1920x1080 */
-		OV5640_MODE_1080P_1920_1080, SCALING,
-		OV5640_PIXEL_RATE_148M,
-		1920, 2500, 1080, 1120,
-		ov5640_setting_1080P_1920_1080,
-		ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
-		OV5640_30_FPS
+		.id		= OV5640_MODE_1080P_1920_1080,
+		.dn_mode	= SCALING,
+		.pixel_rate	= OV5640_PIXEL_RATE_148M,
+		.analog_crop = {
+			.left	= 336,
+			.top	= 434,
+			.width	= 1952,
+			.height	= 1088,
+		},
+		.crop = {
+			.left	= 16,
+			.top	= 4,
+			.width	= 1920,
+			.height	= 1080,
+		},
+		.htot		= 2500,
+		.vblank_def	= 40,
+		.reg_data	= ov5640_setting_1080P_1920_1080,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
+		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 2592x1944 */
-		OV5640_MODE_QSXGA_2592_1944, SCALING,
-		OV5640_PIXEL_RATE_168M,
-		2592, 2844, 1944, 1968,
-		ov5640_setting_QSXGA_2592_1944,
-		ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
-		OV5640_15_FPS
+		.id		= OV5640_MODE_QSXGA_2592_1944,
+		.dn_mode	= SCALING,
+		.pixel_rate	= OV5640_PIXEL_RATE_168M,
+		.analog_crop = {
+			.left	= 0,
+			.top	= 0,
+			.width	= 2624,
+			.height	= 1952,
+		},
+		.crop = {
+			.left	= 16,
+			.top	= 4,
+			.width	= 2592,
+			.height	= 1944,
+		},
+		.htot		= 2844,
+		.vblank_def	= 24,
+		.reg_data	= ov5640_setting_QSXGA_2592_1944,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
+		.max_fps	= OV5640_15_FPS
 	},
 };
 
@@ -1152,9 +1270,10 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor)
 
 static u32 ov5640_calc_pixel_rate(struct ov5640_dev *sensor)
 {
+	const struct ov5640_mode_info *mode = sensor->current_mode;
 	u32 rate;
 
-	rate = sensor->current_mode->vtot * sensor->current_mode->htot;
+	rate = mode->htot * (mode->crop.height + mode->vblank_def);
 	rate *= ov5640_framerates[sensor->current_fr];
 
 	return rate;
@@ -1238,17 +1357,21 @@ static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
 	if (ret < 0)
 		return ret;
 
-	ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact);
+	ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE,
+				 mode->crop.width);
 	if (ret < 0)
 		return ret;
 
-	return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact);
+	return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE,
+				  mode->crop.height);
 }
 
 /* download ov5640 settings to sensor through i2c */
 static int ov5640_set_timings(struct ov5640_dev *sensor,
 			      const struct ov5640_mode_info *mode)
 {
+	const struct v4l2_rect *analog_crop = &mode->analog_crop;
+	const struct v4l2_rect *crop = &mode->crop;
 	int ret;
 
 	if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) {
@@ -1257,11 +1380,39 @@ static int ov5640_set_timings(struct ov5640_dev *sensor,
 			return ret;
 	}
 
-	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HS,
+				 analog_crop->left);
 	if (ret < 0)
 		return ret;
 
-	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact);
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VS,
+				 analog_crop->top);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HW,
+				 analog_crop->left + analog_crop->width - 1);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VH,
+				 analog_crop->top + analog_crop->height - 1);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HOFFS, crop->left);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VOFFS, crop->top);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, crop->width);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, crop->height);
 	if (ret < 0)
 		return ret;
 
@@ -1269,7 +1420,12 @@ static int ov5640_set_timings(struct ov5640_dev *sensor,
 	if (ret < 0)
 		return ret;
 
-	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot);
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
+				 crop->height + mode->vblank_def);
+	if (ret < 0)
+		return ret;
+
+	return 0;
 }
 
 static int ov5640_load_regs(struct ov5640_dev *sensor,
@@ -1697,11 +1853,11 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
 
 	mode = v4l2_find_nearest_size(ov5640_mode_data,
 				      ARRAY_SIZE(ov5640_mode_data),
-				      hact, vact,
-				      width, height);
+				      crop.width, crop.height, width, height);
 
 	if (!mode ||
-	    (!nearest && (mode->hact != width || mode->vact != height)))
+	    (!nearest &&
+	     (mode->crop.width != width || mode->crop.height != height)))
 		return NULL;
 
 	/* Check to see if the current mode exceeds the max frame rate */
@@ -2385,8 +2541,8 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
 	mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
 	if (!mode)
 		return -EINVAL;
-	fmt->width = mode->hact;
-	fmt->height = mode->vact;
+	fmt->width = mode->crop.width;
+	fmt->height = mode->crop.height;
 
 	if (new_mode)
 		*new_mode = mode;
@@ -3019,11 +3175,9 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
 	if (fse->index >= OV5640_NUM_MODES)
 		return -EINVAL;
 
-	fse->min_width =
-		ov5640_mode_data[fse->index].hact;
+	fse->min_width = ov5640_mode_data[fse->index].crop.width;
 	fse->max_width = fse->min_width;
-	fse->min_height =
-		ov5640_mode_data[fse->index].vact;
+	fse->min_height = ov5640_mode_data[fse->index].crop.height;
 	fse->max_height = fse->min_height;
 
 	return 0;
@@ -3087,15 +3241,16 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
 	mode = sensor->current_mode;
 
 	frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
-					       mode->hact, mode->vact);
+					       mode->crop.width,
+					       mode->crop.height);
 	if (frame_rate < 0) {
 		/* Always return a valid frame interval value */
 		fi->interval = sensor->frame_interval;
 		goto out;
 	}
 
-	mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
-				mode->vact, true);
+	mode = ov5640_find_mode(sensor, frame_rate, mode->crop.width,
+				mode->crop.height, true);
 	if (!mode) {
 		ret = -EINVAL;
 		goto out;
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 09/27] media: ov5640: Fix 720x480 in RGB888 mode
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (7 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 08/27] media: ov5640: Rework timings programming Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 10/27] media: ov5640: Split DVP and CSI-2 timings Jacopo Mondi
                   ` (20 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Adjust the left crop of 720x480 to enable capture in RGB888 format,
which is otherwise broken.

The 56 pixels alignment has been copied from the 720x576 mode.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index a3d15e5bcb7d..9e8fcdf4d072 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -749,7 +749,7 @@ ov5640_mode_data[OV5640_NUM_MODES] = {
 			.height	= 1944,
 		},
 		.crop = {
-			.left	= 16,
+			.left	= 56,
 			.top	= 60,
 			.width	= 720,
 			.height	= 480,
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 10/27] media: ov5640: Split DVP and CSI-2 timings
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (8 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 09/27] media: ov5640: Fix 720x480 in RGB888 mode Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 11/27] media: ov5640: Provide timings accessor Jacopo Mondi
                   ` (19 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Separate the timings for the DVP mode from the timings for the CSI-2
mode in the ov5640 modes definition.

The CSI-2 timings will deviate from the DVP ones as the clock tree is
calculated differently.

In CSI-2 mode change the analog crop rectangles as the OV5640 pixel
array is composed as:
- vertically: 16 dummy columns, 2592 valid ones and 16 dummy columns for
  a total of 2624 columns
- horizontally: 8 optical black lines, 6 dummy ones, 1944 valid and 6
  dummies for a total of 1964 lines

Adjust the analog crop rectangle in CSI-2 mode to:
- Skip the first 16 dummy columns
- Skip the first 14 black/dummy lines
- Pass the whole valid pixel array size to the ISP for all modes except
  1024x768, 720p and 1080p which are obtained by cropping the valid pixel
  array.

Tested in RGB565, UYVY and RGB888 modes in CSI-2 mode.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
---
 drivers/media/i2c/ov5640.c | 609 ++++++++++++++++++++++++++-----------
 1 file changed, 434 insertions(+), 175 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 9e8fcdf4d072..d781fe9a66ca 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -29,6 +29,13 @@
 #define OV5640_XCLK_MIN  6000000
 #define OV5640_XCLK_MAX 54000000
 
+#define OV5640_NATIVE_WIDTH		2624
+#define OV5640_NATIVE_HEIGHT		1964
+#define OV5640_PIXEL_ARRAY_TOP		14
+#define OV5640_PIXEL_ARRAY_LEFT		16
+#define OV5640_PIXEL_ARRAY_WIDTH	2592
+#define OV5640_PIXEL_ARRAY_HEIGHT	1944
+
 #define OV5640_DEFAULT_SLAVE_ID 0x3c
 
 #define OV5640_LINK_RATE_MAX		490000000U
@@ -281,24 +288,34 @@ struct reg_value {
 	u32 delay_ms;
 };
 
-struct ov5640_mode_info {
-	enum ov5640_mode_id id;
-	enum ov5640_downsize_mode dn_mode;
-	enum ov5640_pixel_rate_id pixel_rate;
+struct ov5640_timings {
 	/* Analog crop rectangle. */
 	struct v4l2_rect analog_crop;
 	/* Visibile crop: from analog crop top-left corner. */
 	struct v4l2_rect crop;
-	/* Total pixels per line: crop.width + fixed hblank. */
+	/* Total pixels per line: width + fixed hblank. */
 	u32 htot;
-	/* Default vertical blanking: frame height = crop.height + vblank. */
+	/* Default vertical blanking: frame height = height + vblank. */
 	u32 vblank_def;
-	const struct reg_value *reg_data;
-	u32 reg_data_size;
 	/* DVP only; ignored in MIPI mode. */
 	u32 max_fps;
 };
 
+struct ov5640_mode_info {
+	enum ov5640_mode_id id;
+	enum ov5640_downsize_mode dn_mode;
+	enum ov5640_pixel_rate_id pixel_rate;
+
+	unsigned int width;
+	unsigned int height;
+
+	struct ov5640_timings dvp_timings;
+	struct ov5640_timings csi2_timings;
+
+	const struct reg_value *reg_data;
+	u32 reg_data_size;
+};
+
 struct ov5640_ctrls {
 	struct v4l2_ctrl_handler handler;
 	struct v4l2_ctrl *pixel_rate;
@@ -628,247 +645,479 @@ static const struct ov5640_mode_info ov5640_mode_init_data = {
 		.id		= 0,
 		.dn_mode	= SUBSAMPLING,
 		.pixel_rate	= OV5640_PIXEL_RATE_96M,
-		.analog_crop = {
-			.left	= 0,
-			.top	= 4,
-			.width	= 2624,
-			.height	= 1944,
+		.width	= 640,
+		.height	= 480,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 4,
+				.width	= 2624,
+				.height	= 1944,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 6,
+				.width	= 640,
+				.height	= 480,
+			},
+			.htot		= 1896,
+			.vblank_def	= 504,
+			.max_fps	= OV5640_30_FPS
 		},
-		.crop = {
-			.left	= 16,
-			.top	= 6,
-			.width	= 640,
-			.height	= 480,
+		.csi2_timings = {
+			.analog_crop = {
+				.left	= OV5640_PIXEL_ARRAY_LEFT,
+				.top	= OV5640_PIXEL_ARRAY_TOP,
+				.width	= OV5640_PIXEL_ARRAY_WIDTH,
+				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
+			},
+			.crop = {
+				.left	= 2,
+				.top	= 4,
+				.width	= 640,
+				.height	= 480,
+			},
+			.htot		= 1896,
+			.vblank_def	= 504,
 		},
-		.htot		= 1896,
-		.vblank_def	= 504,
 		.reg_data	= ov5640_init_setting_30fps_VGA,
 		.reg_data_size	= ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
-		.max_fps	= OV5640_30_FPS
 };
 
-static const struct ov5640_mode_info
-ov5640_mode_data[OV5640_NUM_MODES] = {
+static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 	{
 		/* 160x120 */
 		.id		= OV5640_MODE_QQVGA_160_120,
 		.dn_mode	= SUBSAMPLING,
 		.pixel_rate	= OV5640_PIXEL_RATE_48M,
-		.analog_crop = {
-			.left	= 0,
-			.top	= 4,
-			.width	= 2624,
-			.height	= 1944,
+		.width		= 160,
+		.height		= 120,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 4,
+				.width	= 2624,
+				.height	= 1944,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 6,
+				.width	= 160,
+				.height	= 120,
+			},
+			.htot		= 1896,
+			.vblank_def	= 864,
+			.max_fps	= OV5640_30_FPS
 		},
-		.crop = {
-			.left	= 16,
-			.top	= 6,
-			.width	= 160,
-			.height	= 120,
+		.csi2_timings = {
+			/* Feed the full valid pixel array to the ISP. */
+			.analog_crop = {
+				.left	= OV5640_PIXEL_ARRAY_LEFT,
+				.top	= OV5640_PIXEL_ARRAY_TOP,
+				.width	= OV5640_PIXEL_ARRAY_WIDTH,
+				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
+			},
+			/* Maintain a minimum processing margin. */
+			.crop = {
+				.left	= 2,
+				.top	= 4,
+				.width	= 160,
+				.height	= 120,
+			},
+			.htot		= 1896,
+			.vblank_def	= 864,
 		},
-		.htot		= 1896,
-		.vblank_def	= 864,
 		.reg_data	= ov5640_setting_QQVGA_160_120,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QQVGA_160_120),
-		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 176x144 */
 		.id		= OV5640_MODE_QCIF_176_144,
 		.dn_mode	= SUBSAMPLING,
 		.pixel_rate	= OV5640_PIXEL_RATE_48M,
-		.analog_crop = {
-			.left	= 0,
-			.top	= 4,
-			.width	= 2624,
-			.height	= 1944,
+		.width		= 176,
+		.height		= 144,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 4,
+				.width	= 2624,
+				.height	= 1944,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 6,
+				.width	= 176,
+				.height	= 144,
+			},
+			.htot		= 1896,
+			.vblank_def	= 840,
+			.max_fps	= OV5640_30_FPS
 		},
-		.crop = {
-			.left	= 16,
-			.top	= 6,
-			.width	= 176,
-			.height	= 144,
+		.csi2_timings = {
+			/* Feed the full valid pixel array to the ISP. */
+			.analog_crop = {
+				.left	= OV5640_PIXEL_ARRAY_LEFT,
+				.top	= OV5640_PIXEL_ARRAY_TOP,
+				.width	= OV5640_PIXEL_ARRAY_WIDTH,
+				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
+			},
+			/* Maintain a minimum processing margin. */
+			.crop = {
+				.left	= 2,
+				.top	= 4,
+				.width	= 176,
+				.height	= 144,
+			},
+			.htot		= 1896,
+			.vblank_def	= 840,
 		},
-		.htot		= 1896,
-		.vblank_def	= 840,
 		.reg_data	= ov5640_setting_QCIF_176_144,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QCIF_176_144),
-		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 320x240 */
 		.id		= OV5640_MODE_QVGA_320_240,
 		.dn_mode	= SUBSAMPLING,
+		.width		= 320,
+		.height		= 240,
 		.pixel_rate	= OV5640_PIXEL_RATE_48M,
-		.analog_crop = {
-			.left	= 0,
-			.top	= 4,
-			.width	= 2624,
-			.height	= 1944,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 4,
+				.width	= 2624,
+				.height	= 1944,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 6,
+				.width	= 320,
+				.height	= 240,
+			},
+			.htot		= 1896,
+			.vblank_def	= 744,
+			.max_fps	= OV5640_30_FPS
 		},
-		.crop = {
-			.left	= 16,
-			.top	= 6,
-			.width	= 320,
-			.height	= 240,
+		.csi2_timings = {
+			/* Feed the full valid pixel array to the ISP. */
+			.analog_crop = {
+				.left	= OV5640_PIXEL_ARRAY_LEFT,
+				.top	= OV5640_PIXEL_ARRAY_TOP,
+				.width	= OV5640_PIXEL_ARRAY_WIDTH,
+				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
+			},
+			/* Maintain a minimum processing margin. */
+			.crop = {
+				.left	= 2,
+				.top	= 4,
+				.width	= 320,
+				.height	= 240,
+			},
+			.htot		= 1896,
+			.vblank_def	= 744,
 		},
-		.htot		= 1896,
-		.vblank_def	= 744,
 		.reg_data	= ov5640_setting_QVGA_320_240,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QVGA_320_240),
-		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 640x480 */
 		.id		= OV5640_MODE_VGA_640_480,
 		.dn_mode	= SUBSAMPLING,
 		.pixel_rate	= OV5640_PIXEL_RATE_48M,
-		.analog_crop = {
-			.left	= 0,
-			.top	= 4,
-			.width	= 2624,
-			.height	= 1944,
+		.width		= 640,
+		.height		= 480,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 4,
+				.width	= 2624,
+				.height	= 1944,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 6,
+				.width	= 640,
+				.height	= 480,
+			},
+			.htot		= 1896,
+			.vblank_def	= 600,
+			.max_fps	= OV5640_60_FPS
 		},
-		.crop = {
-			.left	= 16,
-			.top	= 6,
-			.width	= 640,
-			.height	= 480,
+		.csi2_timings = {
+			/* Feed the full valid pixel array to the ISP. */
+			.analog_crop = {
+				.left	= OV5640_PIXEL_ARRAY_LEFT,
+				.top	= OV5640_PIXEL_ARRAY_TOP,
+				.width	= OV5640_PIXEL_ARRAY_WIDTH,
+				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
+			},
+			/* Maintain a minimum processing margin. */
+			.crop = {
+				.left	= 2,
+				.top	= 4,
+				.width	= 640,
+				.height	= 480,
+			},
+			.htot		= 1896,
+			.vblank_def	= 600,
 		},
-		.htot		= 1896,
-		.vblank_def	= 600,
 		.reg_data	= ov5640_setting_VGA_640_480,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_VGA_640_480),
-		.max_fps	= OV5640_60_FPS
 	}, {
 		/* 720x480 */
 		.id		= OV5640_MODE_NTSC_720_480,
 		.dn_mode	= SUBSAMPLING,
+		.width		= 720,
+		.height		= 480,
 		.pixel_rate	= OV5640_PIXEL_RATE_96M,
-		.analog_crop = {
-			.left	= 0,
-			.top	= 4,
-			.width	= 2624,
-			.height	= 1944,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 4,
+				.width	= 2624,
+				.height	= 1944,
+			},
+			.crop = {
+				.left	= 56,
+				.top	= 60,
+				.width	= 720,
+				.height	= 480,
+			},
+			.htot		= 1896,
+			.vblank_def	= 504,
+			.max_fps	= OV5640_30_FPS
 		},
-		.crop = {
-			.left	= 56,
-			.top	= 60,
-			.width	= 720,
-			.height	= 480,
+		.csi2_timings = {
+			/* Feed the full valid pixel array to the ISP. */
+			.analog_crop = {
+				.left	= OV5640_PIXEL_ARRAY_LEFT,
+				.top	= OV5640_PIXEL_ARRAY_TOP,
+				.width	= OV5640_PIXEL_ARRAY_WIDTH,
+				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
+			},
+			.crop = {
+				.left	= 56,
+				.top	= 60,
+				.width	= 720,
+				.height	= 480,
+			},
+			.htot		= 1896,
+			.vblank_def	= 504,
 		},
-		.htot		= 1896,
-		.vblank_def	= 504,
 		.reg_data	= ov5640_setting_NTSC_720_480,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_NTSC_720_480),
-		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 720x576 */
 		.id		= OV5640_MODE_PAL_720_576,
 		.dn_mode	= SUBSAMPLING,
+		.width		= 720,
+		.height		= 576,
 		.pixel_rate	= OV5640_PIXEL_RATE_96M,
-		.analog_crop = {
-			.left	= 0,
-			.top	= 4,
-			.width	= 2624,
-			.height	= 1944,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 4,
+				.width	= 2624,
+				.height	= 1944,
+			},
+			.crop = {
+				.left	= 56,
+				.top	= 6,
+				.width	= 720,
+				.height	= 576,
+			},
+			.htot		= 1896,
+			.vblank_def	= 408,
+			.max_fps	= OV5640_30_FPS
 		},
-		.crop = {
-			.left	= 56,
-			.top	= 6,
-			.width	= 720,
-			.height	= 576,
+		.csi2_timings = {
+			/* Feed the full valid pixel array to the ISP. */
+			.analog_crop = {
+				.left	= OV5640_PIXEL_ARRAY_LEFT,
+				.top	= OV5640_PIXEL_ARRAY_TOP,
+				.width	= OV5640_PIXEL_ARRAY_WIDTH,
+				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
+			},
+			.crop = {
+				.left	= 56,
+				.top	= 6,
+				.width	= 720,
+				.height	= 576,
+			},
+			.htot		= 1896,
+			.vblank_def	= 408,
 		},
-		.htot		= 1896,
-		.vblank_def	= 408,
 		.reg_data	= ov5640_setting_PAL_720_576,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_PAL_720_576),
-		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 1024x768 */
 		.id		= OV5640_MODE_XGA_1024_768,
 		.dn_mode	= SUBSAMPLING,
 		.pixel_rate	= OV5640_PIXEL_RATE_96M,
-		.analog_crop = {
-			.left	= 0,
-			.top	= 4,
-			.width	= 2624,
-			.height	= 1944,
+		.width		= 1024,
+		.height		= 768,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 4,
+				.width	= 2624,
+				.height	= 1944,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 6,
+				.width	= 1024,
+				.height	= 768,
+			},
+			.htot		= 1896,
+			.vblank_def	= 312,
+			.max_fps	= OV5640_30_FPS
 		},
-		.crop = {
-			.left	= 16,
-			.top	= 6,
-			.width	= 1024,
-			.height	= 768,
+		.csi2_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 4,
+				.width	= OV5640_NATIVE_WIDTH,
+				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 6,
+				.width	= 1024,
+				.height	= 768,
+			},
+			.htot		= 1896,
+			.vblank_def	= 312,
 		},
-		.htot		= 1896,
-		.vblank_def	= 312,
 		.reg_data	= ov5640_setting_XGA_1024_768,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_XGA_1024_768),
-		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 1280x720 */
 		.id		= OV5640_MODE_720P_1280_720,
 		.dn_mode	= SUBSAMPLING,
 		.pixel_rate	= OV5640_PIXEL_RATE_124M,
-		.analog_crop = {
-			.left	= 0,
-			.top	= 250,
-			.width	= 2624,
-			.height	= 1456,
+		.width		= 1280,
+		.height		= 720,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 250,
+				.width	= 2624,
+				.height	= 1456,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 4,
+				.width	= 1280,
+				.height	= 720,
+			},
+			.htot		= 1892,
+			.vblank_def	= 20,
+			.max_fps	= OV5640_30_FPS
 		},
-		.crop = {
-			.left	= 16,
-			.top	= 4,
-			.width	= 1280,
-			.height	= 720,
+		.csi2_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 250,
+				.width	= 2624,
+				.height	= 1456,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 4,
+				.width	= 1280,
+				.height	= 720,
+			},
+			.htot		= 1892,
+			.vblank_def	= 20,
 		},
-		.htot		= 1892,
-		.vblank_def	= 20,
 		.reg_data	= ov5640_setting_720P_1280_720,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_720P_1280_720),
-		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 1920x1080 */
 		.id		= OV5640_MODE_1080P_1920_1080,
 		.dn_mode	= SCALING,
 		.pixel_rate	= OV5640_PIXEL_RATE_148M,
-		.analog_crop = {
-			.left	= 336,
-			.top	= 434,
-			.width	= 1952,
-			.height	= 1088,
+		.width		= 1920,
+		.height		= 1080,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 336,
+				.top	= 434,
+				.width	= 1952,
+				.height	= 1088,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 4,
+				.width	= 1920,
+				.height	= 1080,
+			},
+			.htot		= 2500,
+			.vblank_def	= 40,
+			.max_fps	= OV5640_30_FPS
 		},
-		.crop = {
-			.left	= 16,
-			.top	= 4,
-			.width	= 1920,
-			.height	= 1080,
+		.csi2_timings = {
+			/* Crop the full valid pixel array in the center. */
+			.analog_crop = {
+				.left	= 336,
+				.top	= 434,
+				.width	= 1952,
+				.height	= 1088,
+			},
+			/* Maintain a larger processing margins. */
+			.crop = {
+				.left	= 16,
+				.top	= 4,
+				.width	= 1920,
+				.height	= 1080,
+			},
+			.htot		= 2500,
+			.vblank_def	= 40,
 		},
-		.htot		= 2500,
-		.vblank_def	= 40,
 		.reg_data	= ov5640_setting_1080P_1920_1080,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
-		.max_fps	= OV5640_30_FPS
 	}, {
 		/* 2592x1944 */
 		.id		= OV5640_MODE_QSXGA_2592_1944,
 		.dn_mode	= SCALING,
 		.pixel_rate	= OV5640_PIXEL_RATE_168M,
-		.analog_crop = {
-			.left	= 0,
-			.top	= 0,
-			.width	= 2624,
-			.height	= 1952,
+		.width		= OV5640_PIXEL_ARRAY_WIDTH,
+		.height		= OV5640_PIXEL_ARRAY_HEIGHT,
+		.dvp_timings = {
+			.analog_crop = {
+				.left	= 0,
+				.top	= 0,
+				.width	= 2624,
+				.height	= 1952,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 4,
+				.width	= 2592,
+				.height	= 1944,
+			},
+			.htot		= 2844,
+			.vblank_def	= 24,
+			.max_fps	= OV5640_15_FPS
 		},
-		.crop = {
-			.left	= 16,
-			.top	= 4,
-			.width	= 2592,
-			.height	= 1944,
+		.csi2_timings = {
+			/* Give more processing margin to full resolution. */
+			.analog_crop = {
+				.left	= 0,
+				.top	= 0,
+				.width	= OV5640_NATIVE_WIDTH,
+				.height	= 1952,
+			},
+			.crop = {
+				.left	= 16,
+				.top	= 4,
+				.width	= 2592,
+				.height	= 1944,
+			},
+			.htot		= 2844,
+			.vblank_def	= 24,
 		},
-		.htot		= 2844,
-		.vblank_def	= 24,
 		.reg_data	= ov5640_setting_QSXGA_2592_1944,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944),
-		.max_fps	= OV5640_15_FPS
 	},
 };
 
@@ -1271,9 +1520,10 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor)
 static u32 ov5640_calc_pixel_rate(struct ov5640_dev *sensor)
 {
 	const struct ov5640_mode_info *mode = sensor->current_mode;
+	const struct ov5640_timings *timings = &mode->dvp_timings;
 	u32 rate;
 
-	rate = mode->htot * (mode->crop.height + mode->vblank_def);
+	rate = timings->htot * (timings->crop.height + timings->vblank_def);
 	rate *= ov5640_framerates[sensor->current_fr];
 
 	return rate;
@@ -1357,21 +1607,20 @@ static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
 	if (ret < 0)
 		return ret;
 
-	ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE,
-				 mode->crop.width);
+	ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->width);
 	if (ret < 0)
 		return ret;
 
-	return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE,
-				  mode->crop.height);
+	return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->height);
 }
 
 /* download ov5640 settings to sensor through i2c */
 static int ov5640_set_timings(struct ov5640_dev *sensor,
 			      const struct ov5640_mode_info *mode)
 {
-	const struct v4l2_rect *analog_crop = &mode->analog_crop;
-	const struct v4l2_rect *crop = &mode->crop;
+	const struct ov5640_timings *timings;
+	const struct v4l2_rect *analog_crop;
+	const struct v4l2_rect *crop;
 	int ret;
 
 	if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) {
@@ -1380,6 +1629,14 @@ static int ov5640_set_timings(struct ov5640_dev *sensor,
 			return ret;
 	}
 
+	if (ov5640_is_csi2(sensor))
+		timings = &mode->csi2_timings;
+	else
+		timings = &mode->dvp_timings;
+
+	analog_crop = &timings->analog_crop;
+	crop = &timings->crop;
+
 	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HS,
 				 analog_crop->left);
 	if (ret < 0)
@@ -1408,20 +1665,20 @@ static int ov5640_set_timings(struct ov5640_dev *sensor,
 	if (ret < 0)
 		return ret;
 
-	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, crop->width);
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->width);
 	if (ret < 0)
 		return ret;
 
-	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, crop->height);
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->height);
 	if (ret < 0)
 		return ret;
 
-	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot);
+	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, timings->htot);
 	if (ret < 0)
 		return ret;
 
 	ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
-				 crop->height + mode->vblank_def);
+				 mode->height + timings->vblank_def);
 	if (ret < 0)
 		return ret;
 
@@ -1849,19 +2106,21 @@ static const struct ov5640_mode_info *
 ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
 		 int width, int height, bool nearest)
 {
+	const struct ov5640_timings *timings;
 	const struct ov5640_mode_info *mode;
 
 	mode = v4l2_find_nearest_size(ov5640_mode_data,
 				      ARRAY_SIZE(ov5640_mode_data),
-				      crop.width, crop.height, width, height);
+				      width, height, width, height);
 
 	if (!mode ||
 	    (!nearest &&
-	     (mode->crop.width != width || mode->crop.height != height)))
+	     (mode->width != width || mode->height != height)))
 		return NULL;
 
 	/* Check to see if the current mode exceeds the max frame rate */
-	if (ov5640_framerates[fr] > ov5640_framerates[mode->max_fps])
+	timings = &mode->dvp_timings;
+	if (ov5640_framerates[fr] > ov5640_framerates[timings->max_fps])
 		return NULL;
 
 	return mode;
@@ -2541,8 +2800,8 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
 	mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
 	if (!mode)
 		return -EINVAL;
-	fmt->width = mode->crop.width;
-	fmt->height = mode->crop.height;
+	fmt->width = mode->width;
+	fmt->height = mode->height;
 
 	if (new_mode)
 		*new_mode = mode;
@@ -3175,9 +3434,9 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
 	if (fse->index >= OV5640_NUM_MODES)
 		return -EINVAL;
 
-	fse->min_width = ov5640_mode_data[fse->index].crop.width;
+	fse->min_width = ov5640_mode_data[fse->index].width;
 	fse->max_width = fse->min_width;
-	fse->min_height = ov5640_mode_data[fse->index].crop.height;
+	fse->min_height = ov5640_mode_data[fse->index].height;
 	fse->max_height = fse->min_height;
 
 	return 0;
@@ -3241,16 +3500,16 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
 	mode = sensor->current_mode;
 
 	frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
-					       mode->crop.width,
-					       mode->crop.height);
+					       mode->width,
+					       mode->height);
 	if (frame_rate < 0) {
 		/* Always return a valid frame interval value */
 		fi->interval = sensor->frame_interval;
 		goto out;
 	}
 
-	mode = ov5640_find_mode(sensor, frame_rate, mode->crop.width,
-				mode->crop.height, true);
+	mode = ov5640_find_mode(sensor, frame_rate, mode->width,
+				mode->height, true);
 	if (!mode) {
 		ret = -EINVAL;
 		goto out;
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 11/27] media: ov5640: Provide timings accessor
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (9 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 10/27] media: ov5640: Split DVP and CSI-2 timings Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 12/27] media: ov5640: Re-sort per-mode register tables Jacopo Mondi
                   ` (18 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Provide a function to shortcut access to the correct timings definition
to avoid repeating the same pattern when accessing the sensor timings.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index d781fe9a66ca..afad6fac5c40 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1121,6 +1121,16 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 	},
 };
 
+static const struct ov5640_timings *
+ov5640_timings(const struct ov5640_dev *sensor,
+	       const struct ov5640_mode_info *mode)
+{
+	if (ov5640_is_csi2(sensor))
+		return &mode->csi2_timings;
+
+	return &mode->dvp_timings;
+}
+
 static int ov5640_init_slave_id(struct ov5640_dev *sensor)
 {
 	struct i2c_client *client = sensor->i2c_client;
@@ -1629,11 +1639,7 @@ static int ov5640_set_timings(struct ov5640_dev *sensor,
 			return ret;
 	}
 
-	if (ov5640_is_csi2(sensor))
-		timings = &mode->csi2_timings;
-	else
-		timings = &mode->dvp_timings;
-
+	timings = ov5640_timings(sensor, mode);
 	analog_crop = &timings->analog_crop;
 	crop = &timings->crop;
 
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 12/27] media: ov5640: Re-sort per-mode register tables
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (10 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 11/27] media: ov5640: Provide timings accessor Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:42 ` [PATCH v5 13/27] media: ov5640: Remove duplicated mode settings Jacopo Mondi
                   ` (17 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

The per-mode register tables are not sorted by size. Fix it.

Cosmetic change only.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index afad6fac5c40..65321feb6762 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -482,7 +482,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
 	{0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
 };
 
-static const struct reg_value ov5640_setting_VGA_640_480[] = {
+static const struct reg_value ov5640_setting_QQVGA_160_120[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -493,11 +493,10 @@ static const struct reg_value ov5640_setting_VGA_640_480[] = {
 	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
 	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
 	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_XGA_1024_768[] = {
+static const struct reg_value ov5640_setting_QCIF_176_144[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -527,7 +526,7 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_QQVGA_160_120[] = {
+static const struct reg_value ov5640_setting_VGA_640_480[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -538,10 +537,11 @@ static const struct reg_value ov5640_setting_QQVGA_160_120[] = {
 	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
 	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
 	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_QCIF_176_144[] = {
+static const struct reg_value ov5640_setting_NTSC_720_480[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -556,7 +556,7 @@ static const struct reg_value ov5640_setting_QCIF_176_144[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_NTSC_720_480[] = {
+static const struct reg_value ov5640_setting_PAL_720_576[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -571,7 +571,7 @@ static const struct reg_value ov5640_setting_NTSC_720_480[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_PAL_720_576[] = {
+static const struct reg_value ov5640_setting_XGA_1024_768[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 13/27] media: ov5640: Remove duplicated mode settings
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (11 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 12/27] media: ov5640: Re-sort per-mode register tables Jacopo Mondi
@ 2022-02-24  9:42 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 14/27] media: ov5640: Remove ov5640_mode_init_data Jacopo Mondi
                   ` (16 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:42 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

The register tables for all resolutions smaller than 720p are now
identical.

Remove the duplicated ones and create a single ov5640_setting_low_res[]
register table.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 120 +++++--------------------------------
 1 file changed, 15 insertions(+), 105 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 65321feb6762..9f0816c97cc0 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -482,7 +482,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
 	{0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
 };
 
-static const struct reg_value ov5640_setting_QQVGA_160_120[] = {
+static const struct reg_value ov5640_setting_low_res[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -496,96 +496,6 @@ static const struct reg_value ov5640_setting_QQVGA_160_120[] = {
 	{0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_QCIF_176_144[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_QVGA_320_240[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_VGA_640_480[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_NTSC_720_480[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_PAL_720_576[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_XGA_1024_768[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
 static const struct reg_value ov5640_setting_720P_1280_720[] = {
 	{0x3c07, 0x07, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
@@ -727,8 +637,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 			.htot		= 1896,
 			.vblank_def	= 864,
 		},
-		.reg_data	= ov5640_setting_QQVGA_160_120,
-		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QQVGA_160_120),
+		.reg_data	= ov5640_setting_low_res,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
 	}, {
 		/* 176x144 */
 		.id		= OV5640_MODE_QCIF_176_144,
@@ -771,8 +681,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 			.htot		= 1896,
 			.vblank_def	= 840,
 		},
-		.reg_data	= ov5640_setting_QCIF_176_144,
-		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QCIF_176_144),
+		.reg_data	= ov5640_setting_low_res,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
 	}, {
 		/* 320x240 */
 		.id		= OV5640_MODE_QVGA_320_240,
@@ -815,8 +725,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 			.htot		= 1896,
 			.vblank_def	= 744,
 		},
-		.reg_data	= ov5640_setting_QVGA_320_240,
-		.reg_data_size	= ARRAY_SIZE(ov5640_setting_QVGA_320_240),
+		.reg_data	= ov5640_setting_low_res,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
 	}, {
 		/* 640x480 */
 		.id		= OV5640_MODE_VGA_640_480,
@@ -859,8 +769,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 			.htot		= 1896,
 			.vblank_def	= 600,
 		},
-		.reg_data	= ov5640_setting_VGA_640_480,
-		.reg_data_size	= ARRAY_SIZE(ov5640_setting_VGA_640_480),
+		.reg_data	= ov5640_setting_low_res,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
 	}, {
 		/* 720x480 */
 		.id		= OV5640_MODE_NTSC_720_480,
@@ -902,8 +812,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 			.htot		= 1896,
 			.vblank_def	= 504,
 		},
-		.reg_data	= ov5640_setting_NTSC_720_480,
-		.reg_data_size	= ARRAY_SIZE(ov5640_setting_NTSC_720_480),
+		.reg_data	= ov5640_setting_low_res,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
 	}, {
 		/* 720x576 */
 		.id		= OV5640_MODE_PAL_720_576,
@@ -945,8 +855,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 			.htot		= 1896,
 			.vblank_def	= 408,
 		},
-		.reg_data	= ov5640_setting_PAL_720_576,
-		.reg_data_size	= ARRAY_SIZE(ov5640_setting_PAL_720_576),
+		.reg_data	= ov5640_setting_low_res,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
 	}, {
 		/* 1024x768 */
 		.id		= OV5640_MODE_XGA_1024_768,
@@ -987,8 +897,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 			.htot		= 1896,
 			.vblank_def	= 312,
 		},
-		.reg_data	= ov5640_setting_XGA_1024_768,
-		.reg_data_size	= ARRAY_SIZE(ov5640_setting_XGA_1024_768),
+		.reg_data	= ov5640_setting_low_res,
+		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
 	}, {
 		/* 1280x720 */
 		.id		= OV5640_MODE_720P_1280_720,
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 14/27] media: ov5640: Remove ov5640_mode_init_data
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (12 preceding siblings ...)
  2022-02-24  9:42 ` [PATCH v5 13/27] media: ov5640: Remove duplicated mode settings Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 15/27] media: ov5640: Add HBLANK control Jacopo Mondi
                   ` (15 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

The ov5640_mode_init_data is a fictional sensor more which is used to
program the initial sensor settings.

It is only used to initialize the sensor and can be replaced
it with a throw-away mode which just wraps the register table.

Also rename the register table to drop the format from the name to make
it clear an actual sensor mode has to be applied after the initial
programming.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 67 ++++++--------------------------------
 1 file changed, 10 insertions(+), 57 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 9f0816c97cc0..d395ee9cee9b 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -402,7 +402,7 @@ static inline bool ov5640_is_csi2(const struct ov5640_dev *sensor)
  * over i2c.
  */
 /* YUV422 UYVY VGA@30fps */
-static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
+static const struct reg_value ov5640_init_setting[] = {
 	{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
 	{0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0},
 	{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
@@ -550,50 +550,6 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
 };
 
-/* power-on sensor init reg table */
-static const struct ov5640_mode_info ov5640_mode_init_data = {
-		.id		= 0,
-		.dn_mode	= SUBSAMPLING,
-		.pixel_rate	= OV5640_PIXEL_RATE_96M,
-		.width	= 640,
-		.height	= 480,
-		.dvp_timings = {
-			.analog_crop = {
-				.left	= 0,
-				.top	= 4,
-				.width	= 2624,
-				.height	= 1944,
-			},
-			.crop = {
-				.left	= 16,
-				.top	= 6,
-				.width	= 640,
-				.height	= 480,
-			},
-			.htot		= 1896,
-			.vblank_def	= 504,
-			.max_fps	= OV5640_30_FPS
-		},
-		.csi2_timings = {
-			.analog_crop = {
-				.left	= OV5640_PIXEL_ARRAY_LEFT,
-				.top	= OV5640_PIXEL_ARRAY_TOP,
-				.width	= OV5640_PIXEL_ARRAY_WIDTH,
-				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
-			},
-			.crop = {
-				.left	= 2,
-				.top	= 4,
-				.width	= 640,
-				.height	= 480,
-			},
-			.htot		= 1896,
-			.vblank_def	= 504,
-		},
-		.reg_data	= ov5640_init_setting_30fps_VGA,
-		.reg_data_size	= ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
-};
-
 static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 	{
 		/* 160x120 */
@@ -1601,17 +1557,16 @@ static int ov5640_set_timings(struct ov5640_dev *sensor,
 	return 0;
 }
 
-static int ov5640_load_regs(struct ov5640_dev *sensor,
-			    const struct ov5640_mode_info *mode)
+static void ov5640_load_regs(struct ov5640_dev *sensor,
+			     const struct reg_value *regs, unsigned int regnum)
 {
-	const struct reg_value *regs = mode->reg_data;
 	unsigned int i;
 	u32 delay_ms;
 	u16 reg_addr;
 	u8 mask, val;
 	int ret = 0;
 
-	for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
+	for (i = 0; i < regnum; ++i, ++regs) {
 		delay_ms = regs->delay_ms;
 		reg_addr = regs->reg_addr;
 		val = regs->val;
@@ -1633,8 +1588,6 @@ static int ov5640_load_regs(struct ov5640_dev *sensor,
 		if (delay_ms)
 			usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
 	}
-
-	return ov5640_set_timings(sensor, mode);
 }
 
 static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on)
@@ -2089,7 +2042,8 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor,
 		return ret;
 
 	/* Write capture setting */
-	ret = ov5640_load_regs(sensor, mode);
+	ov5640_load_regs(sensor, mode->reg_data, mode->reg_data_size);
+	ret = ov5640_set_timings(sensor, mode);
 	if (ret < 0)
 		return ret;
 
@@ -2213,7 +2167,8 @@ static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
 		return -EINVAL;
 
 	/* Write capture setting */
-	return ov5640_load_regs(sensor, mode);
+	ov5640_load_regs(sensor, mode->reg_data, mode->reg_data_size);
+	return ov5640_set_timings(sensor, mode);
 }
 
 static int ov5640_set_mode(struct ov5640_dev *sensor)
@@ -2311,10 +2266,8 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor)
 	int ret;
 
 	/* first load the initial register values */
-	ret = ov5640_load_regs(sensor, &ov5640_mode_init_data);
-	if (ret < 0)
-		return ret;
-	sensor->last_mode = &ov5640_mode_init_data;
+	ov5640_load_regs(sensor, ov5640_init_setting,
+			 ARRAY_SIZE(ov5640_init_setting));
 
 	ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
 			     (ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) |
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 15/27] media: ov5640: Add HBLANK control
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (13 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 14/27] media: ov5640: Remove ov5640_mode_init_data Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 16/27] media: ov5640: Add VBLANK control Jacopo Mondi
                   ` (14 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Add the HBLANK control as read-only.

The hblank value is fixed in the mode definition and is updated
everytime a new format is applied.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index d395ee9cee9b..5419c7236348 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -320,6 +320,7 @@ struct ov5640_ctrls {
 	struct v4l2_ctrl_handler handler;
 	struct v4l2_ctrl *pixel_rate;
 	struct v4l2_ctrl *link_freq;
+	struct v4l2_ctrl *hblank;
 	struct {
 		struct v4l2_ctrl *auto_exp;
 		struct v4l2_ctrl *exposure;
@@ -2695,6 +2696,8 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
 	const struct ov5640_mode_info *mode = sensor->current_mode;
 	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
 	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
+	const struct ov5640_timings *timings;
+	unsigned int hblank;
 	unsigned int i = 0;
 	u32 pixel_rate;
 	s64 link_freq;
@@ -2747,6 +2750,11 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
 	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, pixel_rate);
 	__v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, i);
 
+	timings = ov5640_timings(sensor, mode);
+	hblank = timings->htot - mode->width;
+	__v4l2_ctrl_modify_range(sensor->ctrls.hblank,
+				 hblank, hblank, 1, hblank);
+
 	return 0;
 }
 
@@ -3211,6 +3219,8 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
 	const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
 	struct ov5640_ctrls *ctrls = &sensor->ctrls;
 	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+	const struct ov5640_timings *timings;
+	unsigned int hblank;
 	int ret;
 
 	v4l2_ctrl_handler_init(hdl, 32);
@@ -3230,6 +3240,11 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
 					OV5640_DEFAULT_LINK_FREQ,
 					ov5640_csi2_link_freqs);
 
+	timings = ov5640_timings(sensor, mode);
+	hblank = timings->htot - mode->width;
+	ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank,
+					  hblank, 1, hblank);
+
 	/* Auto/manual white balance */
 	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
 					   V4L2_CID_AUTO_WHITE_BALANCE,
@@ -3279,6 +3294,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
 
 	ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 	ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 	ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
 	ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
 
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 16/27] media: ov5640: Add VBLANK control
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (14 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 15/27] media: ov5640: Add HBLANK control Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-04-07 16:25   ` Hugues FRUCHET - FOSS
  2022-02-24  9:43 ` [PATCH v5 17/27] media: ov5640: Change CSI-2 timings to comply with FPS Jacopo Mondi
                   ` (13 subsequent siblings)
  29 siblings, 1 reply; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Add the VBLANK control which allows to select the duration of the
frame vertical blankings and allows to control the framerate.

The VBLANK control also modifies the exposure time range, which cannot
exceed the maximum frame length.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 52 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 5419c7236348..665a8bcebf09 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -36,6 +36,10 @@
 #define OV5640_PIXEL_ARRAY_WIDTH	2592
 #define OV5640_PIXEL_ARRAY_HEIGHT	1944
 
+/* FIXME: not documented. */
+#define OV5640_MIN_VBLANK	24
+#define OV5640_MAX_VTS		1968
+
 #define OV5640_DEFAULT_SLAVE_ID 0x3c
 
 #define OV5640_LINK_RATE_MAX		490000000U
@@ -321,6 +325,7 @@ struct ov5640_ctrls {
 	struct v4l2_ctrl *pixel_rate;
 	struct v4l2_ctrl *link_freq;
 	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *vblank;
 	struct {
 		struct v4l2_ctrl *auto_exp;
 		struct v4l2_ctrl *exposure;
@@ -2697,6 +2702,7 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
 	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
 	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
 	const struct ov5640_timings *timings;
+	s32 exposure_val, exposure_max;
 	unsigned int hblank;
 	unsigned int i = 0;
 	u32 pixel_rate;
@@ -2755,6 +2761,19 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
 	__v4l2_ctrl_modify_range(sensor->ctrls.hblank,
 				 hblank, hblank, 1, hblank);
 
+	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
+				 OV5640_MAX_VTS - mode->height, 1,
+				 timings->vblank_def);
+	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
+
+	exposure_max = timings->crop.height + timings->vblank_def - 4;
+	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
+			       sensor->ctrls.exposure->minimum,
+			       exposure_max);
+	__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
+				 sensor->ctrls.exposure->minimum,
+				 exposure_max, 1, exposure_val);
+
 	return 0;
 }
 
@@ -3127,6 +3146,15 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
 			      (BIT(2) | BIT(1)) : 0);
 }
 
+static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value)
+{
+	const struct ov5640_mode_info *mode = sensor->current_mode;
+
+	/* Update the VTOT timing register value. */
+	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
+				  mode->height + value);
+}
+
 static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
@@ -3157,10 +3185,25 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
 	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	const struct ov5640_mode_info *mode = sensor->current_mode;
+	const struct ov5640_timings *timings;
+	unsigned int exp_max;
 	int ret;
 
 	/* v4l2_ctrl_lock() locks our own mutex */
 
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update the exposure range to the newly programmed vblank. */
+		timings = ov5640_timings(sensor, mode);
+		exp_max = mode->height + ctrl->val - 4;
+		__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
+					 sensor->ctrls.exposure->minimum,
+					 exp_max, sensor->ctrls.exposure->step,
+					 timings->vblank_def);
+		break;
+	}
+
 	/*
 	 * If the device is not powered up by the host driver do
 	 * not apply any controls to H/W at this time. Instead
@@ -3200,6 +3243,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
 	case V4L2_CID_VFLIP:
 		ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
 		break;
+	case V4L2_CID_VBLANK:
+		ret = ov5640_set_ctrl_vblank(sensor, ctrl->val);
+		break;
 	default:
 		ret = -EINVAL;
 		break;
@@ -3220,6 +3266,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
 	struct ov5640_ctrls *ctrls = &sensor->ctrls;
 	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
 	const struct ov5640_timings *timings;
+	unsigned int max_vblank;
 	unsigned int hblank;
 	int ret;
 
@@ -3245,6 +3292,11 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
 	ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank,
 					  hblank, 1, hblank);
 
+	max_vblank = OV5640_MAX_VTS - mode->height;
+	ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+					  OV5640_MIN_VBLANK, max_vblank,
+					  1, timings->vblank_def);
+
 	/* Auto/manual white balance */
 	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
 					   V4L2_CID_AUTO_WHITE_BALANCE,
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 17/27] media: ov5640: Change CSI-2 timings to comply with FPS
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (15 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 16/27] media: ov5640: Add VBLANK control Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 18/27] media: ov5640: Implement init_cfg Jacopo Mondi
                   ` (12 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Now that the frame duration can be controlled by tuning the VBLANK
duration in CSI-2 mode, fix all modes definitions to comply with the
reported FPS.

All modes run at 30 FPS except for full-resolution mode 2592x1944
which runs at 15FPS.

Tested on a 2 data lanes setup in UYVY and RGB565 modes in CSI-2 mode.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 665a8bcebf09..6f10514e3c11 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -596,8 +596,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 				.width	= 160,
 				.height	= 120,
 			},
-			.htot		= 1896,
-			.vblank_def	= 864,
+			.htot		= 1600,
+			.vblank_def	= 878,
 		},
 		.reg_data	= ov5640_setting_low_res,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
@@ -640,8 +640,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 				.width	= 176,
 				.height	= 144,
 			},
-			.htot		= 1896,
-			.vblank_def	= 840,
+			.htot		= 1600,
+			.vblank_def	= 854,
 		},
 		.reg_data	= ov5640_setting_low_res,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
@@ -684,8 +684,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 				.width	= 320,
 				.height	= 240,
 			},
-			.htot		= 1896,
-			.vblank_def	= 744,
+			.htot		= 1600,
+			.vblank_def	= 760,
 		},
 		.reg_data	= ov5640_setting_low_res,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
@@ -728,8 +728,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 				.width	= 640,
 				.height	= 480,
 			},
-			.htot		= 1896,
-			.vblank_def	= 600,
+			.htot		= 1600,
+			.vblank_def	= 520,
 		},
 		.reg_data	= ov5640_setting_low_res,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
@@ -772,7 +772,7 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 				.height	= 480,
 			},
 			.htot		= 1896,
-			.vblank_def	= 504,
+			.vblank_def	= 1206,
 		},
 		.reg_data	= ov5640_setting_low_res,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
@@ -815,7 +815,7 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 				.height	= 576,
 			},
 			.htot		= 1896,
-			.vblank_def	= 408,
+			.vblank_def	= 1110,
 		},
 		.reg_data	= ov5640_setting_low_res,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
@@ -857,7 +857,7 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 				.height	= 768,
 			},
 			.htot		= 1896,
-			.vblank_def	= 312,
+			.vblank_def	= 918,
 		},
 		.reg_data	= ov5640_setting_low_res,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
@@ -898,8 +898,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 				.width	= 1280,
 				.height	= 720,
 			},
-			.htot		= 1892,
-			.vblank_def	= 20,
+			.htot		= 1600,
+			.vblank_def	= 560,
 		},
 		.reg_data	= ov5640_setting_720P_1280_720,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_720P_1280_720),
@@ -942,8 +942,8 @@ static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = {
 				.width	= 1920,
 				.height	= 1080,
 			},
-			.htot		= 2500,
-			.vblank_def	= 40,
+			.htot		= 2234,
+			.vblank_def	= 24,
 		},
 		.reg_data	= ov5640_setting_1080P_1920_1080,
 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 18/27] media: ov5640: Implement init_cfg
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (16 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 17/27] media: ov5640: Change CSI-2 timings to comply with FPS Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 19/27] media: ov5640: Implement get_selection Jacopo Mondi
                   ` (11 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Implement the init_cfg pad operation to initialize the subdev state
format to the default one.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 6f10514e3c11..c95c1f90353d 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -408,6 +408,18 @@ static inline bool ov5640_is_csi2(const struct ov5640_dev *sensor)
  * over i2c.
  */
 /* YUV422 UYVY VGA@30fps */
+
+static const struct v4l2_mbus_framefmt ov5640_default_fmt = {
+	.code = MEDIA_BUS_FMT_UYVY8_2X8,
+	.width = 640,
+	.height = 480,
+	.colorspace = V4L2_COLORSPACE_SRGB,
+	.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB),
+	.quantization = V4L2_QUANTIZATION_FULL_RANGE,
+	.xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB),
+	.field = V4L2_FIELD_NONE,
+};
+
 static const struct reg_value ov5640_init_setting[] = {
 	{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
 	{0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0},
@@ -3514,6 +3526,17 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
 	return ret;
 }
 
+static int ov5640_init_cfg(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_state *state)
+{
+	struct v4l2_mbus_framefmt *fmt =
+				v4l2_subdev_get_try_format(sd, state, 0);
+
+	*fmt = ov5640_default_fmt;
+
+	return 0;
+}
+
 static const struct v4l2_subdev_core_ops ov5640_core_ops = {
 	.s_power = ov5640_s_power,
 	.log_status = v4l2_ctrl_subdev_log_status,
@@ -3528,6 +3551,7 @@ static const struct v4l2_subdev_video_ops ov5640_video_ops = {
 };
 
 static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
+	.init_cfg = ov5640_init_cfg,
 	.enum_mbus_code = ov5640_enum_mbus_code,
 	.get_fmt = ov5640_get_fmt,
 	.set_fmt = ov5640_set_fmt,
@@ -3586,7 +3610,6 @@ static int ov5640_probe(struct i2c_client *client)
 	struct device *dev = &client->dev;
 	struct fwnode_handle *endpoint;
 	struct ov5640_dev *sensor;
-	struct v4l2_mbus_framefmt *fmt;
 	u32 rotation;
 	int ret;
 
@@ -3600,15 +3623,7 @@ static int ov5640_probe(struct i2c_client *client)
 	 * default init sequence initialize sensor to
 	 * YUV422 UYVY VGA@30fps
 	 */
-	fmt = &sensor->fmt;
-	fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
-	fmt->colorspace = V4L2_COLORSPACE_SRGB;
-	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
-	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
-	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
-	fmt->width = 640;
-	fmt->height = 480;
-	fmt->field = V4L2_FIELD_NONE;
+	sensor->fmt = ov5640_default_fmt;
 	sensor->frame_interval.numerator = 1;
 	sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
 	sensor->current_fr = OV5640_30_FPS;
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 19/27] media: ov5640: Implement get_selection
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (17 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 18/27] media: ov5640: Implement init_cfg Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 20/27] media: ov5640: Limit frame_interval to DVP mode only Jacopo Mondi
                   ` (10 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Implement the get_selection pad operation for the OV5640 sensor driver.

The supported targets report the sensor's native size, the active pixel
array size and the analog crop rectangle from which the image is
produced.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 46 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index c95c1f90353d..baf368a39e0f 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2835,6 +2835,45 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
 	return ret;
 }
 
+static int ov5640_get_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_state *sd_state,
+				struct v4l2_subdev_selection *sel)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	const struct ov5640_mode_info *mode = sensor->current_mode;
+	const struct ov5640_timings *timings;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP: {
+		mutex_lock(&sensor->lock);
+		timings = ov5640_timings(sensor, mode);
+		sel->r = timings->analog_crop;
+		mutex_unlock(&sensor->lock);
+
+		return 0;
+	}
+
+	case V4L2_SEL_TGT_NATIVE_SIZE:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.top = 0;
+		sel->r.left = 0;
+		sel->r.width = OV5640_NATIVE_WIDTH;
+		sel->r.height = OV5640_NATIVE_HEIGHT;
+
+		return 0;
+
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.top = OV5640_PIXEL_ARRAY_TOP;
+		sel->r.left = OV5640_PIXEL_ARRAY_LEFT;
+		sel->r.width = OV5640_PIXEL_ARRAY_WIDTH;
+		sel->r.height = OV5640_PIXEL_ARRAY_HEIGHT;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
 static int ov5640_set_framefmt(struct ov5640_dev *sensor,
 			       struct v4l2_mbus_framefmt *format)
 {
@@ -3531,9 +3570,15 @@ static int ov5640_init_cfg(struct v4l2_subdev *sd,
 {
 	struct v4l2_mbus_framefmt *fmt =
 				v4l2_subdev_get_try_format(sd, state, 0);
+	struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0);
 
 	*fmt = ov5640_default_fmt;
 
+	crop->left = OV5640_PIXEL_ARRAY_LEFT;
+	crop->top = OV5640_PIXEL_ARRAY_TOP;
+	crop->width = OV5640_PIXEL_ARRAY_WIDTH;
+	crop->height = OV5640_PIXEL_ARRAY_HEIGHT;
+
 	return 0;
 }
 
@@ -3555,6 +3600,7 @@ static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
 	.enum_mbus_code = ov5640_enum_mbus_code,
 	.get_fmt = ov5640_get_fmt,
 	.set_fmt = ov5640_set_fmt,
+	.get_selection = ov5640_get_selection,
 	.enum_frame_size = ov5640_enum_frame_size,
 	.enum_frame_interval = ov5640_enum_frame_interval,
 };
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 20/27] media: ov5640: Limit frame_interval to DVP mode only
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (18 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 19/27] media: ov5640: Implement get_selection Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-04-07 16:25   ` Hugues FRUCHET - FOSS
  2022-02-24  9:43 ` [PATCH v5 21/27] media: ov5640: Register device properties Jacopo Mondi
                   ` (9 subsequent siblings)
  29 siblings, 1 reply; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

In MIPI mode the frame rate control is performed by adjusting the
frame blankings and the s_frame_interval function is not used anymore.

Only check for the per-mode supported frame rate in DVP mode and do not
restrict MIPI mode.

Disallow enum/s/g_frame_interval if the chip is used in MIPI mode.

While at it re-indent one function which whose parameters were wrongly
aligned.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index baf368a39e0f..6b955163eb4d 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2005,9 +2005,14 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
 	     (mode->width != width || mode->height != height)))
 		return NULL;
 
-	/* Check to see if the current mode exceeds the max frame rate */
+	/*
+	 * Check to see if the current mode exceeds the max frame rate.
+	 * Only DVP mode uses the frame rate set by s_frame_interval, MIPI
+	 * mode controls framerate by setting blankings.
+	 */
 	timings = &mode->dvp_timings;
-	if (ov5640_framerates[fr] > ov5640_framerates[timings->max_fps])
+	if (!ov5640_is_csi2(sensor) &&
+	    ov5640_framerates[fr] > ov5640_framerates[timings->max_fps])
 		return NULL;
 
 	return mode;
@@ -3439,6 +3444,9 @@ static int ov5640_enum_frame_interval(
 	struct v4l2_fract tpf;
 	int ret;
 
+	if (ov5640_is_csi2(sensor))
+		return -EINVAL;
+
 	if (fie->pad != 0)
 		return -EINVAL;
 	if (fie->index >= OV5640_NUM_FRAMERATES)
@@ -3461,6 +3469,9 @@ static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
 {
 	struct ov5640_dev *sensor = to_ov5640_dev(sd);
 
+	if (ov5640_is_csi2(sensor))
+		return -EINVAL;
+
 	mutex_lock(&sensor->lock);
 	fi->interval = sensor->frame_interval;
 	mutex_unlock(&sensor->lock);
@@ -3475,6 +3486,9 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
 	const struct ov5640_mode_info *mode;
 	int frame_rate, ret = 0;
 
+	if (ov5640_is_csi2(sensor))
+		return -EINVAL;
+
 	if (fi->pad != 0)
 		return -EINVAL;
 
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 21/27] media: ov5640: Register device properties
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (19 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 20/27] media: ov5640: Limit frame_interval to DVP mode only Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 22/27] media: ov5640: Add RGB565_1X16 format Jacopo Mondi
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Parse the device properties and register the rotation and orientation
V4L2 controls using v4l2_ctrl_new_fwnode_properties().

Remove the open-coded parsing of the rotation property and assume the
DTS is correct is providing either <0> or <180> as possible rotations.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 29 ++++++++++++-----------------
 1 file changed, 12 insertions(+), 17 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 6b955163eb4d..840ff2e05678 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -3321,6 +3321,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
 	const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
 	struct ov5640_ctrls *ctrls = &sensor->ctrls;
 	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+	struct v4l2_fwnode_device_properties props;
 	const struct ov5640_timings *timings;
 	unsigned int max_vblank;
 	unsigned int hblank;
@@ -3400,6 +3401,17 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
 		goto free_ctrls;
 	}
 
+	ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &props);
+	if (ret)
+		goto free_ctrls;
+
+	if (props.rotation == 180)
+		sensor->upside_down = true;
+
+	ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
+	if (ret)
+		goto free_ctrls;
+
 	ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 	ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 	ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
@@ -3670,7 +3682,6 @@ static int ov5640_probe(struct i2c_client *client)
 	struct device *dev = &client->dev;
 	struct fwnode_handle *endpoint;
 	struct ov5640_dev *sensor;
-	u32 rotation;
 	int ret;
 
 	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
@@ -3693,22 +3704,6 @@ static int ov5640_probe(struct i2c_client *client)
 
 	sensor->ae_target = 52;
 
-	/* optional indication of physical rotation of sensor */
-	ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
-				       &rotation);
-	if (!ret) {
-		switch (rotation) {
-		case 180:
-			sensor->upside_down = true;
-			fallthrough;
-		case 0:
-			break;
-		default:
-			dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n",
-				 rotation);
-		}
-	}
-
 	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
 						  NULL);
 	if (!endpoint) {
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 22/27] media: ov5640: Add RGB565_1X16 format
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (20 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 21/27] media: ov5640: Register device properties Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 23/27] media: ov5640: Add BGR888 format Jacopo Mondi
                   ` (7 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

The driver already supports the 2X8_[LE|BE] variants of RGB565
formats.

As for CSI-2 the 2X8 variants do not apply, add RGB565_1X16 variant
with little-endian ordering of components as required by the CSI-2
specifications.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 840ff2e05678..8dff41a1966e 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -221,6 +221,10 @@ static const struct ov5640_pixfmt {
 		.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.bpp = 16,
+	}, {
+		.code = MEDIA_BUS_FMT_RGB565_1X16,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 16,
 	}, {
 		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
 		.colorspace = V4L2_COLORSPACE_SRGB,
@@ -2900,6 +2904,7 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor,
 		mux = OV5640_FMT_MUX_YUV422;
 		break;
 	case MEDIA_BUS_FMT_RGB565_2X8_LE:
+	case MEDIA_BUS_FMT_RGB565_1X16:
 		/* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
 		fmt = 0x6F;
 		mux = OV5640_FMT_MUX_RGB;
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 23/27] media: ov5640: Add BGR888 format
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (21 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 22/27] media: ov5640: Add RGB565_1X16 format Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 24/27] media: ov5640: Restrict sizes to mbus code Jacopo Mondi
                   ` (6 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Add support for BGR888 image format.

No existing media bus codes describe exactly the way data is transferred
on the CSI-2 bus. This is not a new issue, the CSI-2 YUV422 8-bit format
is described by MEDIA_BUS_FMT_UYVY8_1X16 which is an arbitrary
convention and not an exact match. Use the MEDIA_BUS_FMT_BGR888_1X24 to
follow the same convention, based on the order in which bits are
transmitted over the CSI-2 bus when producing images in RGB24 format.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 8dff41a1966e..5e87c5e5a75c 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -225,6 +225,10 @@ static const struct ov5640_pixfmt {
 		.code = MEDIA_BUS_FMT_RGB565_1X16,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.bpp = 16,
+	}, {
+		.code = MEDIA_BUS_FMT_BGR888_1X24,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 24,
 	}, {
 		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
 		.colorspace = V4L2_COLORSPACE_SRGB,
@@ -2914,6 +2918,11 @@ static int ov5640_set_framefmt(struct ov5640_dev *sensor,
 		fmt = 0x61;
 		mux = OV5640_FMT_MUX_RGB;
 		break;
+	case MEDIA_BUS_FMT_BGR888_1X24:
+		/* BGR888: RGB */
+		fmt = 0x23;
+		mux = OV5640_FMT_MUX_RGB;
+		break;
 	case MEDIA_BUS_FMT_JPEG_1X8:
 		/* YUV422, YUYV */
 		fmt = 0x30;
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 24/27] media: ov5640: Restrict sizes to mbus code
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (22 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 23/27] media: ov5640: Add BGR888 format Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 25/27] media: ov5640: Adjust format to bpp in s_fmt Jacopo Mondi
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

The ov5640 driver supports different sizes for different mbus_codes.
In particular:

- 8bpp modes: high resolution sizes (>= 1280x720)
- 16bpp modes: all sizes
- 24bpp modes: low resolutions sizes (< 1280x720)

Restrict the frame sizes enumerations to the above constraints.

While at it, make sure the fse->mbus_code parameter is valid, and return
-EINVAL if it's not.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 5e87c5e5a75c..1510b9e8322d 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -3448,14 +3448,28 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
 				  struct v4l2_subdev_state *sd_state,
 				  struct v4l2_subdev_frame_size_enum *fse)
 {
+	u32 bpp = ov5640_code_to_bpp(fse->code);
+	unsigned int index = fse->index;
+
 	if (fse->pad != 0)
 		return -EINVAL;
-	if (fse->index >= OV5640_NUM_MODES)
+	if (!bpp)
+		return -EINVAL;
+
+	/* Only low-resolution modes are supported for 24bpp formats. */
+	if (bpp == 24 && index >= OV5640_MODE_720P_1280_720)
+		return -EINVAL;
+
+	/* FIXME: Low resolution modes don't work in 8bpp formats. */
+	if (bpp == 8)
+		index += OV5640_MODE_720P_1280_720;
+
+	if (index >= OV5640_NUM_MODES)
 		return -EINVAL;
 
-	fse->min_width = ov5640_mode_data[fse->index].width;
+	fse->min_width = ov5640_mode_data[index].width;
 	fse->max_width = fse->min_width;
-	fse->min_height = ov5640_mode_data[fse->index].height;
+	fse->min_height = ov5640_mode_data[index].height;
 	fse->max_height = fse->min_height;
 
 	return 0;
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 25/27] media: ov5640: Adjust format to bpp in s_fmt
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (23 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 24/27] media: ov5640: Restrict sizes to mbus code Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 26/27] media: ov5640: Split DVP and CSI-2 formats Jacopo Mondi
                   ` (4 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

The ov5640 driver supports different sizes for different mbus_codes.
In particular:

- 8bpp modes: high resolution sizes (>= 1280x720)
- 16bpp modes: all sizes
- 24bpp modes: low resolutions sizes (< 1280x720)

Adjust the image sizes according to the above constraints.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 1510b9e8322d..9f094d18ad6f 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2693,6 +2693,7 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
 				   enum ov5640_frame_rate fr,
 				   const struct ov5640_mode_info **new_mode)
 {
+	unsigned int bpp = ov5640_code_to_bpp(fmt->code);
 	struct ov5640_dev *sensor = to_ov5640_dev(sd);
 	const struct ov5640_mode_info *mode;
 	int i;
@@ -2700,6 +2701,17 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
 	mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
 	if (!mode)
 		return -EINVAL;
+
+	/*
+	 * Adjust mode according to bpp:
+	 * - 8bpp modes work for resolution >= 1280x720
+	 * - 24bpp modes work resolution < 1280x720
+	 */
+	if (bpp == 8 && mode->width < 1280)
+		mode = &ov5640_mode_data[OV5640_MODE_720P_1280_720];
+	else if (bpp == 24 && mode->width > 1024)
+		mode = &ov5640_mode_data[OV5640_MODE_XGA_1024_768];
+
 	fmt->width = mode->width;
 	fmt->height = mode->height;
 
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 26/27] media: ov5640: Split DVP and CSI-2 formats
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (24 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 25/27] media: ov5640: Adjust format to bpp in s_fmt Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-02-24  9:43 ` [PATCH v5 27/27] media: ov5640: Move format mux config in format Jacopo Mondi
                   ` (3 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

The format enumeration list is shared between CSI-2 and DVP modes.
This lead to the enumeration of unsupported format variants in both
modes.

Separate the list of DVP and CSI-2 formats and create helpers to access
the correct one.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 128 ++++++++++++++++++++++++++-----------
 1 file changed, 91 insertions(+), 37 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 9f094d18ad6f..744bf53248a8 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -188,11 +188,13 @@ enum ov5640_format_mux {
 	OV5640_FMT_MUX_RAW_CIP,
 };
 
-static const struct ov5640_pixfmt {
+struct ov5640_pixfmt {
 	u32 code;
 	u32 colorspace;
 	u8 bpp;
-} ov5640_formats[] = {
+};
+
+static const struct ov5640_pixfmt ov5640_dvp_formats[] = {
 	{
 		.code = MEDIA_BUS_FMT_JPEG_1X8,
 		.colorspace = V4L2_COLORSPACE_JPEG,
@@ -202,23 +204,48 @@ static const struct ov5640_pixfmt {
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.bpp = 16,
 	}, {
-		.code = MEDIA_BUS_FMT_UYVY8_1X16,
+		.code = MEDIA_BUS_FMT_YUYV8_2X8,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.bpp = 16,
 	}, {
-		.code = MEDIA_BUS_FMT_YUYV8_2X8,
+		.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.bpp = 16,
 	}, {
-		.code = MEDIA_BUS_FMT_YUYV8_1X16,
+		.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.bpp = 16,
 	}, {
-		.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 8,
+	}, {
+		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 8
+	}, {
+		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
 		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 8,
+	}, {
+		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 8,
+	},
+	{ /* sentinel */ }
+};
+
+static const struct ov5640_pixfmt ov5640_csi2_formats[] = {
+	{
+		.code = MEDIA_BUS_FMT_JPEG_1X8,
+		.colorspace = V4L2_COLORSPACE_JPEG,
 		.bpp = 16,
 	}, {
-		.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+		.code = MEDIA_BUS_FMT_UYVY8_1X16,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bpp = 16,
+	}, {
+		.code = MEDIA_BUS_FMT_YUYV8_1X16,
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.bpp = 16,
 	}, {
@@ -246,20 +273,9 @@ static const struct ov5640_pixfmt {
 		.colorspace = V4L2_COLORSPACE_SRGB,
 		.bpp = 8,
 	},
+	{ /* sentinel */ }
 };
 
-static u32 ov5640_code_to_bpp(u32 code)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(ov5640_formats); ++i) {
-		if (ov5640_formats[i].code == code)
-			return ov5640_formats[i].bpp;
-	}
-
-	return 0;
-}
-
 /*
  * FIXME: remove this when a subdev API becomes available
  * to set the MIPI CSI-2 virtual channel.
@@ -408,6 +424,35 @@ static inline bool ov5640_is_csi2(const struct ov5640_dev *sensor)
 	return sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY;
 }
 
+static inline const struct ov5640_pixfmt *
+ov5640_formats(struct ov5640_dev *sensor)
+{
+	return ov5640_is_csi2(sensor) ? ov5640_csi2_formats
+				      : ov5640_dvp_formats;
+}
+
+static const struct ov5640_pixfmt *
+ov5640_code_to_pixfmt(struct ov5640_dev *sensor, u32 code)
+{
+	const struct ov5640_pixfmt *formats = ov5640_formats(sensor);
+	unsigned int i;
+
+	for (i = 0; formats[i].code; ++i) {
+		if (formats[i].code == code)
+			return &formats[i];
+	}
+
+	return &formats[0];
+}
+
+static u32 ov5640_code_to_bpp(struct ov5640_dev *sensor, u32 code)
+{
+	const struct ov5640_pixfmt *format = ov5640_code_to_pixfmt(sensor,
+								   code);
+
+	return format->bpp;
+}
+
 /*
  * FIXME: all of these register tables are likely filled with
  * entries that set the register to their power-on default values,
@@ -1389,7 +1434,7 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor)
 	 * (0x01=0.5ns).
 	 */
 	sample_rate = ov5640_pixel_rates[sensor->current_mode->pixel_rate]
-		    * (ov5640_code_to_bpp(fmt->code) / 8);
+		    * (ov5640_code_to_bpp(sensor, fmt->code) / 8);
 	pclk_period = 2000000000U / sample_rate;
 
 	/* Program the clock tree registers. */
@@ -1455,7 +1500,7 @@ static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor)
 	int ret;
 
 	rate = ov5640_calc_pixel_rate(sensor);
-	rate *= ov5640_code_to_bpp(sensor->fmt.code);
+	rate *= ov5640_code_to_bpp(sensor, sensor->fmt.code);
 	rate /= sensor->ep.bus.parallel.bus_width;
 
 	ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
@@ -2693,15 +2738,18 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
 				   enum ov5640_frame_rate fr,
 				   const struct ov5640_mode_info **new_mode)
 {
-	unsigned int bpp = ov5640_code_to_bpp(fmt->code);
 	struct ov5640_dev *sensor = to_ov5640_dev(sd);
 	const struct ov5640_mode_info *mode;
-	int i;
+	const struct ov5640_pixfmt *pixfmt;
+	unsigned int bpp;
 
 	mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
 	if (!mode)
 		return -EINVAL;
 
+	pixfmt = ov5640_code_to_pixfmt(sensor, fmt->code);
+	bpp = pixfmt->bpp;
+
 	/*
 	 * Adjust mode according to bpp:
 	 * - 8bpp modes work for resolution >= 1280x720
@@ -2718,14 +2766,8 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
 	if (new_mode)
 		*new_mode = mode;
 
-	for (i = 0; i < ARRAY_SIZE(ov5640_formats); i++)
-		if (ov5640_formats[i].code == fmt->code)
-			break;
-	if (i >= ARRAY_SIZE(ov5640_formats))
-		i = 0;
-
-	fmt->code = ov5640_formats[i].code;
-	fmt->colorspace = ov5640_formats[i].colorspace;
+	fmt->code = pixfmt->code;
+	fmt->colorspace = pixfmt->colorspace;
 	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
 	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
 	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
@@ -2767,7 +2809,7 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
 	 * progressively slow it down if it exceeds 1GHz.
 	 */
 	num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes;
-	bpp = ov5640_code_to_bpp(fmt->code);
+	bpp = ov5640_code_to_bpp(sensor, fmt->code);
 	do {
 		pixel_rate = ov5640_pixel_rates[pixel_rate_id];
 		link_freq = pixel_rate * bpp / (2 * num_lanes);
@@ -3460,7 +3502,8 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
 				  struct v4l2_subdev_state *sd_state,
 				  struct v4l2_subdev_frame_size_enum *fse)
 {
-	u32 bpp = ov5640_code_to_bpp(fse->code);
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	u32 bpp = ov5640_code_to_bpp(sensor, fse->code);
 	unsigned int index = fse->index;
 
 	if (fse->pad != 0)
@@ -3588,12 +3631,23 @@ static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
 				 struct v4l2_subdev_state *sd_state,
 				 struct v4l2_subdev_mbus_code_enum *code)
 {
-	if (code->pad != 0)
-		return -EINVAL;
-	if (code->index >= ARRAY_SIZE(ov5640_formats))
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	const struct ov5640_pixfmt *formats;
+	unsigned int num_formats;
+
+	if (ov5640_is_csi2(sensor)) {
+		formats = ov5640_csi2_formats;
+		num_formats = ARRAY_SIZE(ov5640_csi2_formats) - 1;
+	} else {
+		formats = ov5640_dvp_formats;
+		num_formats = ARRAY_SIZE(ov5640_dvp_formats) - 1;
+	}
+
+	if (code->index >= num_formats)
 		return -EINVAL;
 
-	code->code = ov5640_formats[code->index].code;
+	code->code = formats[code->index].code;
+
 	return 0;
 }
 
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v5 27/27] media: ov5640: Move format mux config in format
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (25 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 26/27] media: ov5640: Split DVP and CSI-2 formats Jacopo Mondi
@ 2022-02-24  9:43 ` Jacopo Mondi
  2022-03-04  0:09 ` [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Sakari Ailus
                   ` (2 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-02-24  9:43 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: Jacopo Mondi, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

The image format produced by the sensor is controlled by two registers,
whose values computation is open coded in ov5640_set_framefmt().

As we have a list of formats already, move the OV5640_REG_FORMAT_CONTROL00
and OV5640_REG_ISP_FORMAT_MUX_CTRL register values to the static list
of formats instead of open coding it.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/ov5640.c | 233 +++++++++++++++++++------------------
 1 file changed, 117 insertions(+), 116 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 744bf53248a8..4de83d0ef85d 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -192,86 +192,142 @@ struct ov5640_pixfmt {
 	u32 code;
 	u32 colorspace;
 	u8 bpp;
+	u8 ctrl00;
+	enum ov5640_format_mux mux;
 };
 
 static const struct ov5640_pixfmt ov5640_dvp_formats[] = {
 	{
-		.code = MEDIA_BUS_FMT_JPEG_1X8,
-		.colorspace = V4L2_COLORSPACE_JPEG,
-		.bpp = 16,
+		/* YUV422, YUYV */
+		.code		= MEDIA_BUS_FMT_JPEG_1X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.bpp		= 16,
+		.ctrl00		= 0x30,
+		.mux		= OV5640_FMT_MUX_YUV422,
 	}, {
-		.code = MEDIA_BUS_FMT_UYVY8_2X8,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 16,
+		/* YUV422, UYVY */
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 16,
+		.ctrl00		= 0x3f,
+		.mux		= OV5640_FMT_MUX_YUV422,
 	}, {
-		.code = MEDIA_BUS_FMT_YUYV8_2X8,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 16,
+		/* YUV422, YUYV */
+		.code		= MEDIA_BUS_FMT_YUYV8_2X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 16,
+		.ctrl00		= 0x30,
+		.mux		= OV5640_FMT_MUX_YUV422,
 	}, {
-		.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 16,
+		/* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
+		.code		= MEDIA_BUS_FMT_RGB565_2X8_LE,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 16,
+		.ctrl00		= 0x6f,
+		.mux		= OV5640_FMT_MUX_RGB,
 	}, {
-		.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 16,
+		/* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
+		.code		= MEDIA_BUS_FMT_RGB565_2X8_BE,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 16,
+		.ctrl00		= 0x61,
+		.mux		= OV5640_FMT_MUX_RGB,
 	}, {
-		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 8,
+		/* Raw, BGBG... / GRGR... */
+		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 8,
+		.ctrl00		= 0x00,
+		.mux		= OV5640_FMT_MUX_RAW_DPC,
 	}, {
-		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 8
+		/* Raw bayer, GBGB... / RGRG... */
+		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 8,
+		.ctrl00		= 0x01,
+		.mux		= OV5640_FMT_MUX_RAW_DPC,
 	}, {
-		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 8,
+		/* Raw bayer, GRGR... / BGBG... */
+		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 8,
+		.ctrl00		= 0x02,
+		.mux		= OV5640_FMT_MUX_RAW_DPC,
 	}, {
-		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 8,
+		/* Raw bayer, RGRG... / GBGB... */
+		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 8,
+		.ctrl00		= 0x03,
+		.mux		= OV5640_FMT_MUX_RAW_DPC,
 	},
 	{ /* sentinel */ }
 };
 
 static const struct ov5640_pixfmt ov5640_csi2_formats[] = {
 	{
-		.code = MEDIA_BUS_FMT_JPEG_1X8,
-		.colorspace = V4L2_COLORSPACE_JPEG,
-		.bpp = 16,
+		/* YUV422, YUYV */
+		.code		= MEDIA_BUS_FMT_JPEG_1X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+		.bpp		= 16,
+		.ctrl00		= 0x30,
+		.mux		= OV5640_FMT_MUX_YUV422,
 	}, {
-		.code = MEDIA_BUS_FMT_UYVY8_1X16,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 16,
+		/* YUV422, UYVY */
+		.code		= MEDIA_BUS_FMT_UYVY8_1X16,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 16,
+		.ctrl00		= 0x3f,
+		.mux		= OV5640_FMT_MUX_YUV422,
 	}, {
-		.code = MEDIA_BUS_FMT_YUYV8_1X16,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 16,
+		/* YUV422, YUYV */
+		.code		= MEDIA_BUS_FMT_YUYV8_1X16,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 16,
+		.ctrl00		= 0x30,
+		.mux		= OV5640_FMT_MUX_YUV422,
 	}, {
-		.code = MEDIA_BUS_FMT_RGB565_1X16,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 16,
+		/* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
+		.code		= MEDIA_BUS_FMT_RGB565_1X16,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 16,
+		.ctrl00		= 0x6f,
+		.mux		= OV5640_FMT_MUX_RGB,
 	}, {
-		.code = MEDIA_BUS_FMT_BGR888_1X24,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 24,
+		/* BGR888: RGB */
+		.code		= MEDIA_BUS_FMT_BGR888_1X24,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 24,
+		.ctrl00		= 0x23,
+		.mux		= OV5640_FMT_MUX_RGB,
 	}, {
-		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 8,
+		/* Raw, BGBG... / GRGR... */
+		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 8,
+		.ctrl00		= 0x00,
+		.mux		= OV5640_FMT_MUX_RAW_DPC,
 	}, {
-		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 8
+		/* Raw bayer, GBGB... / RGRG... */
+		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 8,
+		.ctrl00		= 0x01,
+		.mux		= OV5640_FMT_MUX_RAW_DPC,
 	}, {
-		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 8,
+		/* Raw bayer, GRGR... / BGBG... */
+		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 8,
+		.ctrl00		= 0x02,
+		.mux		= OV5640_FMT_MUX_RAW_DPC,
 	}, {
-		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
-		.colorspace = V4L2_COLORSPACE_SRGB,
-		.bpp = 8,
+		/* Raw bayer, RGRG... / GBGB... */
+		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.bpp		= 8,
+		.ctrl00		= 0x03,
+		.mux		= OV5640_FMT_MUX_RAW_DPC,
 	},
 	{ /* sentinel */ }
 };
@@ -2944,76 +3000,21 @@ static int ov5640_get_selection(struct v4l2_subdev *sd,
 static int ov5640_set_framefmt(struct ov5640_dev *sensor,
 			       struct v4l2_mbus_framefmt *format)
 {
+	bool is_jpeg = format->code == MEDIA_BUS_FMT_JPEG_1X8;
+	const struct ov5640_pixfmt *pixfmt;
 	int ret = 0;
-	bool is_jpeg = false;
-	u8 fmt, mux;
 
-	switch (format->code) {
-	case MEDIA_BUS_FMT_UYVY8_1X16:
-	case MEDIA_BUS_FMT_UYVY8_2X8:
-		/* YUV422, UYVY */
-		fmt = 0x3f;
-		mux = OV5640_FMT_MUX_YUV422;
-		break;
-	case MEDIA_BUS_FMT_YUYV8_1X16:
-	case MEDIA_BUS_FMT_YUYV8_2X8:
-		/* YUV422, YUYV */
-		fmt = 0x30;
-		mux = OV5640_FMT_MUX_YUV422;
-		break;
-	case MEDIA_BUS_FMT_RGB565_2X8_LE:
-	case MEDIA_BUS_FMT_RGB565_1X16:
-		/* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
-		fmt = 0x6F;
-		mux = OV5640_FMT_MUX_RGB;
-		break;
-	case MEDIA_BUS_FMT_RGB565_2X8_BE:
-		/* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
-		fmt = 0x61;
-		mux = OV5640_FMT_MUX_RGB;
-		break;
-	case MEDIA_BUS_FMT_BGR888_1X24:
-		/* BGR888: RGB */
-		fmt = 0x23;
-		mux = OV5640_FMT_MUX_RGB;
-		break;
-	case MEDIA_BUS_FMT_JPEG_1X8:
-		/* YUV422, YUYV */
-		fmt = 0x30;
-		mux = OV5640_FMT_MUX_YUV422;
-		is_jpeg = true;
-		break;
-	case MEDIA_BUS_FMT_SBGGR8_1X8:
-		/* Raw, BGBG... / GRGR... */
-		fmt = 0x00;
-		mux = OV5640_FMT_MUX_RAW_DPC;
-		break;
-	case MEDIA_BUS_FMT_SGBRG8_1X8:
-		/* Raw bayer, GBGB... / RGRG... */
-		fmt = 0x01;
-		mux = OV5640_FMT_MUX_RAW_DPC;
-		break;
-	case MEDIA_BUS_FMT_SGRBG8_1X8:
-		/* Raw bayer, GRGR... / BGBG... */
-		fmt = 0x02;
-		mux = OV5640_FMT_MUX_RAW_DPC;
-		break;
-	case MEDIA_BUS_FMT_SRGGB8_1X8:
-		/* Raw bayer, RGRG... / GBGB... */
-		fmt = 0x03;
-		mux = OV5640_FMT_MUX_RAW_DPC;
-		break;
-	default:
-		return -EINVAL;
-	}
+	pixfmt = ov5640_code_to_pixfmt(sensor, format->code);
 
 	/* FORMAT CONTROL00: YUV and RGB formatting */
-	ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, fmt);
+	ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00,
+			       pixfmt->ctrl00);
 	if (ret)
 		return ret;
 
 	/* FORMAT MUX CONTROL: ISP YUV or RGB */
-	ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, mux);
+	ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL,
+			       pixfmt->mux);
 	if (ret)
 		return ret;
 
-- 
2.35.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (26 preceding siblings ...)
  2022-02-24  9:43 ` [PATCH v5 27/27] media: ov5640: Move format mux config in format Jacopo Mondi
@ 2022-03-04  0:09 ` Sakari Ailus
  2022-03-04  8:41   ` Eugen.Hristev
  2022-03-23  8:51 ` Tomi Valkeinen
  2022-04-07 16:24 ` Hugues FRUCHET - FOSS
  29 siblings, 1 reply; 53+ messages in thread
From: Sakari Ailus @ 2022-03-04  0:09 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Steve Longerbeam, laurent.pinchart, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hello folks,

On Thu, Feb 24, 2022 at 10:42:46AM +0100, Jacopo Mondi wrote:
> A branch for testing based on the most recent media-master is available at
> https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5

The set has been around for quite some time without tangible functional
changes, please do let me know if you have concerns merging it.

Thanks.

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-03-04  0:09 ` [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Sakari Ailus
@ 2022-03-04  8:41   ` Eugen.Hristev
  2022-03-04  8:45     ` Jacopo Mondi
  0 siblings, 1 reply; 53+ messages in thread
From: Eugen.Hristev @ 2022-03-04  8:41 UTC (permalink / raw)
  To: sakari.ailus, jacopo
  Cc: slongerbeam, laurent.pinchart, hverkuil-cisco, mirela.rabulea,
	xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, jbrunet,
	paul.elder, mchehab, linux-media

On 3/4/22 2:09 AM, Sakari Ailus wrote:

> Hello folks,
> 
> On Thu, Feb 24, 2022 at 10:42:46AM +0100, Jacopo Mondi wrote:
>> A branch for testing based on the most recent media-master is available at
>> https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
> 
> The set has been around for quite some time without tangible functional
> changes, please do let me know if you have concerns merging it.
> 
> Thanks.
> 
> --
> Sakari Ailus
> 


Hello Sakari, Jacopo,

I retested this series and on my side, the regression in version 3 is 
gone, I can capture images fine now YUYV@1280x720 .
I also retested 1920x1080 in RAW, and it works fine.

You can add my :
Tested-by: Eugen Hristev <eugen.hristev@microchip.com>

I am not sure if you have to add it to the whole series, because I only 
tested parallel interface with atmel ISC controller on sama5d2_xplained.
Up to your judgment to add it to the patches that impact the parallel 
interface.

Thanks again Jacopo for improving the support for this sensor.

Eugen

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-03-04  8:41   ` Eugen.Hristev
@ 2022-03-04  8:45     ` Jacopo Mondi
  2022-03-04  8:52       ` Sakari Ailus
  0 siblings, 1 reply; 53+ messages in thread
From: Jacopo Mondi @ 2022-03-04  8:45 UTC (permalink / raw)
  To: Eugen.Hristev
  Cc: sakari.ailus, slongerbeam, laurent.pinchart, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, jbrunet,
	paul.elder, mchehab, linux-media

Hi Eugen

On Fri, Mar 04, 2022 at 08:41:23AM +0000, Eugen.Hristev@microchip.com wrote:
> On 3/4/22 2:09 AM, Sakari Ailus wrote:
>
> > Hello folks,
> >
> > On Thu, Feb 24, 2022 at 10:42:46AM +0100, Jacopo Mondi wrote:
> >> A branch for testing based on the most recent media-master is available at
> >> https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
> >
> > The set has been around for quite some time without tangible functional
> > changes, please do let me know if you have concerns merging it.
> >
> > Thanks.
> >
> > --
> > Sakari Ailus
> >
>
>
> Hello Sakari, Jacopo,
>
> I retested this series and on my side, the regression in version 3 is
> gone, I can capture images fine now YUYV@1280x720 .
> I also retested 1920x1080 in RAW, and it works fine.
>

fiuuuu, I can breath again now :)

> You can add my :
> Tested-by: Eugen Hristev <eugen.hristev@microchip.com>
>
> I am not sure if you have to add it to the whole series, because I only
> tested parallel interface with atmel ISC controller on sama5d2_xplained.
> Up to your judgment to add it to the patches that impact the parallel
> interface.
>
> Thanks again Jacopo for improving the support for this sensor.

Thank you for testing with parallel mode, I would have made lot of
people unhappy otherwise!

>
> Eugen

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-03-04  8:45     ` Jacopo Mondi
@ 2022-03-04  8:52       ` Sakari Ailus
  0 siblings, 0 replies; 53+ messages in thread
From: Sakari Ailus @ 2022-03-04  8:52 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Eugen.Hristev, slongerbeam, laurent.pinchart, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, jbrunet,
	paul.elder, mchehab, linux-media

On Fri, Mar 04, 2022 at 09:45:29AM +0100, Jacopo Mondi wrote:
> > Thanks again Jacopo for improving the support for this sensor.
> 
> Thank you for testing with parallel mode, I would have made lot of
> people unhappy otherwise!

Thanks, guys!

Applied to my tree.

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (27 preceding siblings ...)
  2022-03-04  0:09 ` [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Sakari Ailus
@ 2022-03-23  8:51 ` Tomi Valkeinen
  2022-03-23  8:54   ` Sakari Ailus
                     ` (2 more replies)
  2022-04-07 16:24 ` Hugues FRUCHET - FOSS
  29 siblings, 3 replies; 53+ messages in thread
From: Tomi Valkeinen @ 2022-03-23  8:51 UTC (permalink / raw)
  To: Jacopo Mondi, Steve Longerbeam
  Cc: laurent.pinchart, sakari.ailus, hverkuil-cisco, mirela.rabulea,
	xavier.roumegue, hugues.fruchet, prabhakar.mahadev-lad.rj,
	aford173, festevam, Eugen.Hristev, jbrunet, paul.elder,
	Mauro Carvalho Chehab, linux-media

Hi Jacopo,

On 24/02/2022 11:42, Jacopo Mondi wrote:
> v1:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
> v2:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
> v3:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
> v4:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=7389
> 
> A branch for testing based on the most recent media-master is available at
> https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5

I tested these with DRA76 EVM & CAL, using CAL's legacy non-MC mode. It 
doesn't work. I think there are two problems:

- CAL uses mbus codes like MEDIA_BUS_FMT_UYVY8_2X8 for CSI-2, not 1X16. 
OV5640 used to support 2X8, but now it doesn't.

- OV5640 sets the default code to MEDIA_BUS_FMT_UYVY8_2X8, even for 
CSI-2 where it doesn't support MEDIA_BUS_FMT_UYVY8_2X8.

I'd like to just change CAL and drop the 2X8 support and instead use 
1X16, but then any sensor that uses 2X8 would work. So I guess I need to 
change the code to support both.

Anyway, both of those issues might also surface on other platforms, as 
ov5640 behavior has changed.

  Tomi

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-03-23  8:51 ` Tomi Valkeinen
@ 2022-03-23  8:54   ` Sakari Ailus
  2022-03-23  8:59   ` Laurent Pinchart
  2022-03-23  9:50   ` Jacopo Mondi
  2 siblings, 0 replies; 53+ messages in thread
From: Sakari Ailus @ 2022-03-23  8:54 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Jacopo Mondi, Steve Longerbeam, laurent.pinchart, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Moi,

On Wed, Mar 23, 2022 at 10:51:04AM +0200, Tomi Valkeinen wrote:
> Hi Jacopo,
> 
> On 24/02/2022 11:42, Jacopo Mondi wrote:
> > v1:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
> > v2:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
> > v3:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
> > v4:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7389
> > 
> > A branch for testing based on the most recent media-master is available at
> > https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
> 
> I tested these with DRA76 EVM & CAL, using CAL's legacy non-MC mode. It
> doesn't work. I think there are two problems:
> 
> - CAL uses mbus codes like MEDIA_BUS_FMT_UYVY8_2X8 for CSI-2, not 1X16.
> OV5640 used to support 2X8, but now it doesn't.
> 
> - OV5640 sets the default code to MEDIA_BUS_FMT_UYVY8_2X8, even for CSI-2
> where it doesn't support MEDIA_BUS_FMT_UYVY8_2X8.
> 
> I'd like to just change CAL and drop the 2X8 support and instead use 1X16,
> but then any sensor that uses 2X8 would work. So I guess I need to change
> the code to support both.

How many drivers there are using 2X8 formats on CSI-2? I remember there has
been probably few, and it should be easy to fix them.

It's another question whether the 2X8 format should be kept for
compatibility. Perhaps the driver could allow setting it, but should then
return 1X16 when queried.

-- 
Terveisin,

Sakari Ailus

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-03-23  8:51 ` Tomi Valkeinen
  2022-03-23  8:54   ` Sakari Ailus
@ 2022-03-23  8:59   ` Laurent Pinchart
  2022-03-23  9:50   ` Jacopo Mondi
  2 siblings, 0 replies; 53+ messages in thread
From: Laurent Pinchart @ 2022-03-23  8:59 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Jacopo Mondi, Steve Longerbeam, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Tomi,

On Wed, Mar 23, 2022 at 10:51:04AM +0200, Tomi Valkeinen wrote:
> On 24/02/2022 11:42, Jacopo Mondi wrote:
> > v1:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
> > v2:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
> > v3:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
> > v4:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7389
> > 
> > A branch for testing based on the most recent media-master is available at
> > https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
> 
> I tested these with DRA76 EVM & CAL, using CAL's legacy non-MC mode. It 
> doesn't work. I think there are two problems:
> 
> - CAL uses mbus codes like MEDIA_BUS_FMT_UYVY8_2X8 for CSI-2, not 1X16. 
> OV5640 used to support 2X8, but now it doesn't.
> 
> - OV5640 sets the default code to MEDIA_BUS_FMT_UYVY8_2X8, even for 
> CSI-2 where it doesn't support MEDIA_BUS_FMT_UYVY8_2X8.
> 
> I'd like to just change CAL and drop the 2X8 support and instead use 
> 1X16, but then any sensor that uses 2X8 would work. So I guess I need to 
> change the code to support both.

We really need to phase out 2X8 for CSI-2. Can you add a warning in that
case if you support both ?

> Anyway, both of those issues might also surface on other platforms, as 
> ov5640 behavior has changed.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-03-23  8:51 ` Tomi Valkeinen
  2022-03-23  8:54   ` Sakari Ailus
  2022-03-23  8:59   ` Laurent Pinchart
@ 2022-03-23  9:50   ` Jacopo Mondi
  2022-03-23 10:41     ` Laurent Pinchart
  2022-04-07 16:25     ` Hugues FRUCHET - FOSS
  2 siblings, 2 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-03-23  9:50 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Steve Longerbeam, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Tomi thanks for testing

On Wed, Mar 23, 2022 at 10:51:04AM +0200, Tomi Valkeinen wrote:
> Hi Jacopo,
>
> On 24/02/2022 11:42, Jacopo Mondi wrote:
> > v1:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
> > v2:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
> > v3:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
> > v4:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=7389
> >
> > A branch for testing based on the most recent media-master is available at
> > https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
>
> I tested these with DRA76 EVM & CAL, using CAL's legacy non-MC mode. It
> doesn't work. I think there are two problems:
>
> - CAL uses mbus codes like MEDIA_BUS_FMT_UYVY8_2X8 for CSI-2, not 1X16.
> OV5640 used to support 2X8, but now it doesn't.
>
> - OV5640 sets the default code to MEDIA_BUS_FMT_UYVY8_2X8, even for CSI-2
> where it doesn't support MEDIA_BUS_FMT_UYVY8_2X8.

This might be worth an additional patch that decides what default
format to use based on the bus type.

>
> I'd like to just change CAL and drop the 2X8 support and instead use 1X16,
> but then any sensor that uses 2X8 would work. So I guess I need to change
> the code to support both.
>
> Anyway, both of those issues might also surface on other platforms, as
> ov5640 behavior has changed.
>

I'm afraid sooner or later this should have happened ?

I think CSI-2 receivers should be updated, but I share your concerns
about breaking other platforms.

On one side we shouldn't be breaking userspace and this change might
break some assumptions in users' pipeline configuration scripts and
could prevent drivers that used to work together from being
compatible at all.

On the other side we would never be able to change anything at all if
such a change is expected to happen atomically on all platforms and
sensors.

As the change is so trivial I guess it's fair to expect users of
bridge drivers not compatible with 1X16 to fix them, but I cannot tell
if it's an acceptable policy or not.

As Sakari suggested we could also move all CSI-2 transmitters to use 1X16
and have receivers adjust as soon as someone detects a breakage.

I can revert the change that restricts the enumerated format to the
currently in use bus type[1] if desired, but I would prefer receivers
to adjust when needed. Is this acceptable ?

Thanks
  j

[1] "media: ov5640: Split DVP and CSI-2 formats

>  Tomi

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-03-23  9:50   ` Jacopo Mondi
@ 2022-03-23 10:41     ` Laurent Pinchart
  2022-03-28 14:57       ` Jacopo Mondi
  2022-04-07 16:25     ` Hugues FRUCHET - FOSS
  1 sibling, 1 reply; 53+ messages in thread
From: Laurent Pinchart @ 2022-03-23 10:41 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Tomi Valkeinen, Steve Longerbeam, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

On Wed, Mar 23, 2022 at 10:50:19AM +0100, Jacopo Mondi wrote:
> Hi Tomi thanks for testing
> 
> On Wed, Mar 23, 2022 at 10:51:04AM +0200, Tomi Valkeinen wrote:
> > Hi Jacopo,
> >
> > On 24/02/2022 11:42, Jacopo Mondi wrote:
> > > v1:
> > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
> > > v2:
> > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
> > > v3:
> > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
> > > v4:
> > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7389
> > >
> > > A branch for testing based on the most recent media-master is available at
> > > https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
> >
> > I tested these with DRA76 EVM & CAL, using CAL's legacy non-MC mode. It
> > doesn't work. I think there are two problems:
> >
> > - CAL uses mbus codes like MEDIA_BUS_FMT_UYVY8_2X8 for CSI-2, not 1X16.
> > OV5640 used to support 2X8, but now it doesn't.
> >
> > - OV5640 sets the default code to MEDIA_BUS_FMT_UYVY8_2X8, even for CSI-2
> > where it doesn't support MEDIA_BUS_FMT_UYVY8_2X8.
> 
> This might be worth an additional patch that decides what default
> format to use based on the bus type.
> 
> > I'd like to just change CAL and drop the 2X8 support and instead use 1X16,
> > but then any sensor that uses 2X8 would work. So I guess I need to change
> > the code to support both.
> >
> > Anyway, both of those issues might also surface on other platforms, as
> > ov5640 behavior has changed.
> 
> I'm afraid sooner or later this should have happened ?
> 
> I think CSI-2 receivers should be updated, but I share your concerns
> about breaking other platforms.
> 
> On one side we shouldn't be breaking userspace and this change might
> break some assumptions in users' pipeline configuration scripts and
> could prevent drivers that used to work together from being
> compatible at all.
> 
> On the other side we would never be able to change anything at all if
> such a change is expected to happen atomically on all platforms and
> sensors.
> 
> As the change is so trivial I guess it's fair to expect users of
> bridge drivers not compatible with 1X16 to fix them, but I cannot tell
> if it's an acceptable policy or not.
> 
> As Sakari suggested we could also move all CSI-2 transmitters to use 1X16
> and have receivers adjust as soon as someone detects a breakage.
> 
> I can revert the change that restricts the enumerated format to the
> currently in use bus type[1] if desired, but I would prefer receivers
> to adjust when needed. Is this acceptable ?

That would be my preference too. How about implementing Sakari's
suggestion of turning the 2X8 formats into 1X16 in .set_fmt() for CSI-2
? That way we'll minimize any risk of breakage for userspace. Host-side
drivers that use the OV5640 will still need to be converted from 2X8 to
1X16, but that's in-kernel only and should be manageable.

> [1] "media: ov5640: Split DVP and CSI-2 formats

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-03-23 10:41     ` Laurent Pinchart
@ 2022-03-28 14:57       ` Jacopo Mondi
  2022-03-28 20:50         ` Sakari Ailus
  0 siblings, 1 reply; 53+ messages in thread
From: Jacopo Mondi @ 2022-03-28 14:57 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Tomi Valkeinen, Steve Longerbeam, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi

On Wed, Mar 23, 2022 at 12:41:47PM +0200, Laurent Pinchart wrote:
> On Wed, Mar 23, 2022 at 10:50:19AM +0100, Jacopo Mondi wrote:
> > Hi Tomi thanks for testing
> >
> > On Wed, Mar 23, 2022 at 10:51:04AM +0200, Tomi Valkeinen wrote:
> > > Hi Jacopo,
> > >
> > > On 24/02/2022 11:42, Jacopo Mondi wrote:
> > > > v1:
> > > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
> > > > v2:
> > > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
> > > > v3:
> > > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
> > > > v4:
> > > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7389
> > > >
> > > > A branch for testing based on the most recent media-master is available at
> > > > https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
> > >
> > > I tested these with DRA76 EVM & CAL, using CAL's legacy non-MC mode. It
> > > doesn't work. I think there are two problems:
> > >
> > > - CAL uses mbus codes like MEDIA_BUS_FMT_UYVY8_2X8 for CSI-2, not 1X16.
> > > OV5640 used to support 2X8, but now it doesn't.
> > >
> > > - OV5640 sets the default code to MEDIA_BUS_FMT_UYVY8_2X8, even for CSI-2
> > > where it doesn't support MEDIA_BUS_FMT_UYVY8_2X8.
> >
> > This might be worth an additional patch that decides what default
> > format to use based on the bus type.
> >
> > > I'd like to just change CAL and drop the 2X8 support and instead use 1X16,
> > > but then any sensor that uses 2X8 would work. So I guess I need to change
> > > the code to support both.
> > >
> > > Anyway, both of those issues might also surface on other platforms, as
> > > ov5640 behavior has changed.
> >
> > I'm afraid sooner or later this should have happened ?
> >
> > I think CSI-2 receivers should be updated, but I share your concerns
> > about breaking other platforms.
> >
> > On one side we shouldn't be breaking userspace and this change might
> > break some assumptions in users' pipeline configuration scripts and
> > could prevent drivers that used to work together from being
> > compatible at all.
> >
> > On the other side we would never be able to change anything at all if
> > such a change is expected to happen atomically on all platforms and
> > sensors.
> >
> > As the change is so trivial I guess it's fair to expect users of
> > bridge drivers not compatible with 1X16 to fix them, but I cannot tell
> > if it's an acceptable policy or not.
> >
> > As Sakari suggested we could also move all CSI-2 transmitters to use 1X16
> > and have receivers adjust as soon as someone detects a breakage.
> >
> > I can revert the change that restricts the enumerated format to the
> > currently in use bus type[1] if desired, but I would prefer receivers
> > to adjust when needed. Is this acceptable ?
>
> That would be my preference too. How about implementing Sakari's
> suggestion of turning the 2X8 formats into 1X16 in .set_fmt() for CSI-2

It's quite a tedious job, as for each driver that uses 2X8, if one
doesn't know the device by name, searching online or in the driver for
hints about what interface it uses (or discerning it from the
manufacturing year) takes quite some time.

I tried at the best of my knowledge, focusing on image sensor and
ignoring video converters as most of them have an analogue output, for
which I'm not sure what version is more opportune.

The only outlier I have seen is ov5645, which is a CSI-2 sensor but
uses 2X8. I've fixed that and will send a patch.

> ? That way we'll minimize any risk of breakage for userspace. Host-side

As the format will be adjusted, if drivers do not mess-up and select
something completely different, we should be safer yes. It's a
best-effort approach though.

> drivers that use the OV5640 will still need to be converted from 2X8 to
> 1X16, but that's in-kernel only and should be manageable.
>

Agreed, I don't think fixing all receivers should block this series

(speaking of which: Sakari you have collected the series in one of
your branches, but it is not part of the media pull request for
v5.18-rc1. Should I expect this to be post-poned to v5.19 ?)

Thanks
  j
> > [1] "media: ov5640: Split DVP and CSI-2 formats
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-03-28 14:57       ` Jacopo Mondi
@ 2022-03-28 20:50         ` Sakari Ailus
  0 siblings, 0 replies; 53+ messages in thread
From: Sakari Ailus @ 2022-03-28 20:50 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Laurent Pinchart, Tomi Valkeinen, Steve Longerbeam,
	hverkuil-cisco, mirela.rabulea, xavier.roumegue, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Jacopo,

On Mon, Mar 28, 2022 at 04:57:28PM +0200, Jacopo Mondi wrote:
> (speaking of which: Sakari you have collected the series in one of
> your branches, but it is not part of the media pull request for
> v5.18-rc1. Should I expect this to be post-poned to v5.19 ?)

Yes.

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
                   ` (28 preceding siblings ...)
  2022-03-23  8:51 ` Tomi Valkeinen
@ 2022-04-07 16:24 ` Hugues FRUCHET - FOSS
  29 siblings, 0 replies; 53+ messages in thread
From: Hugues FRUCHET - FOSS @ 2022-04-07 16:24 UTC (permalink / raw)
  To: Jacopo Mondi, Steve Longerbeam
  Cc: laurent.pinchart, sakari.ailus, hverkuil-cisco, mirela.rabulea,
	xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Jacopo,

Many thanks for this huge work !

I have tested the serie on ST platform setup using OV5640 CSI-2.
I have not yet tested the OV5640 parallel mode but will do also.

Find below the main feedback, I have replied with more details on each 
related patches:
1) "2X8" mediabus code support broken, I have reverted the patch to 
continue testing
2) frame interval support dropped in flavour to vblank control: I have 
proposed a patch to support both
3) some resolutions/framerate not supported (MAX_VTS to increase)
4) JPEG framerate is divided by 2 with 1280x720@15, 1280x720@30, 
1920x1080@15
=> I have not found a relevant patch to overcome this, seems linked to 
the OV5640_LINK_RATE_MAX limit (mipi_div).

RAW8 not tested but I can also (on my side, this is functional only for 
resolutions >= 720p).

Here is the summary of resolutions/format tested with this serie and my 
patches on top:


*  QCIF 176x144 RGB565 15fps => OK, got 15
*  QCIF 176x144 YUYV 15fps => OK, got 15
*  QCIF 176x144 JPEG 15fps => OK, got 15
*  QCIF 176x144 RGB565 30fps => OK, got 30
*  QCIF 176x144 YUYV 30fps => OK, got 30
*  QCIF 176x144 JPEG 30fps => OK, got 30
*  QVGA 320x240 RGB565 15fps => OK, got 15
*  QVGA 320x240 YUYV 15fps => OK, got 15
*  QVGA 320x240 JPEG 15fps => OK, got 15
*  QVGA 320x240 RGB565 30fps => OK, got 30
*  QVGA 320x240 YUYV 30fps => OK, got 30
*  QVGA 320x240 JPEG 30fps => OK, got 30
*  VGA 640x480 RGB565 15fps => OK, got 15
*  VGA 640x480 YUYV 15fps => OK, got 15
*  VGA 640x480 JPEG 15fps => OK, got 15
*  VGA 640x480 RGB565 30fps => OK, got 30
*  VGA 640x480 YUYV 30fps => OK, got 30
*  VGA 640x480 JPEG 30fps => OK, got 30
*  480p 720x480 RGB565 15fps => OK, got 15
*  480p 720x480 YUYV 15fps => OK, got 15
*  480p 720x480 JPEG 15fps => OK, got 15
*  480p 720x480 RGB565 30fps => OK, got 30
*  480p 720x480 YUYV 30fps => OK, got 30
*  480p 720x480 JPEG 30fps => OK, got 30
*  XGA 1024x768 RGB565 15fps => OK, got 15
*  XGA 1024x768 YUYV 15fps => OK, got 15
*  XGA 1024x768 JPEG 15fps => OK, got 15
*  XGA 1024x768 RGB565 30fps => OK, got 30
*  XGA 1024x768 YUYV 30fps => OK, got 30
*  XGA 1024x768 JPEG 30fps => OK, got 30
*  720p 1280x720 RGB565 15fps => OK, got 15
*  720p 1280x720 YUYV 15fps => OK, got 15
*  720p 1280x720 JPEG 15fps => KO: got 7
===============================^^
[10917.171528] ov5640 1-003c: rate=62000000, freq=248000000, htot=1600, 
height=720, vblank=1863

*  720p 1280x720 RGB565 30fps => OK, got 30
*  720p 1280x720 YUYV 30fps => OK, got 30
*  720p 1280x720 JPEG 30fps => KO: got 15
===============================^^
[10921.317180] ov5640 1-003c: rate=62000000, freq=248000000, htot=1600, 
height=720, vblank=560

*  HD 1920x1080 RGB565 15fps => OK, got 15
*  HD 1920x1080 YUYV 15fps => OK, got 15
*  HD 1920x1080 JPEG 15fps => KO: got 7
===============================^^
[10925.810657] ov5640 1-003c: rate=74000000, freq=296000000, htot=2234, 
height=1080, vblank=1128

*  5Mp 2592x1944 RGB565 15fps => OK, got 15
*  5Mp 2592x1944 YUYV 15fps => OK, got 15
*  5Mp 2592x1944 JPEG 15fps => OK, got 15


Best regards,
Hugues.


On 2/24/22 10:42 AM, Jacopo Mondi wrote:
> v1:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
> v2:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
> v3:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
> v4:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=7389
> 
> A branch for testing based on the most recent media-master is available at
> https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
> 
> v5 (Sakari):
> - Stay strictly in 80 cols
> - use clamp_t to avoid explicit cast
> - use ov5640_timings() where possible
> 
> v4:
> - Very minor update. Added tags and reworked enum_mbus_format as suggested
>    by Laurent.
> 
> v3:
> The series has now grown by 4 patches and the driver is now even larger
> being the formats and the timings for DVP and CSI-2 defined separately.
> 
> Tested in CSI-2 mode with UYVY, RGB565, SBGGR and RGB24 in all supported modes.
> 
> Tested format and sizes enumeration with the new formats definition.
> 
> Tested frame rate handling:
> 
> 	vblank = ( duration msec * pixe_rate MHz / htot - height)
> 
>    640x480 YUYV 15FPS (default 30 FPS)
> 
> 	duration = 666666 msec
> 	pixel_rate = 48 Mhz
> 	htot = 1600
> 	vtot = 1999
> 	vblank = vtot - height = 1519
> 
> 	$ v4l2-ctl -d /dev/v4l-subdev4 -c 0x009e0901=1519
> 	$ yavta -f YUYV -s 640x480 -c100 --skip 7 /dev/video0
> 	...
> 	10 (2) [-] any 11 614400 B 2189.317617 2189.317629 15.244 fps ts mono/EoF
> 	11 (3) [-] any 12 614400 B 2189.383212 2189.383224 15.245 fps ts mono/EoF
> 	12 (4) [-] any 13 614400 B 2189.448810 2189.448821 15.244 fps ts mono/EoF
> 	13 (5) [-] any 14 614400 B 2189.514405 2189.514417 15.245 fps ts mono/EoF
> 	14 (6) [-] any 15 614400 B 2189.580002 2189.580015 15.245 fps ts mono/EoF
> 	..
> 
>    2592x1944 YUVV 15 FPS (default)
> 	$ yavta -f YUYV -s 2592x1944 -c100 --skip 7 /dev/video0
> 	...
> 	6 (6) [-] any 7 10077696 B 2438.377592 2438.377605 15.009 fps ts mono/EoF
> 	7 (7) [-] any 8 10077696 B 2438.444219 2438.444233 15.009 fps ts mono/EoF
> 	8 (0) [-] any 9 10077696 B 2438.510846 2438.510860 15.009 fps ts mono/EoF
> 	9 (1) [-] any 10 10077696 B 2438.577474 2438.577488 15.009 fps ts mono/EoF
> 	10 (2) [-] any 11 10077696 B 2438.644101 2438.644116 15.009 fps ts mono/EoF
> 	11 (3) [-] any 12 10077696 B 2438.710727 2438.710740 15.009 fps ts mono/EoF
> 	12 (4) [-] any 13 10077696 B 2438.777358 2438.777370 15.008 fps ts mono/EoF
> 	13 (5) [-] any 14 10077696 B 2438.843984 2438.843998 15.009 fps ts mono/EoF
> 	14 (6) [-] any 15 10077696 B 2438.910611 2438.910623 15.009 fps ts mono/EoF
> 	15 (7) [-] any 16 10077696 B 2438.977238 2438.977252 15.009 fps ts mono/EoF
> 	16 (0) [-] any 17 10077696 B 2439.043865 2439.043877 15.009 fps ts mono
> 	...
> 
> 
> To enable higher FPS the LINK_FREQ control should be made writable to increase
> the pixel rate
> 
>    640x480 YUYV 60 FPS (pixel_rate = 96 Mhz)
> 
> 	$ yavta -f YUYV -s 640x480 -c100 --skip 7 /dev/video0
>   	...
> 	9 (1) [-] any 10 614400 B 57.098649 57.098667 59.995 fps ts mono/EoF
> 	10 (2) [-] any 11 614400 B 57.115314 57.115332 60.006 fps ts mono/EoF
> 	11 (3) [-] any 12 614400 B 57.131978 57.131994 60.010 fps ts mono/EoF
> 	12 (4) [-] any 13 614400 B 57.148645 57.148664 59.999 fps ts mono/EoF
> 	13 (5) [-] any 14 614400 B 57.165310 57.165328 60.006 fps ts mono/EoF
> 	14 (6) [-] any 15 614400 B 57.181977 57.181996 59.999 fps ts mono/EoF
> 	15 (7) [-] any 16 614400 B 57.198642 57.198660 60.006 fps ts mono/EoF
> 
> Changelog:
> 
> v2->v3:
> 
> - Eugen (thanks) reported regression in DVP mode :(
>    To maintain the DVP timings un-changed in this version the mode definition now
>    looks like
> 
> 		/* 640x480 */
> 		.id		= OV5640_MODE_VGA_640_480,
> 		.dn_mode	= SUBSAMPLING,
> 		.pixel_rate	= OV5640_PIXEL_RATE_48M,
> 		.width		= 640,
> 		.height		= 480,
> 		.dvp_timings = {
> 			.analog_crop = {
> 				.left	= 0,
> 				.top	= 4,
> 				.width	= 2624,
> 				.height	= 1944,
> 			},
> 			.crop = {
> 				.left	= 16,
> 				.top	= 6,
> 				.width	= 640,
> 				.height	= 480,
> 			},
> 			.htot		= 1896,
> 			.vblank_def	= 600,
> 			.max_fps	= OV5640_60_FPS
> 		},
> 		.csi2_timings = {
> 			.analog_crop = {
> 				/* Feed the full valid pixel array to the ISP. */
> 				.left	= OV5640_PIXEL_ARRAY_LEFT,
> 				.top	= OV5640_PIXEL_ARRAY_TOP,
> 				.width	= OV5640_PIXEL_ARRAY_WIDTH,
> 				.height	= OV5640_PIXEL_ARRAY_HEIGHT,
> 			},
> 			.crop = {
> 				/* Maintain a minimum digital crop processing margins. */
> 				.left	= 2,
> 				.top	= 4,
> 				.width	= 640,
> 				.height	= 480,
> 			},
> 			.htot		= 1600,
> 			.vblank_def	= 520,
> 		},
> 		.reg_data	= ov5640_setting_low_res,
> 		.reg_data_size	= ARRAY_SIZE(ov5640_setting_low_res),
> 
>    with a .dvp_timings and a .csi2_timings members to separate the two.
>    Is it nice ? No it's not, but it should help maintaining DVP users happy.
> 
>    Eugen: if you are willing to run another test round to confirm if this version
>    does not regress DVP it would be great :)
> 
> - Split image formats between CSI-2 and DVP
> - Remove RGB888 as per the CSIS discussion with Laurent
> - Removed register tables for modes < 720 as they're all equal
> - Minor fixes on Laurent's comments
> - Add Adam's tag
> 
> v1 -> v2:
> - rework the modes definition to process the full pixel array
> - rework get_selection to report the correct BOUND and DEFAULT targets
> - implement init_cfg
> - minor style changes as suggested by Laurent
> - test with 1 data lane
> 
> Jacopo Mondi (27):
>    media: ov5640: Add pixel rate to modes
>    media: ov5604: Re-arrange modes definition
>    media: ov5640: Add ov5640_is_csi2() function
>    media: ov5640: Associate bpp with formats
>    media: ov5640: Add LINK_FREQ control
>    media: ov5640: Update pixel_rate and link_freq
>    media: ov5640: Rework CSI-2 clock tree
>    media: ov5640: Rework timings programming
>    media: ov5640: Fix 720x480 in RGB888 mode
>    media: ov5640: Split DVP and CSI-2 timings
>    media: ov5640: Provide timings accessor
>    media: ov5640: Re-sort per-mode register tables
>    media: ov5640: Remove duplicated mode settings
>    media: ov5640: Remove ov5640_mode_init_data
>    media: ov5640: Add HBLANK control
>    media: ov5640: Add VBLANK control
>    media: ov5640: Change CSI-2 timings to comply with FPS
>    media: ov5640: Implement init_cfg
>    media: ov5640: Implement get_selection
>    media: ov5640: Limit frame_interval to DVP mode only
>    media: ov5640: Register device properties
>    media: ov5640: Add RGB565_1X16 format
>    media: ov5640: Add BGR888 format
>    media: ov5640: Restrict sizes to mbus code
>    media: ov5640: Adjust format to bpp in s_fmt
>    media: ov5640: Split DVP and CSI-2 formats
>    media: ov5640: Move format mux config in format
> 
>   drivers/media/i2c/ov5640.c | 1615 ++++++++++++++++++++++++++----------
>   1 file changed, 1160 insertions(+), 455 deletions(-)
> 
> --
> 2.35.0
> 
> 
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-03-23  9:50   ` Jacopo Mondi
  2022-03-23 10:41     ` Laurent Pinchart
@ 2022-04-07 16:25     ` Hugues FRUCHET - FOSS
  2022-04-08 11:05       ` Sakari Ailus
  1 sibling, 1 reply; 53+ messages in thread
From: Hugues FRUCHET - FOSS @ 2022-04-07 16:25 UTC (permalink / raw)
  To: Jacopo Mondi, Tomi Valkeinen
  Cc: Steve Longerbeam, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Jacopo,

On 3/23/22 10:50 AM, Jacopo Mondi wrote:
> Hi Tomi thanks for testing
> 
> On Wed, Mar 23, 2022 at 10:51:04AM +0200, Tomi Valkeinen wrote:
>> Hi Jacopo,
>>
>> On 24/02/2022 11:42, Jacopo Mondi wrote:
>>> v1:
>>> https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
>>> v2:
>>> https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
>>> v3:
>>> https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
>>> v4:
>>> https://patchwork.linuxtv.org/project/linux-media/list/?series=7389
>>>
>>> A branch for testing based on the most recent media-master is available at
>>> https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
>>
>> I tested these with DRA76 EVM & CAL, using CAL's legacy non-MC mode. It
>> doesn't work. I think there are two problems:
>>
>> - CAL uses mbus codes like MEDIA_BUS_FMT_UYVY8_2X8 for CSI-2, not 1X16.
>> OV5640 used to support 2X8, but now it doesn't.
>>
>> - OV5640 sets the default code to MEDIA_BUS_FMT_UYVY8_2X8, even for CSI-2
>> where it doesn't support MEDIA_BUS_FMT_UYVY8_2X8.
> 
> This might be worth an additional patch that decides what default
> format to use based on the bus type.
> 
>>
>> I'd like to just change CAL and drop the 2X8 support and instead use 1X16,
>> but then any sensor that uses 2X8 would work. So I guess I need to change
>> the code to support both.
>>
>> Anyway, both of those issues might also surface on other platforms, as
>> ov5640 behavior has changed.
>>

I am facing the same "2X8" compatibility problem on ST platforms:
- st-mipid02 CSI-2 to parallel bridge [1] must be enhanced to support 
1X16 formats
- dcmi controller [2] must also be enhanced to support 1X16 and extra 
code to support 1X16 input to 2X8 output (as we only have as input the 
V4L2 format, not the mediabus one)
=> with current code, support with OV5640 is broken.

I feel that your proposal to let OV5640 accept 2X8 then silently switch 
to 1X16 can do the job without breaking dcmi/bridge support but need 
further testing to confirm.

Appart from that I don't really understand the logic behind naming 
"1X16" for CSI-2 serial formats, if "2X8" means 2 bytes to send one 
pixel, I would expect that "1X16" means 1 word to send one pixel (16bits 
wide bus), so how to differentiate 16 bits // code from CSI-2 code ?

For the time being I have reverted this commit in order to test the 
other topics of this patchset.

[1] drivers/media/i2c/st-mipid02.c
[2] drivers/media/platform/stm32/stm32-dcmi.c

> 
> I'm afraid sooner or later this should have happened ?
> 
> I think CSI-2 receivers should be updated, but I share your concerns
> about breaking other platforms.
> 
> On one side we shouldn't be breaking userspace and this change might
> break some assumptions in users' pipeline configuration scripts and
> could prevent drivers that used to work together from being
> compatible at all.
> 
> On the other side we would never be able to change anything at all if
> such a change is expected to happen atomically on all platforms and
> sensors.
> 
> As the change is so trivial I guess it's fair to expect users of
> bridge drivers not compatible with 1X16 to fix them, but I cannot tell
> if it's an acceptable policy or not.
> 
> As Sakari suggested we could also move all CSI-2 transmitters to use 1X16
> and have receivers adjust as soon as someone detects a breakage.
> 
> I can revert the change that restricts the enumerated format to the
> currently in use bus type[1] if desired, but I would prefer receivers
> to adjust when needed. Is this acceptable ?
> 
> Thanks
>    j
> 
> [1] "media: ov5640: Split DVP and CSI-2 formats
> 
>>   Tomi
> 
> 

Best regards,
Hugues.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 20/27] media: ov5640: Limit frame_interval to DVP mode only
  2022-02-24  9:43 ` [PATCH v5 20/27] media: ov5640: Limit frame_interval to DVP mode only Jacopo Mondi
@ 2022-04-07 16:25   ` Hugues FRUCHET - FOSS
  2022-04-08 16:08     ` Jacopo Mondi
  0 siblings, 1 reply; 53+ messages in thread
From: Hugues FRUCHET - FOSS @ 2022-04-07 16:25 UTC (permalink / raw)
  To: Jacopo Mondi, Steve Longerbeam
  Cc: laurent.pinchart, sakari.ailus, hverkuil-cisco, mirela.rabulea,
	xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Jacopo,

On 2/24/22 10:43 AM, Jacopo Mondi wrote:
> In MIPI mode the frame rate control is performed by adjusting the
> frame blankings and the s_frame_interval function is not used anymore.
> 
> Only check for the per-mode supported frame rate in DVP mode and do not
> restrict MIPI mode.
> 
> Disallow enum/s/g_frame_interval if the chip is used in MIPI mode.

This is breaking userspace which set framerate through media-ctl:
media-ctl -d /dev/media0 --set-v4l2 "'ov5640 
1-003c':0[fmt:JPEG_1X8/640x480@1/15 field:none]"
because of unsupported framerate, all the rest is ignored (resolution 
and format).

I can understand use of vblank to tune framerate but I would expect that 
compatibility with frame interval setting is kept, it's far more simple 
for an application to set the frame interval versus finding the right 
vblank to apply (not straightforward...)

On my side I have reverted this patch and added support of both, see 
patch proposal in reply to [PATCH v5 16/27] media: ov5640: Add VBLANK 
control.


> 
> While at it re-indent one function which whose parameters were wrongly
> aligned.
> 
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>   drivers/media/i2c/ov5640.c | 18 ++++++++++++++++--
>   1 file changed, 16 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index baf368a39e0f..6b955163eb4d 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -2005,9 +2005,14 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
>   	     (mode->width != width || mode->height != height)))
>   		return NULL;
>   
> -	/* Check to see if the current mode exceeds the max frame rate */
> +	/*
> +	 * Check to see if the current mode exceeds the max frame rate.
> +	 * Only DVP mode uses the frame rate set by s_frame_interval, MIPI
> +	 * mode controls framerate by setting blankings.
> +	 */
>   	timings = &mode->dvp_timings;
> -	if (ov5640_framerates[fr] > ov5640_framerates[timings->max_fps])
> +	if (!ov5640_is_csi2(sensor) &&
> +	    ov5640_framerates[fr] > ov5640_framerates[timings->max_fps])
>   		return NULL;
>   
>   	return mode;
> @@ -3439,6 +3444,9 @@ static int ov5640_enum_frame_interval(
>   	struct v4l2_fract tpf;
>   	int ret;
>   
> +	if (ov5640_is_csi2(sensor))
> +		return -EINVAL;
> +
>   	if (fie->pad != 0)
>   		return -EINVAL;
>   	if (fie->index >= OV5640_NUM_FRAMERATES)
> @@ -3461,6 +3469,9 @@ static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
>   {
>   	struct ov5640_dev *sensor = to_ov5640_dev(sd);
>   
> +	if (ov5640_is_csi2(sensor))
> +		return -EINVAL;
> +
>   	mutex_lock(&sensor->lock);
>   	fi->interval = sensor->frame_interval;
>   	mutex_unlock(&sensor->lock);
> @@ -3475,6 +3486,9 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
>   	const struct ov5640_mode_info *mode;
>   	int frame_rate, ret = 0;
>   
> +	if (ov5640_is_csi2(sensor))
> +		return -EINVAL;
> +
>   	if (fi->pad != 0)
>   		return -EINVAL;
>   
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 16/27] media: ov5640: Add VBLANK control
  2022-02-24  9:43 ` [PATCH v5 16/27] media: ov5640: Add VBLANK control Jacopo Mondi
@ 2022-04-07 16:25   ` Hugues FRUCHET - FOSS
  2022-04-11 15:42     ` Jacopo Mondi
  0 siblings, 1 reply; 53+ messages in thread
From: Hugues FRUCHET - FOSS @ 2022-04-07 16:25 UTC (permalink / raw)
  To: Jacopo Mondi, Steve Longerbeam
  Cc: laurent.pinchart, sakari.ailus, hverkuil-cisco, mirela.rabulea,
	xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Jacopo,

Patch proposed below to support framerate change both with frame 
interval setting and vblank control.

On 2/24/22 10:43 AM, Jacopo Mondi wrote:
> Add the VBLANK control which allows to select the duration of the
> frame vertical blankings and allows to control the framerate.
> 
> The VBLANK control also modifies the exposure time range, which cannot
> exceed the maximum frame length.
> 
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>   drivers/media/i2c/ov5640.c | 52 ++++++++++++++++++++++++++++++++++++++
>   1 file changed, 52 insertions(+)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 5419c7236348..665a8bcebf09 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -36,6 +36,10 @@
>   #define OV5640_PIXEL_ARRAY_WIDTH	2592
>   #define OV5640_PIXEL_ARRAY_HEIGHT	1944
>   
> +/* FIXME: not documented. */
> +#define OV5640_MIN_VBLANK	24
> +#define OV5640_MAX_VTS		1968

Not enough to support 1024x768@15fps (vblank=2607):
+#define OV5640_MAX_VTS		3375 /* 1024x768@15fps, vblank=2607 */


> +
>   #define OV5640_DEFAULT_SLAVE_ID 0x3c
>   
>   #define OV5640_LINK_RATE_MAX		490000000U
> @@ -321,6 +325,7 @@ struct ov5640_ctrls {
>   	struct v4l2_ctrl *pixel_rate;
>   	struct v4l2_ctrl *link_freq;
>   	struct v4l2_ctrl *hblank;
> +	struct v4l2_ctrl *vblank;
>   	struct {
>   		struct v4l2_ctrl *auto_exp;
>   		struct v4l2_ctrl *exposure;
> @@ -2697,6 +2702,7 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
>   	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
>   	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
>   	const struct ov5640_timings *timings;
> +	s32 exposure_val, exposure_max;
>   	unsigned int hblank;
>   	unsigned int i = 0;
>   	u32 pixel_rate;
> @@ -2755,6 +2761,19 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
>   	__v4l2_ctrl_modify_range(sensor->ctrls.hblank,
>   				 hblank, hblank, 1, hblank);
>   
> +	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
> +				 OV5640_MAX_VTS - mode->height, 1,
> +				 timings->vblank_def);
> +	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
> +
> +	exposure_max = timings->crop.height + timings->vblank_def - 4;
> +	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
> +			       sensor->ctrls.exposure->minimum,
> +			       exposure_max);
> +	__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
> +				 sensor->ctrls.exposure->minimum,
> +				 exposure_max, 1, exposure_val);
> +

+	vblank = timings->vblank_def;
+
+	if (sensor->current_fr != timings->def_fps) {
+		/* Compute the blanking according to the required framerate */
+
+		int fie_num = sensor->frame_interval.numerator;
+		int fie_denom = sensor->frame_interval.denominator;
+
+		vblank = ((fie_num * pixel_rate / fie_denom) / timings->htot) -
+			 mode->height;
+	}
+
  	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
  				 OV5640_MAX_VTS - mode->height, 1,
-				 timings->vblank_def);
-	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
+				 vblank);
+	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, vblank);

-	exposure_max = timings->crop.height + timings->vblank_def - 4;
+	exposure_max = timings->crop.height + vblank - 4;
  	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
  			       sensor->ctrls.exposure->minimum,
  			       exposure_max);



@@ -3606,8 +3636,7 @@ static int ov5640_s_frame_interval(struct 
v4l2_subdev *sd,
  		sensor->current_mode = mode;
  		sensor->pending_mode_change = true;

-		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
-					 ov5640_calc_pixel_rate(sensor));
+		ov5640_update_pixel_rate(sensor);
  	}
  out:
  	mutex_unlock(&sensor->lock);


Added def_fps (default framerate) in order to known when using 
vblank_def and when computing it from frame interval:


@@ -383,6 +383,8 @@ struct ov5640_timings {
  	u32 vblank_def;
  	/* DVP only; ignored in MIPI mode. */
  	u32 max_fps;
+	/* CSI-2 only; default fps for default blanking */
+	u32 def_fps;
  };

@@ -719,6 +722,7 @@ static const struct ov5640_mode_info 
ov5640_mode_data[OV5640_NUM_MODES] = {
  			},
  			.htot		= 1600,
  			.vblank_def	= 878,
+			.def_fps	= OV5640_30_FPS
[...]
@@ -1108,6 +1120,7 @@ static const struct ov5640_mode_info 
ov5640_mode_data[OV5640_NUM_MODES] = {
  			},
  			.htot		= 2844,
  			.vblank_def	= 24,
+			.def_fps	= OV5640_15_FPS
  		},


>   	return 0;
>   }
>   
> @@ -3127,6 +3146,15 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
>   			      (BIT(2) | BIT(1)) : 0);
>   }
>   
> +static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value)
> +{
> +	const struct ov5640_mode_info *mode = sensor->current_mode;
> +
> +	/* Update the VTOT timing register value. */
> +	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
> +				  mode->height + value);
> +}
> +
>   static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
>   {
>   	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
> @@ -3157,10 +3185,25 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
>   {
>   	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
>   	struct ov5640_dev *sensor = to_ov5640_dev(sd);
> +	const struct ov5640_mode_info *mode = sensor->current_mode;
> +	const struct ov5640_timings *timings;
> +	unsigned int exp_max;
>   	int ret;
>   
>   	/* v4l2_ctrl_lock() locks our own mutex */
>   
> +	switch (ctrl->id) {
> +	case V4L2_CID_VBLANK:
> +		/* Update the exposure range to the newly programmed vblank. */
> +		timings = ov5640_timings(sensor, mode);
> +		exp_max = mode->height + ctrl->val - 4;
> +		__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
> +					 sensor->ctrls.exposure->minimum,
> +					 exp_max, sensor->ctrls.exposure->step,
> +					 timings->vblank_def);
> +		break;
> +	}
> +
>   	/*
>   	 * If the device is not powered up by the host driver do
>   	 * not apply any controls to H/W at this time. Instead
> @@ -3200,6 +3243,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
>   	case V4L2_CID_VFLIP:
>   		ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
>   		break;
> +	case V4L2_CID_VBLANK:
> +		ret = ov5640_set_ctrl_vblank(sensor, ctrl->val);
> +		break;
>   	default:
>   		ret = -EINVAL;
>   		break;
> @@ -3220,6 +3266,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
>   	struct ov5640_ctrls *ctrls = &sensor->ctrls;
>   	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
>   	const struct ov5640_timings *timings;
> +	unsigned int max_vblank;
>   	unsigned int hblank;
>   	int ret;
>   
> @@ -3245,6 +3292,11 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
>   	ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank,
>   					  hblank, 1, hblank);
>   
> +	max_vblank = OV5640_MAX_VTS - mode->height;
> +	ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
> +					  OV5640_MIN_VBLANK, max_vblank,
> +					  1, timings->vblank_def);
> +
>   	/* Auto/manual white balance */
>   	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
>   					   V4L2_CID_AUTO_WHITE_BALANCE,
> 

Hugues.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 06/27] media: ov5640: Update pixel_rate and link_freq
  2022-02-24  9:42 ` [PATCH v5 06/27] media: ov5640: Update pixel_rate and link_freq Jacopo Mondi
@ 2022-04-07 16:25   ` Hugues FRUCHET - FOSS
  2022-04-11 16:46     ` Jacopo Mondi
  0 siblings, 1 reply; 53+ messages in thread
From: Hugues FRUCHET - FOSS @ 2022-04-07 16:25 UTC (permalink / raw)
  To: Jacopo Mondi, Steve Longerbeam
  Cc: laurent.pinchart, sakari.ailus, hverkuil-cisco, mirela.rabulea,
	xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Jacopo,

Issue with link frequency value reported, see below.

On 2/24/22 10:42 AM, Jacopo Mondi wrote:
> After having set a new format re-calculate the pixel_rate and link_freq
> control values and update them when in MIPI mode.
> 
> Take into account the limitation of the link frequency having to be
> strictly smaller than 1GHz when computing the desired link_freq, and
> adjust the resulting pixel_rate acounting for the clock tree
> configuration.
> 
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>   drivers/media/i2c/ov5640.c | 66 ++++++++++++++++++++++++++++++++++++--
>   1 file changed, 64 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index f9763edf2422..791694bfed41 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -31,6 +31,8 @@
>   
>   #define OV5640_DEFAULT_SLAVE_ID 0x3c
>   
> +#define OV5640_LINK_RATE_MAX		490000000U
> +
>   #define OV5640_REG_SYS_RESET02		0x3002
>   #define OV5640_REG_SYS_CLOCK_ENABLE02	0x3006
>   #define OV5640_REG_SYS_CTRL0		0x3008
> @@ -2412,6 +2414,66 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
>   	return 0;
>   }
>   
> +static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
> +{
> +	const struct ov5640_mode_info *mode = sensor->current_mode;
> +	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
> +	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
> +	unsigned int i = 0;
> +	u32 pixel_rate;
> +	s64 link_freq;
> +	u32 num_lanes;
> +	u32 bpp;
> +
> +	/*
> +	 * Update the pixel rate control value.
> +	 *
> +	 * For DVP mode, maintain the pixel rate calculation using fixed FPS.
> +	 */
> +	if (!ov5640_is_csi2(sensor)) {
> +		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
> +					 ov5640_calc_pixel_rate(sensor));
> +
> +		return 0;
> +	}
> +
> +	/*
> +	 * The MIPI CSI-2 link frequency should comply with the CSI-2
> +	 * specification and be lower than 1GHz.
> +	 *
> +	 * Start from the suggested pixel_rate for the current mode and
> +	 * progressively slow it down if it exceeds 1GHz.
> +	 */
> +	num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes;
> +	bpp = ov5640_code_to_bpp(fmt->code);
> +	do {
> +		pixel_rate = ov5640_pixel_rates[pixel_rate_id];
> +		link_freq = pixel_rate * bpp / (2 * num_lanes);
> +	} while (link_freq >= 1000000000U &&
> +		 ++pixel_rate_id < OV5640_NUM_PIXEL_RATES);
> +
> +	/*
> +	 * Higher link rates require the clock tree to be programmed with
> +	 * 'mipi_div' = 1; this has the effect of halving the actual output
> +	 * pixel rate in the MIPI domain.
> +	 *
> +	 * Adjust the pixel rate control value to report it correctly to
> +	 * userspace.
> +	 */
> +	if (link_freq > OV5640_LINK_RATE_MAX)
> +		pixel_rate /= 2;

Need to divide also the link_frequency (st-mipid02 bridge interfacing is 
broken here). Patch proposal below:

+	sensor->current_link_freq = link_freq;
+
+	 * Adjust the pixel rate & link frequency control value to report it
+	 * correctly to userspace.
  	 */
-	if (link_freq > OV5640_LINK_RATE_MAX)
+	if (link_freq > OV5640_LINK_RATE_MAX) {
  		pixel_rate /= 2;
+		link_freq /= 2;
+	}


Doing so we cannot relay anymore on link_frequency control reading in 
ov5640_set_mipi_pclk(), use current_link_freq variable instead

@@ -1440,7 +1453,7 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev 
*sensor)
  	int ret;

  	/* Use the link freq computed at ov5640_update_pixel_rate() time. */
-	link_freq = ov5640_csi2_link_freqs[sensor->ctrls.link_freq->cur.val];
+	link_freq = sensor->current_link_freq;

@@ -3782,6 +3811,7 @@ static int ov5640_probe(struct i2c_client *client)
  	sensor->current_mode =
  		&ov5640_mode_data[OV5640_MODE_VGA_640_480];
  	sensor->last_mode = sensor->current_mode;
+	sensor->current_link_freq = OV5640_DEFAULT_LINK_FREQ;




> +
> +	for (i = 0; i < ARRAY_SIZE(ov5640_csi2_link_freqs); ++i) {
> +		if (ov5640_csi2_link_freqs[i] == link_freq)
> +			break;
> +	}
> +
> +	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, pixel_rate);
> +	__v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, i);
> +
> +	return 0;
> +}
> +
>   static int ov5640_set_fmt(struct v4l2_subdev *sd,
>   			  struct v4l2_subdev_state *sd_state,
>   			  struct v4l2_subdev_format *format)
> @@ -2451,8 +2513,8 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
>   	/* update format even if code is unchanged, resolution might change */
>   	sensor->fmt = *mbus_fmt;
>   
> -	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
> -				 ov5640_calc_pixel_rate(sensor));
> +	ov5640_update_pixel_rate(sensor);
> +
>   out:
>   	mutex_unlock(&sensor->lock);
>   	return ret;
> 

Hugues.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-04-07 16:25     ` Hugues FRUCHET - FOSS
@ 2022-04-08 11:05       ` Sakari Ailus
  2022-04-26 12:32         ` Hugues FRUCHET - FOSS
  0 siblings, 1 reply; 53+ messages in thread
From: Sakari Ailus @ 2022-04-08 11:05 UTC (permalink / raw)
  To: Hugues FRUCHET - FOSS
  Cc: Jacopo Mondi, Tomi Valkeinen, Steve Longerbeam, laurent.pinchart,
	hverkuil-cisco, mirela.rabulea, xavier.roumegue, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Hugues,

On Thu, Apr 07, 2022 at 06:25:11PM +0200, Hugues FRUCHET - FOSS wrote:
> Hi Jacopo,
> 
> On 3/23/22 10:50 AM, Jacopo Mondi wrote:
> > Hi Tomi thanks for testing
> > 
> > On Wed, Mar 23, 2022 at 10:51:04AM +0200, Tomi Valkeinen wrote:
> > > Hi Jacopo,
> > > 
> > > On 24/02/2022 11:42, Jacopo Mondi wrote:
> > > > v1:
> > > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
> > > > v2:
> > > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
> > > > v3:
> > > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
> > > > v4:
> > > > https://patchwork.linuxtv.org/project/linux-media/list/?series=7389
> > > > 
> > > > A branch for testing based on the most recent media-master is available at
> > > > https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
> > > 
> > > I tested these with DRA76 EVM & CAL, using CAL's legacy non-MC mode. It
> > > doesn't work. I think there are two problems:
> > > 
> > > - CAL uses mbus codes like MEDIA_BUS_FMT_UYVY8_2X8 for CSI-2, not 1X16.
> > > OV5640 used to support 2X8, but now it doesn't.
> > > 
> > > - OV5640 sets the default code to MEDIA_BUS_FMT_UYVY8_2X8, even for CSI-2
> > > where it doesn't support MEDIA_BUS_FMT_UYVY8_2X8.
> > 
> > This might be worth an additional patch that decides what default
> > format to use based on the bus type.
> > 
> > > 
> > > I'd like to just change CAL and drop the 2X8 support and instead use 1X16,
> > > but then any sensor that uses 2X8 would work. So I guess I need to change
> > > the code to support both.
> > > 
> > > Anyway, both of those issues might also surface on other platforms, as
> > > ov5640 behavior has changed.
> > > 
> 
> I am facing the same "2X8" compatibility problem on ST platforms:
> - st-mipid02 CSI-2 to parallel bridge [1] must be enhanced to support 1X16
> formats
> - dcmi controller [2] must also be enhanced to support 1X16 and extra code
> to support 1X16 input to 2X8 output (as we only have as input the V4L2
> format, not the mediabus one)
> => with current code, support with OV5640 is broken.
> 
> I feel that your proposal to let OV5640 accept 2X8 then silently switch to
> 1X16 can do the job without breaking dcmi/bridge support but need further
> testing to confirm.
> 
> Appart from that I don't really understand the logic behind naming "1X16"
> for CSI-2 serial formats, if "2X8" means 2 bytes to send one pixel, I would
> expect that "1X16" means 1 word to send one pixel (16bits wide bus), so how
> to differentiate 16 bits // code from CSI-2 code ?

Please see:

<URL:https://hverkuil.home.xs4all.nl/spec/userspace-api/v4l/subdev-formats.html#v4l2-mbus-pixelcode>

I.e. st-mipid02 and dcmi drivers should be fixed.

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 20/27] media: ov5640: Limit frame_interval to DVP mode only
  2022-04-07 16:25   ` Hugues FRUCHET - FOSS
@ 2022-04-08 16:08     ` Jacopo Mondi
  0 siblings, 0 replies; 53+ messages in thread
From: Jacopo Mondi @ 2022-04-08 16:08 UTC (permalink / raw)
  To: Hugues FRUCHET - FOSS
  Cc: Steve Longerbeam, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Hugues,
   thanks very much for testing

On Thu, Apr 07, 2022 at 06:25:25PM +0200, Hugues FRUCHET - FOSS wrote:
> Hi Jacopo,
>
> On 2/24/22 10:43 AM, Jacopo Mondi wrote:
> > In MIPI mode the frame rate control is performed by adjusting the
> > frame blankings and the s_frame_interval function is not used anymore.
> >
> > Only check for the per-mode supported frame rate in DVP mode and do not
> > restrict MIPI mode.
> >
> > Disallow enum/s/g_frame_interval if the chip is used in MIPI mode.
>
> This is breaking userspace which set framerate through media-ctl:
> media-ctl -d /dev/media0 --set-v4l2 "'ov5640
> 1-003c':0[fmt:JPEG_1X8/640x480@1/15 field:none]"
> because of unsupported framerate, all the rest is ignored (resolution and
> format).
>
> I can understand use of vblank to tune framerate but I would expect that
> compatibility with frame interval setting is kept, it's far more simple for
> an application to set the frame interval versus finding the right vblank to
> apply (not straightforward...)

I understand it might seem easier to state what FPS you want instead
of going through calculations, but I think the frame_interval ioctls
are actually mis-leading and should be discouraged for sensor drivers
(and consequentially for userspace).

frame_interval encourages driver developers to fix on a usually limited
set of supported modes, which limits the actual sensor capabilities to
a few pre-defined modes.

Drivers that support frame rate handling through frame_interval
usually do not expose configurable blankings, which has a direct
impact on the maximum achievable exposure time and should be in
control of userspace.

That said, I think it's maintainer's call to decide when moving to a
different API is considered a user-space breakage or not :)

>
> On my side I have reverted this patch and added support of both, see patch
> proposal in reply to [PATCH v5 16/27] media: ov5640: Add VBLANK control.
>
>
> >
> > While at it re-indent one function which whose parameters were wrongly
> > aligned.
> >
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >   drivers/media/i2c/ov5640.c | 18 ++++++++++++++++--
> >   1 file changed, 16 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> > index baf368a39e0f..6b955163eb4d 100644
> > --- a/drivers/media/i2c/ov5640.c
> > +++ b/drivers/media/i2c/ov5640.c
> > @@ -2005,9 +2005,14 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
> >   	     (mode->width != width || mode->height != height)))
> >   		return NULL;
> > -	/* Check to see if the current mode exceeds the max frame rate */
> > +	/*
> > +	 * Check to see if the current mode exceeds the max frame rate.
> > +	 * Only DVP mode uses the frame rate set by s_frame_interval, MIPI
> > +	 * mode controls framerate by setting blankings.
> > +	 */
> >   	timings = &mode->dvp_timings;
> > -	if (ov5640_framerates[fr] > ov5640_framerates[timings->max_fps])
> > +	if (!ov5640_is_csi2(sensor) &&
> > +	    ov5640_framerates[fr] > ov5640_framerates[timings->max_fps])
> >   		return NULL;
> >   	return mode;
> > @@ -3439,6 +3444,9 @@ static int ov5640_enum_frame_interval(
> >   	struct v4l2_fract tpf;
> >   	int ret;
> > +	if (ov5640_is_csi2(sensor))
> > +		return -EINVAL;
> > +
> >   	if (fie->pad != 0)
> >   		return -EINVAL;
> >   	if (fie->index >= OV5640_NUM_FRAMERATES)
> > @@ -3461,6 +3469,9 @@ static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
> >   {
> >   	struct ov5640_dev *sensor = to_ov5640_dev(sd);
> > +	if (ov5640_is_csi2(sensor))
> > +		return -EINVAL;
> > +
> >   	mutex_lock(&sensor->lock);
> >   	fi->interval = sensor->frame_interval;
> >   	mutex_unlock(&sensor->lock);
> > @@ -3475,6 +3486,9 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
> >   	const struct ov5640_mode_info *mode;
> >   	int frame_rate, ret = 0;
> > +	if (ov5640_is_csi2(sensor))
> > +		return -EINVAL;
> > +
> >   	if (fi->pad != 0)
> >   		return -EINVAL;
> >

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 16/27] media: ov5640: Add VBLANK control
  2022-04-07 16:25   ` Hugues FRUCHET - FOSS
@ 2022-04-11 15:42     ` Jacopo Mondi
  2022-04-26 12:53       ` Hugues FRUCHET - FOSS
  0 siblings, 1 reply; 53+ messages in thread
From: Jacopo Mondi @ 2022-04-11 15:42 UTC (permalink / raw)
  To: Hugues FRUCHET - FOSS
  Cc: Steve Longerbeam, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Hugues,

On Thu, Apr 07, 2022 at 06:25:32PM +0200, Hugues FRUCHET - FOSS wrote:
> Hi Jacopo,
>
> Patch proposed below to support framerate change both with frame interval
> setting and vblank control.
>
> On 2/24/22 10:43 AM, Jacopo Mondi wrote:
> > Add the VBLANK control which allows to select the duration of the
> > frame vertical blankings and allows to control the framerate.
> >
> > The VBLANK control also modifies the exposure time range, which cannot
> > exceed the maximum frame length.
> >
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >   drivers/media/i2c/ov5640.c | 52 ++++++++++++++++++++++++++++++++++++++
> >   1 file changed, 52 insertions(+)
> >
> > diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> > index 5419c7236348..665a8bcebf09 100644
> > --- a/drivers/media/i2c/ov5640.c
> > +++ b/drivers/media/i2c/ov5640.c
> > @@ -36,6 +36,10 @@
> >   #define OV5640_PIXEL_ARRAY_WIDTH	2592
> >   #define OV5640_PIXEL_ARRAY_HEIGHT	1944
> > +/* FIXME: not documented. */
> > +#define OV5640_MIN_VBLANK	24
> > +#define OV5640_MAX_VTS		1968
>
> Not enough to support 1024x768@15fps (vblank=2607):
> +#define OV5640_MAX_VTS		3375 /* 1024x768@15fps, vblank=2607 */

I guess this applies to all modes, as 1024x768 has the same htot and
vtot as lower resolution modes (just an higher default frame rate as
it proved to be more stable for high-freq capture in my testing).

Indeed to slow FPS down either blankings are enlarged or the pixel
rate is reduced. I reported in the cover letter that to increase the
frame rate (for example VGA@60Hz) the pixel rate should be increased.

As the pixel rate is a RO control, we should allow userspace to
control LINK_FREQ, which is now registered as read-only to do so.

-------------- From cover letter ----------------------------------------

To enable higher FPS the LINK_FREQ control should be made writable to increase
the pixel rate (default for 640x480 is 48MHz)

  640x480 YUYV 60 FPS (pixel_rate = 96 Mhz)

        $ yavta -f YUYV -s 640x480 -c100 --skip 7 /dev/video0
        ...
        9 (1) [-] any 10 614400 B 57.098649 57.098667 59.995 fps ts mono/EoF
        10 (2) [-] any 11 614400 B 57.115314 57.115332 60.006 fps ts mono/EoF

--------------------------------------------------------------------------

To achive slower FPS, the pixel rate can be reduced. In example, to
get 1024x768@15FPS let's reduce the pixel clock to 48Mhz in the driver
(default is 96)

---------------------------------------------------------------------------

        vblank = (10^6/15) * 48 / hblank - height)
               = 919

        # v4l2-ctl -d /dev/v4l-subdev4 -c 0x009e0901=919
        # v4l2-ctl -l -d /dev/v4l-subdev4
              ...
              vertical_blanking 0x009e0901 (int)    : min=24 max=2895 step=1 default=520 value=919
              ...
              pixel_rate 0x009f0902 (int64)  : min=48000000 max=168000000 step=1 default=48000000 value=48000000 flags=read-only
              ...

        # yavta..
        0 (0) [-] any 0 1572864 B 197.054072 197.054091 20.519 fps ts mono/EoF
        1 (1) [-] any 1 1572864 B 197.120665 197.120683 15.017 fps ts mono/EoF
        2 (2) [-] any 2 1572864 B 197.187260 197.187278 15.016 fps ts mono/EoF
        3 (3) [-] any 3 1572864 B 197.253854 197.253873 15.016 fps ts mono/EoF
        4 (0) [-] any 4 1572864 B 197.320449 197.320469 15.016 fps ts mono/EoF
        5 (1) [-] any 5 1572864 B 197.387044 197.387068 15.016 fps ts mono/EoF
        6 (2) [-] any 6 1572864 B 197.453636 197.453659 15.017 fps ts mono/EoF
        7 (3) [-] any 7 1572864 B 197.520232 197.520257 15.016 fps ts mono/EoF
---------------------------------------------------------------------------

Hence I think the real solution would be to make LINK_FREQ
controllable by userspace to enlarge the number of achievable
configurations. I thought it was safer to make LINK_FREQ writable on
top, but can be fast-tracked if desired.

Ofc enlarging VBLANK max is an option as well, unfortunately I haven't
found documented anywhere what the max value is, and depending on the
mode I've seen contradictory results.

Thanks
   j

>
>
> > +
> >   #define OV5640_DEFAULT_SLAVE_ID 0x3c
> >   #define OV5640_LINK_RATE_MAX		490000000U
> > @@ -321,6 +325,7 @@ struct ov5640_ctrls {
> >   	struct v4l2_ctrl *pixel_rate;
> >   	struct v4l2_ctrl *link_freq;
> >   	struct v4l2_ctrl *hblank;
> > +	struct v4l2_ctrl *vblank;
> >   	struct {
> >   		struct v4l2_ctrl *auto_exp;
> >   		struct v4l2_ctrl *exposure;
> > @@ -2697,6 +2702,7 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
> >   	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
> >   	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
> >   	const struct ov5640_timings *timings;
> > +	s32 exposure_val, exposure_max;
> >   	unsigned int hblank;
> >   	unsigned int i = 0;
> >   	u32 pixel_rate;
> > @@ -2755,6 +2761,19 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
> >   	__v4l2_ctrl_modify_range(sensor->ctrls.hblank,
> >   				 hblank, hblank, 1, hblank);
> > +	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
> > +				 OV5640_MAX_VTS - mode->height, 1,
> > +				 timings->vblank_def);
> > +	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
> > +
> > +	exposure_max = timings->crop.height + timings->vblank_def - 4;
> > +	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
> > +			       sensor->ctrls.exposure->minimum,
> > +			       exposure_max);
> > +	__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
> > +				 sensor->ctrls.exposure->minimum,
> > +				 exposure_max, 1, exposure_val);
> > +
>
> +	vblank = timings->vblank_def;
> +
> +	if (sensor->current_fr != timings->def_fps) {
> +		/* Compute the blanking according to the required framerate */
> +
> +		int fie_num = sensor->frame_interval.numerator;
> +		int fie_denom = sensor->frame_interval.denominator;
> +
> +		vblank = ((fie_num * pixel_rate / fie_denom) / timings->htot) -
> +			 mode->height;
> +	}
> +
>  	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
>  				 OV5640_MAX_VTS - mode->height, 1,
> -				 timings->vblank_def);
> -	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
> +				 vblank);
> +	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, vblank);
>
> -	exposure_max = timings->crop.height + timings->vblank_def - 4;
> +	exposure_max = timings->crop.height + vblank - 4;
>  	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
>  			       sensor->ctrls.exposure->minimum,
>  			       exposure_max);
>
>
>
> @@ -3606,8 +3636,7 @@ static int ov5640_s_frame_interval(struct v4l2_subdev
> *sd,
>  		sensor->current_mode = mode;
>  		sensor->pending_mode_change = true;
>
> -		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
> -					 ov5640_calc_pixel_rate(sensor));
> +		ov5640_update_pixel_rate(sensor);
>  	}
>  out:
>  	mutex_unlock(&sensor->lock);
>
>
> Added def_fps (default framerate) in order to known when using vblank_def
> and when computing it from frame interval:
>
>
> @@ -383,6 +383,8 @@ struct ov5640_timings {
>  	u32 vblank_def;
>  	/* DVP only; ignored in MIPI mode. */
>  	u32 max_fps;
> +	/* CSI-2 only; default fps for default blanking */
> +	u32 def_fps;
>  };
>
> @@ -719,6 +722,7 @@ static const struct ov5640_mode_info
> ov5640_mode_data[OV5640_NUM_MODES] = {
>  			},
>  			.htot		= 1600,
>  			.vblank_def	= 878,
> +			.def_fps	= OV5640_30_FPS
> [...]
> @@ -1108,6 +1120,7 @@ static const struct ov5640_mode_info
> ov5640_mode_data[OV5640_NUM_MODES] = {
>  			},
>  			.htot		= 2844,
>  			.vblank_def	= 24,
> +			.def_fps	= OV5640_15_FPS
>  		},
>
>
> >   	return 0;
> >   }
> > @@ -3127,6 +3146,15 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
> >   			      (BIT(2) | BIT(1)) : 0);
> >   }
> > +static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value)
> > +{
> > +	const struct ov5640_mode_info *mode = sensor->current_mode;
> > +
> > +	/* Update the VTOT timing register value. */
> > +	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
> > +				  mode->height + value);
> > +}
> > +
> >   static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> >   {
> >   	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
> > @@ -3157,10 +3185,25 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
> >   {
> >   	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
> >   	struct ov5640_dev *sensor = to_ov5640_dev(sd);
> > +	const struct ov5640_mode_info *mode = sensor->current_mode;
> > +	const struct ov5640_timings *timings;
> > +	unsigned int exp_max;
> >   	int ret;
> >   	/* v4l2_ctrl_lock() locks our own mutex */
> > +	switch (ctrl->id) {
> > +	case V4L2_CID_VBLANK:
> > +		/* Update the exposure range to the newly programmed vblank. */
> > +		timings = ov5640_timings(sensor, mode);
> > +		exp_max = mode->height + ctrl->val - 4;
> > +		__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
> > +					 sensor->ctrls.exposure->minimum,
> > +					 exp_max, sensor->ctrls.exposure->step,
> > +					 timings->vblank_def);
> > +		break;
> > +	}
> > +
> >   	/*
> >   	 * If the device is not powered up by the host driver do
> >   	 * not apply any controls to H/W at this time. Instead
> > @@ -3200,6 +3243,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
> >   	case V4L2_CID_VFLIP:
> >   		ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
> >   		break;
> > +	case V4L2_CID_VBLANK:
> > +		ret = ov5640_set_ctrl_vblank(sensor, ctrl->val);
> > +		break;
> >   	default:
> >   		ret = -EINVAL;
> >   		break;
> > @@ -3220,6 +3266,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
> >   	struct ov5640_ctrls *ctrls = &sensor->ctrls;
> >   	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
> >   	const struct ov5640_timings *timings;
> > +	unsigned int max_vblank;
> >   	unsigned int hblank;
> >   	int ret;
> > @@ -3245,6 +3292,11 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
> >   	ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank,
> >   					  hblank, 1, hblank);
> > +	max_vblank = OV5640_MAX_VTS - mode->height;
> > +	ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
> > +					  OV5640_MIN_VBLANK, max_vblank,
> > +					  1, timings->vblank_def);
> > +
> >   	/* Auto/manual white balance */
> >   	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
> >   					   V4L2_CID_AUTO_WHITE_BALANCE,
> >
>
> Hugues.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 06/27] media: ov5640: Update pixel_rate and link_freq
  2022-04-07 16:25   ` Hugues FRUCHET - FOSS
@ 2022-04-11 16:46     ` Jacopo Mondi
  2022-04-26 13:34       ` Hugues FRUCHET - FOSS
  0 siblings, 1 reply; 53+ messages in thread
From: Jacopo Mondi @ 2022-04-11 16:46 UTC (permalink / raw)
  To: Hugues FRUCHET - FOSS
  Cc: Steve Longerbeam, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Hugues,

On Thu, Apr 07, 2022 at 06:25:57PM +0200, Hugues FRUCHET - FOSS wrote:
> Hi Jacopo,
>
> Issue with link frequency value reported, see below.
>
> On 2/24/22 10:42 AM, Jacopo Mondi wrote:
> > After having set a new format re-calculate the pixel_rate and link_freq
> > control values and update them when in MIPI mode.
> >
> > Take into account the limitation of the link frequency having to be
> > strictly smaller than 1GHz when computing the desired link_freq, and
> > adjust the resulting pixel_rate acounting for the clock tree
> > configuration.
> >
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >   drivers/media/i2c/ov5640.c | 66 ++++++++++++++++++++++++++++++++++++--
> >   1 file changed, 64 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> > index f9763edf2422..791694bfed41 100644
> > --- a/drivers/media/i2c/ov5640.c
> > +++ b/drivers/media/i2c/ov5640.c
> > @@ -31,6 +31,8 @@
> >   #define OV5640_DEFAULT_SLAVE_ID 0x3c
> > +#define OV5640_LINK_RATE_MAX		490000000U
> > +
> >   #define OV5640_REG_SYS_RESET02		0x3002
> >   #define OV5640_REG_SYS_CLOCK_ENABLE02	0x3006
> >   #define OV5640_REG_SYS_CTRL0		0x3008
> > @@ -2412,6 +2414,66 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
> >   	return 0;
> >   }
> > +static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
> > +{
> > +	const struct ov5640_mode_info *mode = sensor->current_mode;
> > +	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
> > +	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
> > +	unsigned int i = 0;
> > +	u32 pixel_rate;
> > +	s64 link_freq;
> > +	u32 num_lanes;
> > +	u32 bpp;
> > +
> > +	/*
> > +	 * Update the pixel rate control value.
> > +	 *
> > +	 * For DVP mode, maintain the pixel rate calculation using fixed FPS.
> > +	 */
> > +	if (!ov5640_is_csi2(sensor)) {
> > +		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
> > +					 ov5640_calc_pixel_rate(sensor));
> > +
> > +		return 0;
> > +	}
> > +
> > +	/*
> > +	 * The MIPI CSI-2 link frequency should comply with the CSI-2
> > +	 * specification and be lower than 1GHz.
> > +	 *
> > +	 * Start from the suggested pixel_rate for the current mode and
> > +	 * progressively slow it down if it exceeds 1GHz.
> > +	 */
> > +	num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes;
> > +	bpp = ov5640_code_to_bpp(fmt->code);
> > +	do {
> > +		pixel_rate = ov5640_pixel_rates[pixel_rate_id];
> > +		link_freq = pixel_rate * bpp / (2 * num_lanes);
> > +	} while (link_freq >= 1000000000U &&
> > +		 ++pixel_rate_id < OV5640_NUM_PIXEL_RATES);
> > +
> > +	/*
> > +	 * Higher link rates require the clock tree to be programmed with
> > +	 * 'mipi_div' = 1; this has the effect of halving the actual output
> > +	 * pixel rate in the MIPI domain.
> > +	 *
> > +	 * Adjust the pixel rate control value to report it correctly to
> > +	 * userspace.
> > +	 */
> > +	if (link_freq > OV5640_LINK_RATE_MAX)
> > +		pixel_rate /= 2;
>
> Need to divide also the link_frequency (st-mipid02 bridge interfacing is
> broken here). Patch proposal below:
>
> +	sensor->current_link_freq = link_freq;
> +
> +	 * Adjust the pixel rate & link frequency control value to report it
> +	 * correctly to userspace.
>  	 */
> -	if (link_freq > OV5640_LINK_RATE_MAX)
> +	if (link_freq > OV5640_LINK_RATE_MAX) {
>  		pixel_rate /= 2;
> +		link_freq /= 2;
> +	}

This has been my headache for a long time and I'm still not 100%
convinced what I have here is the best solution, but at least works
for more much modes than what it used to.

Can I ask how did you get to the conclusion that link_rate should be
halved ? Is your receiver driver complaining ? Have you sampled the
actual link frequency ?

I tried applying your patch and

1) on imx8mp with mipi-csis receiver your patch breaks a few modes but
   still works for most of them. The system seems unstable compared to
   the original version, and sometimes I get hangs/segfauls for high
   resolution modes. The receiver driver is the mipi-csis one [1]

   [1] drivers/media/platform/imx/imx-mipi-csis.c

2) on i.MX6 I spent quite some time debugging why high-res modes do
   not work there with my series, and my understanding is that the i.MX6
   CSI-2 receiver only supports a total bandwidth of 1Gbps/lane, which
   the high res modes of the ov5640 sensor exceeds, having a clock rate
   frequency of 672 MHz.  (Unrelated: the limitation of 1Gbps might be due
   to the fact the i.MX6 receiver implements the v1.0 version of the
   CSI-2 specs, but I found nowhere a confirmation that v1.0 is limited
   to 1Gbps compared to the 1.5Gbps limit of v1.1).

   If I apply your patch I can capture 1080p and full res, but the
   images are crippled. The pixels are repeated multiple times in the final
   image, I cannot tell if that's an issue on the receiver or due to the
   link rate being actually faster than what reported with your change.

Could you on how you got to halve the reported pixel rate ?

Others have tested with other csi-2 receivers which sample link freq
using v4l2_get_link_freq() as well, and they have not reported issues
afaict.

(Please note that using a link_freq that doesn't come from the control
in ov5640_set_mipi_pclk() makes it harder to tune the pixel rate from
userspace to accommodate more configurations, as I suggested we should
do in reply to "[PATCH v5 16/27] media: ov5640: Add VBLANK control"
but it might still be doable).

Thanks a lot for testing!
   j


[2] That's what I have applied

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index faca36dc4187..910b58fb1e08 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -461,6 +461,7 @@ struct ov5640_dev {

        bool pending_mode_change;
        bool streaming;
+       s64 current_link_freq;
 };

 static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
@@ -1439,7 +1440,7 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor)
        int ret;

        /* Use the link frequency computed at ov5640_update_pixel_rate() time. */
-       link_freq = ov5640_csi2_link_freqs[sensor->ctrls.link_freq->cur.val];
+       link_freq = sensor->current_link_freq;

        /*
         * - mipi_div - Additional divider for the MIPI lane clock.
@@ -2836,6 +2837,8 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
        } while (link_freq >= 1000000000U &&
                 ++pixel_rate_id < OV5640_NUM_PIXEL_RATES);

+       sensor->current_link_freq = link_freq;
+
        /*
         * Higher link rates require the clock tree to be programmed with
         * 'mipi_div' = 1; this has the effect of halving the actual output
@@ -2844,8 +2847,10 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
         * Adjust the pixel rate control value to report it correctly to
         * userspace.
         */
-       if (link_freq > OV5640_LINK_RATE_MAX)
+       if (link_freq > OV5640_LINK_RATE_MAX) {
                pixel_rate /= 2;
+               link_freq /= 2;
+       }

        for (i = 0; i < ARRAY_SIZE(ov5640_csi2_link_freqs); ++i) {
                if (ov5640_csi2_link_freqs[i] == link_freq)
@@ -3777,6 +3782,7 @@ static int ov5640_probe(struct i2c_client *client)
        sensor->current_mode =
                &ov5640_mode_data[OV5640_MODE_VGA_640_480];
        sensor->last_mode = sensor->current_mode;
+       sensor->current_link_freq = OV5640_DEFAULT_LINK_FREQ;

        sensor->ae_target = 52;




>
>
> Doing so we cannot relay anymore on link_frequency control reading in
> ov5640_set_mipi_pclk(), use current_link_freq variable instead
>
> @@ -1440,7 +1453,7 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev
> *sensor)
>  	int ret;
>
>  	/* Use the link freq computed at ov5640_update_pixel_rate() time. */
> -	link_freq = ov5640_csi2_link_freqs[sensor->ctrls.link_freq->cur.val];
> +	link_freq = sensor->current_link_freq;
>
> @@ -3782,6 +3811,7 @@ static int ov5640_probe(struct i2c_client *client)
>  	sensor->current_mode =
>  		&ov5640_mode_data[OV5640_MODE_VGA_640_480];
>  	sensor->last_mode = sensor->current_mode;
> +	sensor->current_link_freq = OV5640_DEFAULT_LINK_FREQ;
>
>
>
>
> > +
> > +	for (i = 0; i < ARRAY_SIZE(ov5640_csi2_link_freqs); ++i) {
> > +		if (ov5640_csi2_link_freqs[i] == link_freq)
> > +			break;
> > +	}
> > +
> > +	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, pixel_rate);
> > +	__v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, i);
> > +
> > +	return 0;
> > +}
> > +
> >   static int ov5640_set_fmt(struct v4l2_subdev *sd,
> >   			  struct v4l2_subdev_state *sd_state,
> >   			  struct v4l2_subdev_format *format)
> > @@ -2451,8 +2513,8 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
> >   	/* update format even if code is unchanged, resolution might change */
> >   	sensor->fmt = *mbus_fmt;
> > -	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
> > -				 ov5640_calc_pixel_rate(sensor));
> > +	ov5640_update_pixel_rate(sensor);
> > +
> >   out:
> >   	mutex_unlock(&sensor->lock);
> >   	return ret;
> >
>
> Hugues.

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI
  2022-04-08 11:05       ` Sakari Ailus
@ 2022-04-26 12:32         ` Hugues FRUCHET - FOSS
  0 siblings, 0 replies; 53+ messages in thread
From: Hugues FRUCHET - FOSS @ 2022-04-26 12:32 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Jacopo Mondi, Tomi Valkeinen, Steve Longerbeam, laurent.pinchart,
	hverkuil-cisco, mirela.rabulea, xavier.roumegue, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Sakari,

On 4/8/22 1:05 PM, Sakari Ailus wrote:
> Hi Hugues,
> 
> On Thu, Apr 07, 2022 at 06:25:11PM +0200, Hugues FRUCHET - FOSS wrote:
>> Hi Jacopo,
>>
>> On 3/23/22 10:50 AM, Jacopo Mondi wrote:
>>> Hi Tomi thanks for testing
>>>
>>> On Wed, Mar 23, 2022 at 10:51:04AM +0200, Tomi Valkeinen wrote:
>>>> Hi Jacopo,
>>>>
>>>> On 24/02/2022 11:42, Jacopo Mondi wrote:
>>>>> v1:
>>>>> https://patchwork.linuxtv.org/project/linux-media/list/?series=7249
>>>>> v2:
>>>>> https://patchwork.linuxtv.org/project/linux-media/list/?series=7311
>>>>> v3:
>>>>> https://patchwork.linuxtv.org/project/linux-media/list/?series=7385
>>>>> v4:
>>>>> https://patchwork.linuxtv.org/project/linux-media/list/?series=7389
>>>>>
>>>>> A branch for testing based on the most recent media-master is available at
>>>>> https://git.sr.ht/~jmondi_/linux #jmondi/media-master/ov5640-v5
>>>>
>>>> I tested these with DRA76 EVM & CAL, using CAL's legacy non-MC mode. It
>>>> doesn't work. I think there are two problems:
>>>>
>>>> - CAL uses mbus codes like MEDIA_BUS_FMT_UYVY8_2X8 for CSI-2, not 1X16.
>>>> OV5640 used to support 2X8, but now it doesn't.
>>>>
>>>> - OV5640 sets the default code to MEDIA_BUS_FMT_UYVY8_2X8, even for CSI-2
>>>> where it doesn't support MEDIA_BUS_FMT_UYVY8_2X8.
>>>
>>> This might be worth an additional patch that decides what default
>>> format to use based on the bus type.
>>>
>>>>
>>>> I'd like to just change CAL and drop the 2X8 support and instead use 1X16,
>>>> but then any sensor that uses 2X8 would work. So I guess I need to change
>>>> the code to support both.
>>>>
>>>> Anyway, both of those issues might also surface on other platforms, as
>>>> ov5640 behavior has changed.
>>>>
>>
>> I am facing the same "2X8" compatibility problem on ST platforms:
>> - st-mipid02 CSI-2 to parallel bridge [1] must be enhanced to support 1X16
>> formats
>> - dcmi controller [2] must also be enhanced to support 1X16 and extra code
>> to support 1X16 input to 2X8 output (as we only have as input the V4L2
>> format, not the mediabus one)
>> => with current code, support with OV5640 is broken.
>>
>> I feel that your proposal to let OV5640 accept 2X8 then silently switch to
>> 1X16 can do the job without breaking dcmi/bridge support but need further
>> testing to confirm.
>>
>> Appart from that I don't really understand the logic behind naming "1X16"
>> for CSI-2 serial formats, if "2X8" means 2 bytes to send one pixel, I would
>> expect that "1X16" means 1 word to send one pixel (16bits wide bus), so how
>> to differentiate 16 bits // code from CSI-2 code ?
> 
> Please see:
> 
> <URL:https://hverkuil.home.xs4all.nl/spec/userspace-api/v4l/subdev-formats.html#v4l2-mbus-pixelcode>
> 
> I.e. st-mipid02 and dcmi drivers should be fixed.
> 

OK, so "single clock cycle" convention for serial bus (copy/paste here 
for the sake of clarity):

4.13.3.4.1.1. Media Bus Pixel Codes

The media bus pixel codes document parallel formats. Should the pixel 
data be transported over a serial bus, the media bus pixel code that 
describes a parallel format that transfers a sample on a single clock 
cycle is used. For instance, both MEDIA_BUS_FMT_BGR888_1X24 and 
MEDIA_BUS_FMT_BGR888_3X8 are used on parallel busses for transferring an 
8 bits per sample BGR data, whereas on serial busses the data in this 
format is only referred to using MEDIA_BUS_FMT_BGR888_1X24. This is 
because there is effectively only a single way to transport that format 
on the serial busses.

I'll try to change both dcmi and mipid02 in that way...

Best regards,
Hugues.



^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 16/27] media: ov5640: Add VBLANK control
  2022-04-11 15:42     ` Jacopo Mondi
@ 2022-04-26 12:53       ` Hugues FRUCHET - FOSS
  2022-04-26 13:58         ` Jacopo Mondi
  0 siblings, 1 reply; 53+ messages in thread
From: Hugues FRUCHET - FOSS @ 2022-04-26 12:53 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Steve Longerbeam, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Jacopo,

On 4/11/22 5:42 PM, Jacopo Mondi wrote:
> Hi Hugues,
> 
> On Thu, Apr 07, 2022 at 06:25:32PM +0200, Hugues FRUCHET - FOSS wrote:
>> Hi Jacopo,
>>
>> Patch proposed below to support framerate change both with frame interval
>> setting and vblank control.
>>
>> On 2/24/22 10:43 AM, Jacopo Mondi wrote:
>>> Add the VBLANK control which allows to select the duration of the
>>> frame vertical blankings and allows to control the framerate.
>>>
>>> The VBLANK control also modifies the exposure time range, which cannot
>>> exceed the maximum frame length.
>>>
>>> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>> ---
>>>    drivers/media/i2c/ov5640.c | 52 ++++++++++++++++++++++++++++++++++++++
>>>    1 file changed, 52 insertions(+)
>>>
>>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>>> index 5419c7236348..665a8bcebf09 100644
>>> --- a/drivers/media/i2c/ov5640.c
>>> +++ b/drivers/media/i2c/ov5640.c
>>> @@ -36,6 +36,10 @@
>>>    #define OV5640_PIXEL_ARRAY_WIDTH	2592
>>>    #define OV5640_PIXEL_ARRAY_HEIGHT	1944
>>> +/* FIXME: not documented. */
>>> +#define OV5640_MIN_VBLANK	24
>>> +#define OV5640_MAX_VTS		1968
>>
>> Not enough to support 1024x768@15fps (vblank=2607):
>> +#define OV5640_MAX_VTS		3375 /* 1024x768@15fps, vblank=2607 */
> 
> I guess this applies to all modes, as 1024x768 has the same htot and
> vtot as lower resolution modes (just an higher default frame rate as
> it proved to be more stable for high-freq capture in my testing).
> 
> Indeed to slow FPS down either blankings are enlarged or the pixel
> rate is reduced. I reported in the cover letter that to increase the
> frame rate (for example VGA@60Hz) the pixel rate should be increased.
> 
> As the pixel rate is a RO control, we should allow userspace to
> control LINK_FREQ, which is now registered as read-only to do so.
> 
> -------------- From cover letter ----------------------------------------
> 
> To enable higher FPS the LINK_FREQ control should be made writable to increase
> the pixel rate (default for 640x480 is 48MHz)
> 
>    640x480 YUYV 60 FPS (pixel_rate = 96 Mhz)
> 
>          $ yavta -f YUYV -s 640x480 -c100 --skip 7 /dev/video0
>          ...
>          9 (1) [-] any 10 614400 B 57.098649 57.098667 59.995 fps ts mono/EoF
>          10 (2) [-] any 11 614400 B 57.115314 57.115332 60.006 fps ts mono/EoF
> 
> --------------------------------------------------------------------------
> 
> To achive slower FPS, the pixel rate can be reduced. In example, to
> get 1024x768@15FPS let's reduce the pixel clock to 48Mhz in the driver
> (default is 96)
> 
> ---------------------------------------------------------------------------
> 
>          vblank = (10^6/15) * 48 / hblank - height)
>                 = 919
> 
>          # v4l2-ctl -d /dev/v4l-subdev4 -c 0x009e0901=919
>          # v4l2-ctl -l -d /dev/v4l-subdev4
>                ...
>                vertical_blanking 0x009e0901 (int)    : min=24 max=2895 step=1 default=520 value=919
>                ...
>                pixel_rate 0x009f0902 (int64)  : min=48000000 max=168000000 step=1 default=48000000 value=48000000 flags=read-only
>                ...
> 
>          # yavta..
>          0 (0) [-] any 0 1572864 B 197.054072 197.054091 20.519 fps ts mono/EoF
>          1 (1) [-] any 1 1572864 B 197.120665 197.120683 15.017 fps ts mono/EoF
>          2 (2) [-] any 2 1572864 B 197.187260 197.187278 15.016 fps ts mono/EoF
>          3 (3) [-] any 3 1572864 B 197.253854 197.253873 15.016 fps ts mono/EoF
>          4 (0) [-] any 4 1572864 B 197.320449 197.320469 15.016 fps ts mono/EoF
>          5 (1) [-] any 5 1572864 B 197.387044 197.387068 15.016 fps ts mono/EoF
>          6 (2) [-] any 6 1572864 B 197.453636 197.453659 15.017 fps ts mono/EoF
>          7 (3) [-] any 7 1572864 B 197.520232 197.520257 15.016 fps ts mono/EoF
> ---------------------------------------------------------------------------
> 
> Hence I think the real solution would be to make LINK_FREQ
> controllable by userspace to enlarge the number of achievable
> configurations. I thought it was safer to make LINK_FREQ writable on
> top, but can be fast-tracked if desired.
> 
> Ofc enlarging VBLANK max is an option as well, unfortunately I haven't
> found documented anywhere what the max value is, and depending on the
> mode I've seen contradictory results.


My opinion is that we cannot continue this way increasing more and more 
complexity on application side...
As far as I have understood well, LINK_FREQ control has been introduced 
in order that CSI receivers be informed of the frequency of link so 
pixels sampling goes well, what is the point to expose such hardware 
setting to application ?

For this exact point of 15fps, driver can solve it itself by selecting a 
lower link frequency if vblank is not in the admissible range.

Another option is to continue to use frame interval control, and that 
solve also the VGA@60fps.

> 
> Thanks
>     j
> 
>>
>>
>>> +
>>>    #define OV5640_DEFAULT_SLAVE_ID 0x3c
>>>    #define OV5640_LINK_RATE_MAX		490000000U
>>> @@ -321,6 +325,7 @@ struct ov5640_ctrls {
>>>    	struct v4l2_ctrl *pixel_rate;
>>>    	struct v4l2_ctrl *link_freq;
>>>    	struct v4l2_ctrl *hblank;
>>> +	struct v4l2_ctrl *vblank;
>>>    	struct {
>>>    		struct v4l2_ctrl *auto_exp;
>>>    		struct v4l2_ctrl *exposure;
>>> @@ -2697,6 +2702,7 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
>>>    	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
>>>    	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
>>>    	const struct ov5640_timings *timings;
>>> +	s32 exposure_val, exposure_max;
>>>    	unsigned int hblank;
>>>    	unsigned int i = 0;
>>>    	u32 pixel_rate;
>>> @@ -2755,6 +2761,19 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
>>>    	__v4l2_ctrl_modify_range(sensor->ctrls.hblank,
>>>    				 hblank, hblank, 1, hblank);
>>> +	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
>>> +				 OV5640_MAX_VTS - mode->height, 1,
>>> +				 timings->vblank_def);
>>> +	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
>>> +
>>> +	exposure_max = timings->crop.height + timings->vblank_def - 4;
>>> +	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
>>> +			       sensor->ctrls.exposure->minimum,
>>> +			       exposure_max);
>>> +	__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
>>> +				 sensor->ctrls.exposure->minimum,
>>> +				 exposure_max, 1, exposure_val);
>>> +
>>
>> +	vblank = timings->vblank_def;
>> +
>> +	if (sensor->current_fr != timings->def_fps) {
>> +		/* Compute the blanking according to the required framerate */
>> +
>> +		int fie_num = sensor->frame_interval.numerator;
>> +		int fie_denom = sensor->frame_interval.denominator;
>> +
>> +		vblank = ((fie_num * pixel_rate / fie_denom) / timings->htot) -
>> +			 mode->height;
>> +	}
>> +
>>   	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
>>   				 OV5640_MAX_VTS - mode->height, 1,
>> -				 timings->vblank_def);
>> -	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
>> +				 vblank);
>> +	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, vblank);
>>
>> -	exposure_max = timings->crop.height + timings->vblank_def - 4;
>> +	exposure_max = timings->crop.height + vblank - 4;
>>   	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
>>   			       sensor->ctrls.exposure->minimum,
>>   			       exposure_max);
>>
>>
>>
>> @@ -3606,8 +3636,7 @@ static int ov5640_s_frame_interval(struct v4l2_subdev
>> *sd,
>>   		sensor->current_mode = mode;
>>   		sensor->pending_mode_change = true;
>>
>> -		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
>> -					 ov5640_calc_pixel_rate(sensor));
>> +		ov5640_update_pixel_rate(sensor);
>>   	}
>>   out:
>>   	mutex_unlock(&sensor->lock);
>>
>>
>> Added def_fps (default framerate) in order to known when using vblank_def
>> and when computing it from frame interval:
>>
>>
>> @@ -383,6 +383,8 @@ struct ov5640_timings {
>>   	u32 vblank_def;
>>   	/* DVP only; ignored in MIPI mode. */
>>   	u32 max_fps;
>> +	/* CSI-2 only; default fps for default blanking */
>> +	u32 def_fps;
>>   };
>>
>> @@ -719,6 +722,7 @@ static const struct ov5640_mode_info
>> ov5640_mode_data[OV5640_NUM_MODES] = {
>>   			},
>>   			.htot		= 1600,
>>   			.vblank_def	= 878,
>> +			.def_fps	= OV5640_30_FPS
>> [...]
>> @@ -1108,6 +1120,7 @@ static const struct ov5640_mode_info
>> ov5640_mode_data[OV5640_NUM_MODES] = {
>>   			},
>>   			.htot		= 2844,
>>   			.vblank_def	= 24,
>> +			.def_fps	= OV5640_15_FPS
>>   		},
>>
>>
>>>    	return 0;
>>>    }
>>> @@ -3127,6 +3146,15 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
>>>    			      (BIT(2) | BIT(1)) : 0);
>>>    }
>>> +static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value)
>>> +{
>>> +	const struct ov5640_mode_info *mode = sensor->current_mode;
>>> +
>>> +	/* Update the VTOT timing register value. */
>>> +	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
>>> +				  mode->height + value);
>>> +}
>>> +
>>>    static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
>>>    {
>>>    	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
>>> @@ -3157,10 +3185,25 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
>>>    {
>>>    	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
>>>    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
>>> +	const struct ov5640_mode_info *mode = sensor->current_mode;
>>> +	const struct ov5640_timings *timings;
>>> +	unsigned int exp_max;
>>>    	int ret;
>>>    	/* v4l2_ctrl_lock() locks our own mutex */
>>> +	switch (ctrl->id) {
>>> +	case V4L2_CID_VBLANK:
>>> +		/* Update the exposure range to the newly programmed vblank. */
>>> +		timings = ov5640_timings(sensor, mode);
>>> +		exp_max = mode->height + ctrl->val - 4;
>>> +		__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
>>> +					 sensor->ctrls.exposure->minimum,
>>> +					 exp_max, sensor->ctrls.exposure->step,
>>> +					 timings->vblank_def);
>>> +		break;
>>> +	}
>>> +
>>>    	/*
>>>    	 * If the device is not powered up by the host driver do
>>>    	 * not apply any controls to H/W at this time. Instead
>>> @@ -3200,6 +3243,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
>>>    	case V4L2_CID_VFLIP:
>>>    		ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
>>>    		break;
>>> +	case V4L2_CID_VBLANK:
>>> +		ret = ov5640_set_ctrl_vblank(sensor, ctrl->val);
>>> +		break;
>>>    	default:
>>>    		ret = -EINVAL;
>>>    		break;
>>> @@ -3220,6 +3266,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
>>>    	struct ov5640_ctrls *ctrls = &sensor->ctrls;
>>>    	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
>>>    	const struct ov5640_timings *timings;
>>> +	unsigned int max_vblank;
>>>    	unsigned int hblank;
>>>    	int ret;
>>> @@ -3245,6 +3292,11 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
>>>    	ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank,
>>>    					  hblank, 1, hblank);
>>> +	max_vblank = OV5640_MAX_VTS - mode->height;
>>> +	ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
>>> +					  OV5640_MIN_VBLANK, max_vblank,
>>> +					  1, timings->vblank_def);
>>> +
>>>    	/* Auto/manual white balance */
>>>    	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
>>>    					   V4L2_CID_AUTO_WHITE_BALANCE,
>>>
>>
>> Hugues.

Best regards,

Hugues.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 06/27] media: ov5640: Update pixel_rate and link_freq
  2022-04-11 16:46     ` Jacopo Mondi
@ 2022-04-26 13:34       ` Hugues FRUCHET - FOSS
  0 siblings, 0 replies; 53+ messages in thread
From: Hugues FRUCHET - FOSS @ 2022-04-26 13:34 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Steve Longerbeam, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Jacopo,

On 4/11/22 6:46 PM, Jacopo Mondi wrote:
> Hi Hugues,
> 
> On Thu, Apr 07, 2022 at 06:25:57PM +0200, Hugues FRUCHET - FOSS wrote:
>> Hi Jacopo,
>>
>> Issue with link frequency value reported, see below.
>>
>> On 2/24/22 10:42 AM, Jacopo Mondi wrote:
>>> After having set a new format re-calculate the pixel_rate and link_freq
>>> control values and update them when in MIPI mode.
>>>
>>> Take into account the limitation of the link frequency having to be
>>> strictly smaller than 1GHz when computing the desired link_freq, and
>>> adjust the resulting pixel_rate acounting for the clock tree
>>> configuration.
>>>
>>> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>> ---
>>>    drivers/media/i2c/ov5640.c | 66 ++++++++++++++++++++++++++++++++++++--
>>>    1 file changed, 64 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>>> index f9763edf2422..791694bfed41 100644
>>> --- a/drivers/media/i2c/ov5640.c
>>> +++ b/drivers/media/i2c/ov5640.c
>>> @@ -31,6 +31,8 @@
>>>    #define OV5640_DEFAULT_SLAVE_ID 0x3c
>>> +#define OV5640_LINK_RATE_MAX		490000000U
>>> +
>>>    #define OV5640_REG_SYS_RESET02		0x3002
>>>    #define OV5640_REG_SYS_CLOCK_ENABLE02	0x3006
>>>    #define OV5640_REG_SYS_CTRL0		0x3008
>>> @@ -2412,6 +2414,66 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
>>>    	return 0;
>>>    }
>>> +static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
>>> +{
>>> +	const struct ov5640_mode_info *mode = sensor->current_mode;
>>> +	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
>>> +	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
>>> +	unsigned int i = 0;
>>> +	u32 pixel_rate;
>>> +	s64 link_freq;
>>> +	u32 num_lanes;
>>> +	u32 bpp;
>>> +
>>> +	/*
>>> +	 * Update the pixel rate control value.
>>> +	 *
>>> +	 * For DVP mode, maintain the pixel rate calculation using fixed FPS.
>>> +	 */
>>> +	if (!ov5640_is_csi2(sensor)) {
>>> +		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
>>> +					 ov5640_calc_pixel_rate(sensor));
>>> +
>>> +		return 0;
>>> +	}
>>> +
>>> +	/*
>>> +	 * The MIPI CSI-2 link frequency should comply with the CSI-2
>>> +	 * specification and be lower than 1GHz.
>>> +	 *
>>> +	 * Start from the suggested pixel_rate for the current mode and
>>> +	 * progressively slow it down if it exceeds 1GHz.
>>> +	 */
>>> +	num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes;
>>> +	bpp = ov5640_code_to_bpp(fmt->code);
>>> +	do {
>>> +		pixel_rate = ov5640_pixel_rates[pixel_rate_id];
>>> +		link_freq = pixel_rate * bpp / (2 * num_lanes);
>>> +	} while (link_freq >= 1000000000U &&
>>> +		 ++pixel_rate_id < OV5640_NUM_PIXEL_RATES);
>>> +
>>> +	/*
>>> +	 * Higher link rates require the clock tree to be programmed with
>>> +	 * 'mipi_div' = 1; this has the effect of halving the actual output
>>> +	 * pixel rate in the MIPI domain.
>>> +	 *
>>> +	 * Adjust the pixel rate control value to report it correctly to
>>> +	 * userspace.
>>> +	 */
>>> +	if (link_freq > OV5640_LINK_RATE_MAX)
>>> +		pixel_rate /= 2;
>>
>> Need to divide also the link_frequency (st-mipid02 bridge interfacing is
>> broken here). Patch proposal below:
>>
>> +	sensor->current_link_freq = link_freq;
>> +
>> +	 * Adjust the pixel rate & link frequency control value to report it
>> +	 * correctly to userspace.
>>   	 */
>> -	if (link_freq > OV5640_LINK_RATE_MAX)
>> +	if (link_freq > OV5640_LINK_RATE_MAX) {
>>   		pixel_rate /= 2;
>> +		link_freq /= 2;
>> +	}
> 
> This has been my headache for a long time and I'm still not 100%
> convinced what I have here is the best solution, but at least works
> for more much modes than what it used to.
> 
> Can I ask how did you get to the conclusion that link_rate should be
> halved ? Is your receiver driver complaining ? Have you sampled the
> actual link frequency ?

First because nothing was working on my setup when reaching the 
OV5640_LINK_RATE_MAX limit, this concerns resolutions >= 1280x720, 
resolutions below are running fine.

Reading code and coming to this exact line, I was thinking that it was 
an obvious point, because pixel_rate and link_freq are correlated:
  * link_freq = (pixel_rate * bpp) / (2 * data_lanes)
and my hypothesis was that the receiver of your test setup was using the 
pixel_rate CID and not the link_freq, while my mipid02 receiver reads 
first the link_freq CID, so it was making sense...

Here are the pixel_rate/link_freq/pclk_period tested OK on my setup for 
all resolutions with 15 & 30fps:

QCIF 176x144 RGB565 15fps => OK, got 15
QCIF 176x144 YUYV 15fps   => OK, got 15
QCIF 176x144 JPEG 15fps   => OK, got 15
rate=48000000, freq=192000000, mipi_div=2, pclk_period=20, htot=1600, 
height=144, vblank=1856

QCIF 176x144 RGB565 30fps => OK, got 30
QCIF 176x144 YUYV 30fps   => OK, got 30
QCIF 176x144 JPEG 30fps   => OK, got 30
rate=48000000, freq=192000000, mipi_div=2, pclk_period=20, htot=1600, 
height=144, vblank=854

QVGA 320x240 RGB565 15fps => OK, got 15
QVGA 320x240 YUYV 15fps   => OK, got 15
QVGA 320x240 JPEG 15fps   => OK, got 15
rate=48000000, freq=192000000, mipi_div=2, pclk_period=20, htot=1600, 
height=240, vblank=1760

QVGA 320x240 RGB565 30fps => OK, got 30
QVGA 320x240 YUYV 30fps   => OK, got 30
QVGA 320x240 JPEG 30fps   => OK, got 30
rate=48000000, freq=192000000, mipi_div=2, pclk_period=20, htot=1600, 
height=240, vblank=760


VGA 640x480 RGB565 15fps => OK, got 15
VGA 640x480 YUYV 15fps   => OK, got 15
VGA 640x480 JPEG 15fps   => OK, got 15
  rate=48000000, freq=192000000, mipi_div=2, pclk_period=20, htot=1600, 
height=480, vblank=1520

VGA 640x480 RGB565 30fps => OK, got 30
VGA 640x480 YUYV 30fps   => OK, got 30
VGA 640x480 JPEG 30fps   => OK, got 30
rate=48000000, freq=192000000, mipi_div=2, pclk_period=20, htot=1600, 
height=480, vblank=520

480p 720x480 RGB565 15fps => OK, got 15
480p 720x480 YUYV 15fps   => OK, got 15
480p 720x480 JPEG 15fps   => OK, got 15
rate=96000000, freq=384000000, mipi_div=2, pclk_period=10, htot=1896, 
height=480, vblank=2895

480p 720x480 RGB565 30fps => OK, got 30
480p 720x480 YUYV 30fps   => OK, got 30
480p 720x480 JPEG 30fps   => OK, got 30
rate=96000000, freq=384000000, mipi_div=2, pclk_period=10, htot=1896, 
height=480, vblank=1206

XGA 1024x768 RGB565 15fps => OK, got 15
XGA 1024x768 YUYV 15fps   => OK, got 15
XGA 1024x768 JPEG 15fps   => OK, got 15
rate=96000000, freq=384000000, mipi_div=2, pclk_period=10, htot=1896, 
height=768, vblank=2607

XGA 1024x768 RGB565 30fps => OK, got 30
XGA 1024x768 YUYV 30fps   => OK, got 30
XGA 1024x768 JPEG 30fps   => OK, got 30
rate=96000000, freq=384000000, mipi_div=2, pclk_period=10, htot=1896, 
height=768, vblank=918

720p 1280x720 RGB565 15fps => OK, got 15
720p 1280x720 YUYV 15fps   => OK, got 15
720p 1280x720 JPEG 15fps   => OK, got 15
rate=62000000, freq=248000000, mipi_div=1, pclk_period=16, htot=1600, 
height=720, vblank=1863

720p 1280x720 RGB565 30fps => OK, got 30
720p 1280x720 YUYV 30fps   => OK, got 30
720p 1280x720 JPEG 30fps   => OK, got 30
rate=62000000, freq=248000000, mipi_div=1, pclk_period=16, htot=1600, 
height=720, vblank=560

HD 1920x1080 RGB565 15fps => OK, got 15
HD 1920x1080 YUYV 15fps   => OK, got 15
HD 1920x1080 JPEG 15fps   => OK, got 15
rate=74000000, freq=296000000, mipi_div=1, pclk_period=13, htot=2234, 
height=1080, vblank=1128

HD 1920x1080 RGB565 30fps => OK, got 30
HD 1920x1080 YUYV 30fps   => OK, got 30
HD 1920x1080 JPEG 30fps   => OK, got 30
rate=74000000, freq=296000000, mipi_div=1, pclk_period=13, htot=2234, 
height=1080, vblank=24

5Mp 2592x1944 RGB565 15fps => OK, got 15
5Mp 2592x1944 YUYV 15fps   => OK, got 14
5Mp 2592x1944 JPEG 15fps   => OK, got 15
rate=84000000, freq=336000000, mipi_div=1, pclk_period=11, htot=2844, 
height=1944, vblank=24


> 
> I tried applying your patch and
> 
> 1) on imx8mp with mipi-csis receiver your patch breaks a few modes but
>     still works for most of them. The system seems unstable compared to
>     the original version, and sometimes I get hangs/segfauls for high
>     resolution modes. The receiver driver is the mipi-csis one [1]
> 
>     [1] drivers/media/platform/imx/imx-mipi-csis.c

There was a problem with pclk_period computation, please git it a try 
with the new version.

> 
> 2) on i.MX6 I spent quite some time debugging why high-res modes do
>     not work there with my series, and my understanding is that the i.MX6
>     CSI-2 receiver only supports a total bandwidth of 1Gbps/lane, which
>     the high res modes of the ov5640 sensor exceeds, having a clock rate
>     frequency of 672 MHz.  (Unrelated: the limitation of 1Gbps might be due
>     to the fact the i.MX6 receiver implements the v1.0 version of the
>     CSI-2 specs, but I found nowhere a confirmation that v1.0 is limited
>     to 1Gbps compared to the 1.5Gbps limit of v1.1).
> 
>     If I apply your patch I can capture 1080p and full res, but the
>     images are crippled. The pixels are repeated multiple times in the final
>     image, I cannot tell if that's an issue on the receiver or due to the
>     link rate being actually faster than what reported with your change.
> 
> Could you on how you got to halve the reported pixel rate ?
> 
> Others have tested with other csi-2 receivers which sample link freq
> using v4l2_get_link_freq() as well, and they have not reported issues
> afaict.
> 

I feel that we should share the same test procedure in order to confirm 
test verdicts for all known platforms.

> (Please note that using a link_freq that doesn't come from the control
> in ov5640_set_mipi_pclk() makes it harder to tune the pixel rate from
> userspace to accommodate more configurations, as I suggested we should
> do in reply to "[PATCH v5 16/27] media: ov5640: Add VBLANK control"
> but it might still be doable).
> 
> Thanks a lot for testing!
>     j
> 
> 
> [2] That's what I have applied
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index faca36dc4187..910b58fb1e08 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -461,6 +461,7 @@ struct ov5640_dev {
> 
>          bool pending_mode_change;
>          bool streaming;
> +       s64 current_link_freq;
>   };
> 
>   static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
> @@ -1439,7 +1440,7 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor)
>          int ret;
> 
>          /* Use the link frequency computed at ov5640_update_pixel_rate() time. */
> -       link_freq = ov5640_csi2_link_freqs[sensor->ctrls.link_freq->cur.val];
> +       link_freq = sensor->current_link_freq;
> 
>          /*
>           * - mipi_div - Additional divider for the MIPI lane clock.
> @@ -2836,6 +2837,8 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
>          } while (link_freq >= 1000000000U &&
>                   ++pixel_rate_id < OV5640_NUM_PIXEL_RATES);
> 
> +       sensor->current_link_freq = link_freq;
> +
>          /*
>           * Higher link rates require the clock tree to be programmed with
>           * 'mipi_div' = 1; this has the effect of halving the actual output
> @@ -2844,8 +2847,10 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
>           * Adjust the pixel rate control value to report it correctly to
>           * userspace.
>           */
> -       if (link_freq > OV5640_LINK_RATE_MAX)
> +       if (link_freq > OV5640_LINK_RATE_MAX) {
>                  pixel_rate /= 2;
> +               link_freq /= 2;
> +       }
> 
>          for (i = 0; i < ARRAY_SIZE(ov5640_csi2_link_freqs); ++i) {
>                  if (ov5640_csi2_link_freqs[i] == link_freq)
> @@ -3777,6 +3782,7 @@ static int ov5640_probe(struct i2c_client *client)
>          sensor->current_mode =
>                  &ov5640_mode_data[OV5640_MODE_VGA_640_480];
>          sensor->last_mode = sensor->current_mode;
> +       sensor->current_link_freq = OV5640_DEFAULT_LINK_FREQ;
> 
>          sensor->ae_target = 52;
> 
> 
> 
> 
>>
>>
>> Doing so we cannot relay anymore on link_frequency control reading in
>> ov5640_set_mipi_pclk(), use current_link_freq variable instead
>>
>> @@ -1440,7 +1453,7 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev
>> *sensor)
>>   	int ret;
>>
>>   	/* Use the link freq computed at ov5640_update_pixel_rate() time. */
>> -	link_freq = ov5640_csi2_link_freqs[sensor->ctrls.link_freq->cur.val];
>> +	link_freq = sensor->current_link_freq;
>>
>> @@ -3782,6 +3811,7 @@ static int ov5640_probe(struct i2c_client *client)
>>   	sensor->current_mode =
>>   		&ov5640_mode_data[OV5640_MODE_VGA_640_480];
>>   	sensor->last_mode = sensor->current_mode;
>> +	sensor->current_link_freq = OV5640_DEFAULT_LINK_FREQ;
>>
>>
>>
>>
>>> +
>>> +	for (i = 0; i < ARRAY_SIZE(ov5640_csi2_link_freqs); ++i) {
>>> +		if (ov5640_csi2_link_freqs[i] == link_freq)
>>> +			break;
>>> +	}
>>> +
>>> +	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, pixel_rate);
>>> +	__v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, i);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>>    static int ov5640_set_fmt(struct v4l2_subdev *sd,
>>>    			  struct v4l2_subdev_state *sd_state,
>>>    			  struct v4l2_subdev_format *format)
>>> @@ -2451,8 +2513,8 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
>>>    	/* update format even if code is unchanged, resolution might change */
>>>    	sensor->fmt = *mbus_fmt;
>>> -	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
>>> -				 ov5640_calc_pixel_rate(sensor));
>>> +	ov5640_update_pixel_rate(sensor);
>>> +
>>>    out:
>>>    	mutex_unlock(&sensor->lock);
>>>    	return ret;
>>>
>>
>> Hugues.

Hugues.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 16/27] media: ov5640: Add VBLANK control
  2022-04-26 12:53       ` Hugues FRUCHET - FOSS
@ 2022-04-26 13:58         ` Jacopo Mondi
  2022-04-26 14:32           ` Hugues FRUCHET - FOSS
  0 siblings, 1 reply; 53+ messages in thread
From: Jacopo Mondi @ 2022-04-26 13:58 UTC (permalink / raw)
  To: Hugues FRUCHET - FOSS
  Cc: Steve Longerbeam, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Hugues

On Tue, Apr 26, 2022 at 02:53:12PM +0200, Hugues FRUCHET - FOSS wrote:
> Hi Jacopo,
>
> On 4/11/22 5:42 PM, Jacopo Mondi wrote:
> > Hi Hugues,
> >
> > On Thu, Apr 07, 2022 at 06:25:32PM +0200, Hugues FRUCHET - FOSS wrote:
> > > Hi Jacopo,
> > >
> > > Patch proposed below to support framerate change both with frame interval
> > > setting and vblank control.
> > >
> > > On 2/24/22 10:43 AM, Jacopo Mondi wrote:
> > > > Add the VBLANK control which allows to select the duration of the
> > > > frame vertical blankings and allows to control the framerate.
> > > >
> > > > The VBLANK control also modifies the exposure time range, which cannot
> > > > exceed the maximum frame length.
> > > >
> > > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > ---
> > > >    drivers/media/i2c/ov5640.c | 52 ++++++++++++++++++++++++++++++++++++++
> > > >    1 file changed, 52 insertions(+)
> > > >
> > > > diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> > > > index 5419c7236348..665a8bcebf09 100644
> > > > --- a/drivers/media/i2c/ov5640.c
> > > > +++ b/drivers/media/i2c/ov5640.c
> > > > @@ -36,6 +36,10 @@
> > > >    #define OV5640_PIXEL_ARRAY_WIDTH	2592
> > > >    #define OV5640_PIXEL_ARRAY_HEIGHT	1944
> > > > +/* FIXME: not documented. */
> > > > +#define OV5640_MIN_VBLANK	24
> > > > +#define OV5640_MAX_VTS		1968
> > >
> > > Not enough to support 1024x768@15fps (vblank=2607):
> > > +#define OV5640_MAX_VTS		3375 /* 1024x768@15fps, vblank=2607 */
> >
> > I guess this applies to all modes, as 1024x768 has the same htot and
> > vtot as lower resolution modes (just an higher default frame rate as
> > it proved to be more stable for high-freq capture in my testing).
> >
> > Indeed to slow FPS down either blankings are enlarged or the pixel
> > rate is reduced. I reported in the cover letter that to increase the
> > frame rate (for example VGA@60Hz) the pixel rate should be increased.
> >
> > As the pixel rate is a RO control, we should allow userspace to
> > control LINK_FREQ, which is now registered as read-only to do so.
> >
> > -------------- From cover letter ----------------------------------------
> >
> > To enable higher FPS the LINK_FREQ control should be made writable to increase
> > the pixel rate (default for 640x480 is 48MHz)
> >
> >    640x480 YUYV 60 FPS (pixel_rate = 96 Mhz)
> >
> >          $ yavta -f YUYV -s 640x480 -c100 --skip 7 /dev/video0
> >          ...
> >          9 (1) [-] any 10 614400 B 57.098649 57.098667 59.995 fps ts mono/EoF
> >          10 (2) [-] any 11 614400 B 57.115314 57.115332 60.006 fps ts mono/EoF
> >
> > --------------------------------------------------------------------------
> >
> > To achive slower FPS, the pixel rate can be reduced. In example, to
> > get 1024x768@15FPS let's reduce the pixel clock to 48Mhz in the driver
> > (default is 96)
> >
> > ---------------------------------------------------------------------------
> >
> >          vblank = (10^6/15) * 48 / hblank - height)
> >                 = 919
> >
> >          # v4l2-ctl -d /dev/v4l-subdev4 -c 0x009e0901=919
> >          # v4l2-ctl -l -d /dev/v4l-subdev4
> >                ...
> >                vertical_blanking 0x009e0901 (int)    : min=24 max=2895 step=1 default=520 value=919
> >                ...
> >                pixel_rate 0x009f0902 (int64)  : min=48000000 max=168000000 step=1 default=48000000 value=48000000 flags=read-only
> >                ...
> >
> >          # yavta..
> >          0 (0) [-] any 0 1572864 B 197.054072 197.054091 20.519 fps ts mono/EoF
> >          1 (1) [-] any 1 1572864 B 197.120665 197.120683 15.017 fps ts mono/EoF
> >          2 (2) [-] any 2 1572864 B 197.187260 197.187278 15.016 fps ts mono/EoF
> >          3 (3) [-] any 3 1572864 B 197.253854 197.253873 15.016 fps ts mono/EoF
> >          4 (0) [-] any 4 1572864 B 197.320449 197.320469 15.016 fps ts mono/EoF
> >          5 (1) [-] any 5 1572864 B 197.387044 197.387068 15.016 fps ts mono/EoF
> >          6 (2) [-] any 6 1572864 B 197.453636 197.453659 15.017 fps ts mono/EoF
> >          7 (3) [-] any 7 1572864 B 197.520232 197.520257 15.016 fps ts mono/EoF
> > ---------------------------------------------------------------------------
> >
> > Hence I think the real solution would be to make LINK_FREQ
> > controllable by userspace to enlarge the number of achievable
> > configurations. I thought it was safer to make LINK_FREQ writable on
> > top, but can be fast-tracked if desired.
> >
> > Ofc enlarging VBLANK max is an option as well, unfortunately I haven't
> > found documented anywhere what the max value is, and depending on the
> > mode I've seen contradictory results.
>
>
> My opinion is that we cannot continue this way increasing more and more
> complexity on application side...

Without digressing too much on matters of principle, I understand that
simple applications might be frustrated as they would just be happy
with a preview at fixed FPS.

Unfortunately more complex use cases need to control the most
intimate parameters of how the capture system work and an operating
system should enable applications to fully exploit the hw capabilities
instead of limiting them for sake of simplicity. That's where a
camera stack helps abstracting away the most complex details for
simple applications and at the same time allow the more complex ones to
be realized.

> As far as I have understood well, LINK_FREQ control has been introduced in
> order that CSI receivers be informed of the frequency of link so pixels
> sampling goes well, what is the point to expose such hardware setting to
> application ?

What about the example above ? One link freq can accommodate a certain
set of operating modes, not all of them. If that's fine with some
sensors, one which supports formats with bpp in the range of [8-24] and
resolutions that could freely scale in the [8,8] - [2592-1944] range cannot
be expected to be operated in full without changing some of its
operational parameters.

>
> For this exact point of 15fps, driver can solve it itself by selecting a
> lower link frequency if vblank is not in the admissible range.
>

Isn't it simpler, once you know the desired format, resolution
and frame duration, compute the precise configuration of your system,
instead of implementing complex heuristics in kernel space ?

If all drivers have to implement such heuristics we'll end up
with most of them doing the same thing but slightly differently. I
wouldn't be happy about this if I were to maintain the drivers.

In this case the adjustment you proposed is not that complex, but
again pushes for moving complexity to the drivers. I think we should
move in the other direction instead :)

To get there, there need to be consensus and motivation to get past
the idea a complex camera system can be operated in full with a single
gstreamer/yavta command line.

> Another option is to continue to use frame interval control, and that solve
> also the VGA@60fps.
>

You know my opinion about frame_interval. It simply doesn't allow
precise frame duration handling unless drivers are instrumented to
support a lot of different configurations, generally by defining some
more fixed modes (something we have to live with for existing drivers
but something that not be encouraged for new ones imho).

Let's hear from others as well on this!

Thanks
   j

> >
> > Thanks
> >     j
> >
> > >
> > >
> > > > +
> > > >    #define OV5640_DEFAULT_SLAVE_ID 0x3c
> > > >    #define OV5640_LINK_RATE_MAX		490000000U
> > > > @@ -321,6 +325,7 @@ struct ov5640_ctrls {
> > > >    	struct v4l2_ctrl *pixel_rate;
> > > >    	struct v4l2_ctrl *link_freq;
> > > >    	struct v4l2_ctrl *hblank;
> > > > +	struct v4l2_ctrl *vblank;
> > > >    	struct {
> > > >    		struct v4l2_ctrl *auto_exp;
> > > >    		struct v4l2_ctrl *exposure;
> > > > @@ -2697,6 +2702,7 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
> > > >    	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
> > > >    	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
> > > >    	const struct ov5640_timings *timings;
> > > > +	s32 exposure_val, exposure_max;
> > > >    	unsigned int hblank;
> > > >    	unsigned int i = 0;
> > > >    	u32 pixel_rate;
> > > > @@ -2755,6 +2761,19 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
> > > >    	__v4l2_ctrl_modify_range(sensor->ctrls.hblank,
> > > >    				 hblank, hblank, 1, hblank);
> > > > +	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
> > > > +				 OV5640_MAX_VTS - mode->height, 1,
> > > > +				 timings->vblank_def);
> > > > +	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
> > > > +
> > > > +	exposure_max = timings->crop.height + timings->vblank_def - 4;
> > > > +	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
> > > > +			       sensor->ctrls.exposure->minimum,
> > > > +			       exposure_max);
> > > > +	__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
> > > > +				 sensor->ctrls.exposure->minimum,
> > > > +				 exposure_max, 1, exposure_val);
> > > > +
> > >
> > > +	vblank = timings->vblank_def;
> > > +
> > > +	if (sensor->current_fr != timings->def_fps) {
> > > +		/* Compute the blanking according to the required framerate */
> > > +
> > > +		int fie_num = sensor->frame_interval.numerator;
> > > +		int fie_denom = sensor->frame_interval.denominator;
> > > +
> > > +		vblank = ((fie_num * pixel_rate / fie_denom) / timings->htot) -
> > > +			 mode->height;
> > > +	}
> > > +
> > >   	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
> > >   				 OV5640_MAX_VTS - mode->height, 1,
> > > -				 timings->vblank_def);
> > > -	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
> > > +				 vblank);
> > > +	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, vblank);
> > >
> > > -	exposure_max = timings->crop.height + timings->vblank_def - 4;
> > > +	exposure_max = timings->crop.height + vblank - 4;
> > >   	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
> > >   			       sensor->ctrls.exposure->minimum,
> > >   			       exposure_max);
> > >
> > >
> > >
> > > @@ -3606,8 +3636,7 @@ static int ov5640_s_frame_interval(struct v4l2_subdev
> > > *sd,
> > >   		sensor->current_mode = mode;
> > >   		sensor->pending_mode_change = true;
> > >
> > > -		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
> > > -					 ov5640_calc_pixel_rate(sensor));
> > > +		ov5640_update_pixel_rate(sensor);
> > >   	}
> > >   out:
> > >   	mutex_unlock(&sensor->lock);
> > >
> > >
> > > Added def_fps (default framerate) in order to known when using vblank_def
> > > and when computing it from frame interval:
> > >
> > >
> > > @@ -383,6 +383,8 @@ struct ov5640_timings {
> > >   	u32 vblank_def;
> > >   	/* DVP only; ignored in MIPI mode. */
> > >   	u32 max_fps;
> > > +	/* CSI-2 only; default fps for default blanking */
> > > +	u32 def_fps;
> > >   };
> > >
> > > @@ -719,6 +722,7 @@ static const struct ov5640_mode_info
> > > ov5640_mode_data[OV5640_NUM_MODES] = {
> > >   			},
> > >   			.htot		= 1600,
> > >   			.vblank_def	= 878,
> > > +			.def_fps	= OV5640_30_FPS
> > > [...]
> > > @@ -1108,6 +1120,7 @@ static const struct ov5640_mode_info
> > > ov5640_mode_data[OV5640_NUM_MODES] = {
> > >   			},
> > >   			.htot		= 2844,
> > >   			.vblank_def	= 24,
> > > +			.def_fps	= OV5640_15_FPS
> > >   		},
> > >
> > >
> > > >    	return 0;
> > > >    }
> > > > @@ -3127,6 +3146,15 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
> > > >    			      (BIT(2) | BIT(1)) : 0);
> > > >    }
> > > > +static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value)
> > > > +{
> > > > +	const struct ov5640_mode_info *mode = sensor->current_mode;
> > > > +
> > > > +	/* Update the VTOT timing register value. */
> > > > +	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
> > > > +				  mode->height + value);
> > > > +}
> > > > +
> > > >    static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> > > >    {
> > > >    	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
> > > > @@ -3157,10 +3185,25 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
> > > >    {
> > > >    	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
> > > >    	struct ov5640_dev *sensor = to_ov5640_dev(sd);
> > > > +	const struct ov5640_mode_info *mode = sensor->current_mode;
> > > > +	const struct ov5640_timings *timings;
> > > > +	unsigned int exp_max;
> > > >    	int ret;
> > > >    	/* v4l2_ctrl_lock() locks our own mutex */
> > > > +	switch (ctrl->id) {
> > > > +	case V4L2_CID_VBLANK:
> > > > +		/* Update the exposure range to the newly programmed vblank. */
> > > > +		timings = ov5640_timings(sensor, mode);
> > > > +		exp_max = mode->height + ctrl->val - 4;
> > > > +		__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
> > > > +					 sensor->ctrls.exposure->minimum,
> > > > +					 exp_max, sensor->ctrls.exposure->step,
> > > > +					 timings->vblank_def);
> > > > +		break;
> > > > +	}
> > > > +
> > > >    	/*
> > > >    	 * If the device is not powered up by the host driver do
> > > >    	 * not apply any controls to H/W at this time. Instead
> > > > @@ -3200,6 +3243,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
> > > >    	case V4L2_CID_VFLIP:
> > > >    		ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
> > > >    		break;
> > > > +	case V4L2_CID_VBLANK:
> > > > +		ret = ov5640_set_ctrl_vblank(sensor, ctrl->val);
> > > > +		break;
> > > >    	default:
> > > >    		ret = -EINVAL;
> > > >    		break;
> > > > @@ -3220,6 +3266,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
> > > >    	struct ov5640_ctrls *ctrls = &sensor->ctrls;
> > > >    	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
> > > >    	const struct ov5640_timings *timings;
> > > > +	unsigned int max_vblank;
> > > >    	unsigned int hblank;
> > > >    	int ret;
> > > > @@ -3245,6 +3292,11 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
> > > >    	ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank,
> > > >    					  hblank, 1, hblank);
> > > > +	max_vblank = OV5640_MAX_VTS - mode->height;
> > > > +	ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
> > > > +					  OV5640_MIN_VBLANK, max_vblank,
> > > > +					  1, timings->vblank_def);
> > > > +
> > > >    	/* Auto/manual white balance */
> > > >    	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
> > > >    					   V4L2_CID_AUTO_WHITE_BALANCE,
> > > >
> > >
> > > Hugues.
>
> Best regards,
>
> Hugues.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v5 16/27] media: ov5640: Add VBLANK control
  2022-04-26 13:58         ` Jacopo Mondi
@ 2022-04-26 14:32           ` Hugues FRUCHET - FOSS
  0 siblings, 0 replies; 53+ messages in thread
From: Hugues FRUCHET - FOSS @ 2022-04-26 14:32 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Steve Longerbeam, laurent.pinchart, sakari.ailus, hverkuil-cisco,
	mirela.rabulea, xavier.roumegue, tomi.valkeinen, hugues.fruchet,
	prabhakar.mahadev-lad.rj, aford173, festevam, Eugen.Hristev,
	jbrunet, paul.elder, Mauro Carvalho Chehab, linux-media

Hi Jacopo,

On 4/26/22 3:58 PM, Jacopo Mondi wrote:
> Hi Hugues
> 
> On Tue, Apr 26, 2022 at 02:53:12PM +0200, Hugues FRUCHET - FOSS wrote:
>> Hi Jacopo,
>>
>> On 4/11/22 5:42 PM, Jacopo Mondi wrote:
>>> Hi Hugues,
>>>
>>> On Thu, Apr 07, 2022 at 06:25:32PM +0200, Hugues FRUCHET - FOSS wrote:
>>>> Hi Jacopo,
>>>>
>>>> Patch proposed below to support framerate change both with frame interval
>>>> setting and vblank control.
>>>>
>>>> On 2/24/22 10:43 AM, Jacopo Mondi wrote:
>>>>> Add the VBLANK control which allows to select the duration of the
>>>>> frame vertical blankings and allows to control the framerate.
>>>>>
>>>>> The VBLANK control also modifies the exposure time range, which cannot
>>>>> exceed the maximum frame length.
>>>>>
>>>>> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>>> ---
>>>>>     drivers/media/i2c/ov5640.c | 52 ++++++++++++++++++++++++++++++++++++++
>>>>>     1 file changed, 52 insertions(+)
>>>>>
>>>>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>>>>> index 5419c7236348..665a8bcebf09 100644
>>>>> --- a/drivers/media/i2c/ov5640.c
>>>>> +++ b/drivers/media/i2c/ov5640.c
>>>>> @@ -36,6 +36,10 @@
>>>>>     #define OV5640_PIXEL_ARRAY_WIDTH	2592
>>>>>     #define OV5640_PIXEL_ARRAY_HEIGHT	1944
>>>>> +/* FIXME: not documented. */
>>>>> +#define OV5640_MIN_VBLANK	24
>>>>> +#define OV5640_MAX_VTS		1968
>>>>
>>>> Not enough to support 1024x768@15fps (vblank=2607):
>>>> +#define OV5640_MAX_VTS		3375 /* 1024x768@15fps, vblank=2607 */
>>>
>>> I guess this applies to all modes, as 1024x768 has the same htot and
>>> vtot as lower resolution modes (just an higher default frame rate as
>>> it proved to be more stable for high-freq capture in my testing).
>>>
>>> Indeed to slow FPS down either blankings are enlarged or the pixel
>>> rate is reduced. I reported in the cover letter that to increase the
>>> frame rate (for example VGA@60Hz) the pixel rate should be increased.
>>>
>>> As the pixel rate is a RO control, we should allow userspace to
>>> control LINK_FREQ, which is now registered as read-only to do so.
>>>
>>> -------------- From cover letter ----------------------------------------
>>>
>>> To enable higher FPS the LINK_FREQ control should be made writable to increase
>>> the pixel rate (default for 640x480 is 48MHz)
>>>
>>>     640x480 YUYV 60 FPS (pixel_rate = 96 Mhz)
>>>
>>>           $ yavta -f YUYV -s 640x480 -c100 --skip 7 /dev/video0
>>>           ...
>>>           9 (1) [-] any 10 614400 B 57.098649 57.098667 59.995 fps ts mono/EoF
>>>           10 (2) [-] any 11 614400 B 57.115314 57.115332 60.006 fps ts mono/EoF
>>>
>>> --------------------------------------------------------------------------
>>>
>>> To achive slower FPS, the pixel rate can be reduced. In example, to
>>> get 1024x768@15FPS let's reduce the pixel clock to 48Mhz in the driver
>>> (default is 96)
>>>
>>> ---------------------------------------------------------------------------
>>>
>>>           vblank = (10^6/15) * 48 / hblank - height)
>>>                  = 919
>>>
>>>           # v4l2-ctl -d /dev/v4l-subdev4 -c 0x009e0901=919
>>>           # v4l2-ctl -l -d /dev/v4l-subdev4
>>>                 ...
>>>                 vertical_blanking 0x009e0901 (int)    : min=24 max=2895 step=1 default=520 value=919
>>>                 ...
>>>                 pixel_rate 0x009f0902 (int64)  : min=48000000 max=168000000 step=1 default=48000000 value=48000000 flags=read-only
>>>                 ...
>>>
>>>           # yavta..
>>>           0 (0) [-] any 0 1572864 B 197.054072 197.054091 20.519 fps ts mono/EoF
>>>           1 (1) [-] any 1 1572864 B 197.120665 197.120683 15.017 fps ts mono/EoF
>>>           2 (2) [-] any 2 1572864 B 197.187260 197.187278 15.016 fps ts mono/EoF
>>>           3 (3) [-] any 3 1572864 B 197.253854 197.253873 15.016 fps ts mono/EoF
>>>           4 (0) [-] any 4 1572864 B 197.320449 197.320469 15.016 fps ts mono/EoF
>>>           5 (1) [-] any 5 1572864 B 197.387044 197.387068 15.016 fps ts mono/EoF
>>>           6 (2) [-] any 6 1572864 B 197.453636 197.453659 15.017 fps ts mono/EoF
>>>           7 (3) [-] any 7 1572864 B 197.520232 197.520257 15.016 fps ts mono/EoF
>>> ---------------------------------------------------------------------------
>>>
>>> Hence I think the real solution would be to make LINK_FREQ
>>> controllable by userspace to enlarge the number of achievable
>>> configurations. I thought it was safer to make LINK_FREQ writable on
>>> top, but can be fast-tracked if desired.
>>>
>>> Ofc enlarging VBLANK max is an option as well, unfortunately I haven't
>>> found documented anywhere what the max value is, and depending on the
>>> mode I've seen contradictory results.
>>
>>
>> My opinion is that we cannot continue this way increasing more and more
>> complexity on application side...
> 
> Without digressing too much on matters of principle, I understand that
> simple applications might be frustrated as they would just be happy
> with a preview at fixed FPS.
> 
> Unfortunately more complex use cases need to control the most
> intimate parameters of how the capture system work and an operating
> system should enable applications to fully exploit the hw capabilities
> instead of limiting them for sake of simplicity. That's where a
> camera stack helps abstracting away the most complex details for
> simple applications and at the same time allow the more complex ones to
> be realized.
> 
>> As far as I have understood well, LINK_FREQ control has been introduced in
>> order that CSI receivers be informed of the frequency of link so pixels
>> sampling goes well, what is the point to expose such hardware setting to
>> application ?
> 
> What about the example above ? One link freq can accommodate a certain
> set of operating modes, not all of them. If that's fine with some
> sensors, one which supports formats with bpp in the range of [8-24] and
> resolutions that could freely scale in the [8,8] - [2592-1944] range cannot
> be expected to be operated in full without changing some of its
> operational parameters.
> 
>>
>> For this exact point of 15fps, driver can solve it itself by selecting a
>> lower link frequency if vblank is not in the admissible range.
>>
> 
> Isn't it simpler, once you know the desired format, resolution
> and frame duration, compute the precise configuration of your system,
> instead of implementing complex heuristics in kernel space ?
> 
> If all drivers have to implement such heuristics we'll end up
> with most of them doing the same thing but slightly differently. I
> wouldn't be happy about this if I were to maintain the drivers.
> 
> In this case the adjustment you proposed is not that complex, but
> again pushes for moving complexity to the drivers. I think we should
> move in the other direction instead :)
> 
> To get there, there need to be consensus and motivation to get past
> the idea a complex camera system can be operated in full with a single
> gstreamer/yavta command line.
> 
>> Another option is to continue to use frame interval control, and that solve
>> also the VGA@60fps.
>>
> 
> You know my opinion about frame_interval. It simply doesn't allow
> precise frame duration handling unless drivers are instrumented to
> support a lot of different configurations, generally by defining some
> more fixed modes (something we have to live with for existing drivers
> but something that not be encouraged for new ones imho).
> 
> Let's hear from others as well on this!
> 

I'm fine to introduce new features for advanced control, like fine-grain 
tuning of framerate using hblank/vblank/link_freq, but this should not 
break basic control. I feel that it's not a big deal to support both: 
frame interval setting do the job first, then this could be override by 
hblank/vblank/link_freq.
Additionally, we still support frame interval on parallel setup, so 
sounds more coherent for me to keep it also for CSI-2.


> Thanks
>     j
> 
>>>
>>> Thanks
>>>      j
>>>
>>>>
>>>>
>>>>> +
>>>>>     #define OV5640_DEFAULT_SLAVE_ID 0x3c
>>>>>     #define OV5640_LINK_RATE_MAX		490000000U
>>>>> @@ -321,6 +325,7 @@ struct ov5640_ctrls {
>>>>>     	struct v4l2_ctrl *pixel_rate;
>>>>>     	struct v4l2_ctrl *link_freq;
>>>>>     	struct v4l2_ctrl *hblank;
>>>>> +	struct v4l2_ctrl *vblank;
>>>>>     	struct {
>>>>>     		struct v4l2_ctrl *auto_exp;
>>>>>     		struct v4l2_ctrl *exposure;
>>>>> @@ -2697,6 +2702,7 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
>>>>>     	struct v4l2_mbus_framefmt *fmt = &sensor->fmt;
>>>>>     	enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate;
>>>>>     	const struct ov5640_timings *timings;
>>>>> +	s32 exposure_val, exposure_max;
>>>>>     	unsigned int hblank;
>>>>>     	unsigned int i = 0;
>>>>>     	u32 pixel_rate;
>>>>> @@ -2755,6 +2761,19 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
>>>>>     	__v4l2_ctrl_modify_range(sensor->ctrls.hblank,
>>>>>     				 hblank, hblank, 1, hblank);
>>>>> +	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
>>>>> +				 OV5640_MAX_VTS - mode->height, 1,
>>>>> +				 timings->vblank_def);
>>>>> +	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
>>>>> +
>>>>> +	exposure_max = timings->crop.height + timings->vblank_def - 4;
>>>>> +	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
>>>>> +			       sensor->ctrls.exposure->minimum,
>>>>> +			       exposure_max);
>>>>> +	__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
>>>>> +				 sensor->ctrls.exposure->minimum,
>>>>> +				 exposure_max, 1, exposure_val);
>>>>> +
>>>>
>>>> +	vblank = timings->vblank_def;
>>>> +
>>>> +	if (sensor->current_fr != timings->def_fps) {
>>>> +		/* Compute the blanking according to the required framerate */
>>>> +
>>>> +		int fie_num = sensor->frame_interval.numerator;
>>>> +		int fie_denom = sensor->frame_interval.denominator;
>>>> +
>>>> +		vblank = ((fie_num * pixel_rate / fie_denom) / timings->htot) -
>>>> +			 mode->height;
>>>> +	}
>>>> +
>>>>    	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK,
>>>>    				 OV5640_MAX_VTS - mode->height, 1,
>>>> -				 timings->vblank_def);
>>>> -	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, timings->vblank_def);
>>>> +				 vblank);
>>>> +	__v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, vblank);
>>>>
>>>> -	exposure_max = timings->crop.height + timings->vblank_def - 4;
>>>> +	exposure_max = timings->crop.height + vblank - 4;
>>>>    	exposure_val = clamp_t(s32, sensor->ctrls.exposure->val,
>>>>    			       sensor->ctrls.exposure->minimum,
>>>>    			       exposure_max);
>>>>
>>>>
>>>>
>>>> @@ -3606,8 +3636,7 @@ static int ov5640_s_frame_interval(struct v4l2_subdev
>>>> *sd,
>>>>    		sensor->current_mode = mode;
>>>>    		sensor->pending_mode_change = true;
>>>>
>>>> -		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
>>>> -					 ov5640_calc_pixel_rate(sensor));
>>>> +		ov5640_update_pixel_rate(sensor);
>>>>    	}
>>>>    out:
>>>>    	mutex_unlock(&sensor->lock);
>>>>
>>>>
>>>> Added def_fps (default framerate) in order to known when using vblank_def
>>>> and when computing it from frame interval:
>>>>
>>>>
>>>> @@ -383,6 +383,8 @@ struct ov5640_timings {
>>>>    	u32 vblank_def;
>>>>    	/* DVP only; ignored in MIPI mode. */
>>>>    	u32 max_fps;
>>>> +	/* CSI-2 only; default fps for default blanking */
>>>> +	u32 def_fps;
>>>>    };
>>>>
>>>> @@ -719,6 +722,7 @@ static const struct ov5640_mode_info
>>>> ov5640_mode_data[OV5640_NUM_MODES] = {
>>>>    			},
>>>>    			.htot		= 1600,
>>>>    			.vblank_def	= 878,
>>>> +			.def_fps	= OV5640_30_FPS
>>>> [...]
>>>> @@ -1108,6 +1120,7 @@ static const struct ov5640_mode_info
>>>> ov5640_mode_data[OV5640_NUM_MODES] = {
>>>>    			},
>>>>    			.htot		= 2844,
>>>>    			.vblank_def	= 24,
>>>> +			.def_fps	= OV5640_15_FPS
>>>>    		},
>>>>
>>>>
>>>>>     	return 0;
>>>>>     }
>>>>> @@ -3127,6 +3146,15 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
>>>>>     			      (BIT(2) | BIT(1)) : 0);
>>>>>     }
>>>>> +static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value)
>>>>> +{
>>>>> +	const struct ov5640_mode_info *mode = sensor->current_mode;
>>>>> +
>>>>> +	/* Update the VTOT timing register value. */
>>>>> +	return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
>>>>> +				  mode->height + value);
>>>>> +}
>>>>> +
>>>>>     static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
>>>>>     {
>>>>>     	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
>>>>> @@ -3157,10 +3185,25 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
>>>>>     {
>>>>>     	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
>>>>>     	struct ov5640_dev *sensor = to_ov5640_dev(sd);
>>>>> +	const struct ov5640_mode_info *mode = sensor->current_mode;
>>>>> +	const struct ov5640_timings *timings;
>>>>> +	unsigned int exp_max;
>>>>>     	int ret;
>>>>>     	/* v4l2_ctrl_lock() locks our own mutex */
>>>>> +	switch (ctrl->id) {
>>>>> +	case V4L2_CID_VBLANK:
>>>>> +		/* Update the exposure range to the newly programmed vblank. */
>>>>> +		timings = ov5640_timings(sensor, mode);
>>>>> +		exp_max = mode->height + ctrl->val - 4;
>>>>> +		__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
>>>>> +					 sensor->ctrls.exposure->minimum,
>>>>> +					 exp_max, sensor->ctrls.exposure->step,
>>>>> +					 timings->vblank_def);
>>>>> +		break;
>>>>> +	}
>>>>> +
>>>>>     	/*
>>>>>     	 * If the device is not powered up by the host driver do
>>>>>     	 * not apply any controls to H/W at this time. Instead
>>>>> @@ -3200,6 +3243,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
>>>>>     	case V4L2_CID_VFLIP:
>>>>>     		ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
>>>>>     		break;
>>>>> +	case V4L2_CID_VBLANK:
>>>>> +		ret = ov5640_set_ctrl_vblank(sensor, ctrl->val);
>>>>> +		break;
>>>>>     	default:
>>>>>     		ret = -EINVAL;
>>>>>     		break;
>>>>> @@ -3220,6 +3266,7 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
>>>>>     	struct ov5640_ctrls *ctrls = &sensor->ctrls;
>>>>>     	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
>>>>>     	const struct ov5640_timings *timings;
>>>>> +	unsigned int max_vblank;
>>>>>     	unsigned int hblank;
>>>>>     	int ret;
>>>>> @@ -3245,6 +3292,11 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
>>>>>     	ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank,
>>>>>     					  hblank, 1, hblank);
>>>>> +	max_vblank = OV5640_MAX_VTS - mode->height;
>>>>> +	ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
>>>>> +					  OV5640_MIN_VBLANK, max_vblank,
>>>>> +					  1, timings->vblank_def);
>>>>> +
>>>>>     	/* Auto/manual white balance */
>>>>>     	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
>>>>>     					   V4L2_CID_AUTO_WHITE_BALANCE,
>>>>>
>>>>
>>>> Hugues.
>>
>> Best regards,
>>
>> Hugues.

Hugues.

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2022-04-26 14:33 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-24  9:42 [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 01/27] media: ov5640: Add pixel rate to modes Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 02/27] media: ov5604: Re-arrange modes definition Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 03/27] media: ov5640: Add ov5640_is_csi2() function Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 04/27] media: ov5640: Associate bpp with formats Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 05/27] media: ov5640: Add LINK_FREQ control Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 06/27] media: ov5640: Update pixel_rate and link_freq Jacopo Mondi
2022-04-07 16:25   ` Hugues FRUCHET - FOSS
2022-04-11 16:46     ` Jacopo Mondi
2022-04-26 13:34       ` Hugues FRUCHET - FOSS
2022-02-24  9:42 ` [PATCH v5 07/27] media: ov5640: Rework CSI-2 clock tree Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 08/27] media: ov5640: Rework timings programming Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 09/27] media: ov5640: Fix 720x480 in RGB888 mode Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 10/27] media: ov5640: Split DVP and CSI-2 timings Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 11/27] media: ov5640: Provide timings accessor Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 12/27] media: ov5640: Re-sort per-mode register tables Jacopo Mondi
2022-02-24  9:42 ` [PATCH v5 13/27] media: ov5640: Remove duplicated mode settings Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 14/27] media: ov5640: Remove ov5640_mode_init_data Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 15/27] media: ov5640: Add HBLANK control Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 16/27] media: ov5640: Add VBLANK control Jacopo Mondi
2022-04-07 16:25   ` Hugues FRUCHET - FOSS
2022-04-11 15:42     ` Jacopo Mondi
2022-04-26 12:53       ` Hugues FRUCHET - FOSS
2022-04-26 13:58         ` Jacopo Mondi
2022-04-26 14:32           ` Hugues FRUCHET - FOSS
2022-02-24  9:43 ` [PATCH v5 17/27] media: ov5640: Change CSI-2 timings to comply with FPS Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 18/27] media: ov5640: Implement init_cfg Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 19/27] media: ov5640: Implement get_selection Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 20/27] media: ov5640: Limit frame_interval to DVP mode only Jacopo Mondi
2022-04-07 16:25   ` Hugues FRUCHET - FOSS
2022-04-08 16:08     ` Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 21/27] media: ov5640: Register device properties Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 22/27] media: ov5640: Add RGB565_1X16 format Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 23/27] media: ov5640: Add BGR888 format Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 24/27] media: ov5640: Restrict sizes to mbus code Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 25/27] media: ov5640: Adjust format to bpp in s_fmt Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 26/27] media: ov5640: Split DVP and CSI-2 formats Jacopo Mondi
2022-02-24  9:43 ` [PATCH v5 27/27] media: ov5640: Move format mux config in format Jacopo Mondi
2022-03-04  0:09 ` [PATCH v5 00/27] media: ov5640: Rework the clock tree programming for MIPI Sakari Ailus
2022-03-04  8:41   ` Eugen.Hristev
2022-03-04  8:45     ` Jacopo Mondi
2022-03-04  8:52       ` Sakari Ailus
2022-03-23  8:51 ` Tomi Valkeinen
2022-03-23  8:54   ` Sakari Ailus
2022-03-23  8:59   ` Laurent Pinchart
2022-03-23  9:50   ` Jacopo Mondi
2022-03-23 10:41     ` Laurent Pinchart
2022-03-28 14:57       ` Jacopo Mondi
2022-03-28 20:50         ` Sakari Ailus
2022-04-07 16:25     ` Hugues FRUCHET - FOSS
2022-04-08 11:05       ` Sakari Ailus
2022-04-26 12:32         ` Hugues FRUCHET - FOSS
2022-04-07 16:24 ` Hugues FRUCHET - FOSS

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.