All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls
@ 2023-01-31 19:20 Dave Stevenson
  2023-01-31 19:20 ` [PATCH 01/11] media: i2c: imx290: Match kernel coding style on whitespace Dave Stevenson
                   ` (11 more replies)
  0 siblings, 12 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

Hi All

This is a small patch set that fixes a number of issues, adds in support
for the alternate input clock frequency option, and expands the control support
for flips and VBLANK/HBLANK.

My source tree has the 2 patches I've just sent for mono support first, but I
believe the two series should apply in either order.

  Dave

Dave Stevenson (11):
  media: i2c: imx290: Match kernel coding style on whitespace
  media: i2c: imx290: Set the colorspace fields in the format
  media: i2c: imx290: Add V4L2_SUBDEV_FL_HAS_EVENTS and subscribe hooks
  media: i2c: imx290: Fix the pixel rate at 148.5Mpix/s
  media: i2c: imx290: Support 60fps in 2 lane operation
  media: i2c: imx290: Use CSI timings as per datasheet
  media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write
  media: i2c: imx290: Convert V4L2_CID_VBLANK to read/write
  media: i2c: imx290: Remove duplicated write to IMX290_CTRL_07
  media: i2c: imx290: Add support for 74.25MHz external clock
  media: i2c: imx290: Add support for H & V Flips

 drivers/media/i2c/imx290.c | 429 ++++++++++++++++++++++++++++---------
 1 file changed, 333 insertions(+), 96 deletions(-)

-- 
2.34.1


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

* [PATCH 01/11] media: i2c: imx290: Match kernel coding style on whitespace
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02  0:21   ` Laurent Pinchart
  2023-01-31 19:20 ` [PATCH 02/11] media: i2c: imx290: Set the colorspace fields in the format Dave Stevenson
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

Fix up a couple of coding style issues regarding missing blank
lines after declarations, double blank lines, and incorrect
indentation.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index a370f1102334..88c7201510a2 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -106,7 +106,6 @@
 
 #define IMX290_VMAX_DEFAULT				1125
 
-
 /*
  * The IMX290 pixel array is organized as follows:
  *
@@ -335,6 +334,7 @@ static const s64 imx290_link_freq_2lanes[] = {
 	[FREQ_INDEX_1080P] = 445500000,
 	[FREQ_INDEX_720P] = 297000000,
 };
+
 static const s64 imx290_link_freq_4lanes[] = {
 	[FREQ_INDEX_1080P] = 222750000,
 	[FREQ_INDEX_720P] = 148500000,
@@ -465,7 +465,7 @@ static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *val
 			      data, (addr >> IMX290_REG_SIZE_SHIFT) & 3);
 	if (ret < 0) {
 		dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n",
-			 ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
+			((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
 			 addr & IMX290_REG_ADDR_MASK, ret);
 		return ret;
 	}
@@ -486,7 +486,7 @@ static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err)
 			       data, (addr >> IMX290_REG_SIZE_SHIFT) & 3);
 	if (ret < 0) {
 		dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n",
-			 ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
+			((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
 			 addr & IMX290_REG_ADDR_MASK, ret);
 		if (err)
 			*err = ret;
@@ -752,8 +752,7 @@ static int imx290_start_streaming(struct imx290 *imx290,
 
 	/* Set init register settings */
 	ret = imx290_set_register_array(imx290, imx290_global_init_settings,
-					ARRAY_SIZE(
-						imx290_global_init_settings));
+					ARRAY_SIZE(imx290_global_init_settings));
 	if (ret < 0) {
 		dev_err(imx290->dev, "Could not set init registers\n");
 		return ret;
-- 
2.34.1


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

* [PATCH 02/11] media: i2c: imx290: Set the colorspace fields in the format
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
  2023-01-31 19:20 ` [PATCH 01/11] media: i2c: imx290: Match kernel coding style on whitespace Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02  0:29   ` Laurent Pinchart
  2023-01-31 19:20 ` [PATCH 03/11] media: i2c: imx290: Add V4L2_SUBDEV_FL_HAS_EVENTS and subscribe hooks Dave Stevenson
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

The colorspace fields were left untouched in imx290_set_fmt
which lead to a v4l2-compliance failure.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 88c7201510a2..bf96fd914303 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -897,6 +897,14 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
 		fmt->format.code = imx290_formats[0].code[imx290->mono];
 
 	fmt->format.field = V4L2_FIELD_NONE;
+	fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+	fmt->format.ycbcr_enc =
+		V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace);
+	fmt->format.quantization =
+		V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->format.colorspace,
+					      fmt->format.ycbcr_enc);
+	fmt->format.xfer_func =
+		V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace);
 
 	format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
 
-- 
2.34.1


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

* [PATCH 03/11] media: i2c: imx290: Add V4L2_SUBDEV_FL_HAS_EVENTS and subscribe hooks
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
  2023-01-31 19:20 ` [PATCH 01/11] media: i2c: imx290: Match kernel coding style on whitespace Dave Stevenson
  2023-01-31 19:20 ` [PATCH 02/11] media: i2c: imx290: Set the colorspace fields in the format Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02  0:29   ` Laurent Pinchart
  2023-02-03  8:54   ` Alexander Stein
  2023-01-31 19:20 ` [PATCH 04/11] media: i2c: imx290: Fix the pixel rate at 148.5Mpix/s Dave Stevenson
                   ` (8 subsequent siblings)
  11 siblings, 2 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

Any V4L2 subdevice that implements controls and declares
V4L2_SUBDEV_FL_HAS_DEVNODE should also declare V4L2_SUBDEV_FL_HAS_EVENTS
and implement subscribe_event and unsubscribe_event hooks.

This driver didn't and would therefore fail v4l2-compliance
testing.

Add the relevant hooks.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index bf96fd914303..12946ca9d8d2 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -20,6 +20,7 @@
 #include <media/media-entity.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 
@@ -977,6 +978,11 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
 	return 0;
 }
 
+static const struct v4l2_subdev_core_ops imx290_core_ops = {
+	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
 static const struct v4l2_subdev_video_ops imx290_video_ops = {
 	.s_stream = imx290_set_stream,
 };
@@ -991,6 +997,7 @@ static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
 };
 
 static const struct v4l2_subdev_ops imx290_subdev_ops = {
+	.core = &imx290_core_ops,
 	.video = &imx290_video_ops,
 	.pad = &imx290_pad_ops,
 };
@@ -1009,7 +1016,8 @@ static int imx290_subdev_init(struct imx290 *imx290)
 	imx290->current_mode = &imx290_modes_ptr(imx290)[0];
 
 	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
-	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+			    V4L2_SUBDEV_FL_HAS_EVENTS;
 	imx290->sd.dev = imx290->dev;
 	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
 	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
-- 
2.34.1


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

* [PATCH 04/11] media: i2c: imx290: Fix the pixel rate at 148.5Mpix/s
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
                   ` (2 preceding siblings ...)
  2023-01-31 19:20 ` [PATCH 03/11] media: i2c: imx290: Add V4L2_SUBDEV_FL_HAS_EVENTS and subscribe hooks Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02  8:13   ` Laurent Pinchart
  2023-01-31 19:20 ` [PATCH 05/11] media: i2c: imx290: Support 60fps in 2 lane operation Dave Stevenson
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

The datasheet lists the link frequency changes between
1080p and 720p modes. This is correct that the link frequency
changes as measured on an oscilloscope.

Link frequency is not necessarily the same as pixel rate.

The datasheet gives standard configurations for 1080p and 720p
modes at a number of frame rates.
Looking at the 1080p mode it gives:
HMAX = 0x898 = 2200
VMAX = 0x465 = 1125
2200 * 1125 * 60fps = 148.5MPix/s

Looking at the 720p mode it gives:
HMAX = 0xce4 = 3300
VMAX = 0x2ee = 750
3300 * 750 * 60fps = 148.5Mpix/s

This driver currently scales the pixel rate proportionally to the
link frequency, however the above shows that this is not the
correct thing to do, and currently all frame rate and exposure
calculations give incorrect results.

Correctly report the pixel rate as being 148.5MPix/s under any
mode.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 12946ca9d8d2..bd8729aed43c 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -107,6 +107,7 @@
 
 #define IMX290_VMAX_DEFAULT				1125
 
+#define IMX290_PIXEL_RATE				148500000
 /*
  * The IMX290 pixel array is organized as follows:
  *
@@ -190,7 +191,6 @@ struct imx290 {
 
 	struct v4l2_ctrl_handler ctrls;
 	struct v4l2_ctrl *link_freq;
-	struct v4l2_ctrl *pixel_rate;
 	struct v4l2_ctrl *hblank;
 	struct v4l2_ctrl *vblank;
 };
@@ -649,15 +649,8 @@ static void imx290_ctrl_update(struct imx290 *imx290,
 {
 	unsigned int hblank = mode->hmax - mode->width;
 	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
-	s64 link_freq = imx290_link_freqs_ptr(imx290)[mode->link_freq_index];
-	u64 pixel_rate;
-
-	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
-	pixel_rate = link_freq * 2 * imx290->nlanes;
-	do_div(pixel_rate, imx290_format_info(format->code)->bpp);
 
 	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
-	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, pixel_rate);
 
 	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
 	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
@@ -707,9 +700,9 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 	if (imx290->link_freq)
 		imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
-	imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
-					       V4L2_CID_PIXEL_RATE,
-					       1, INT_MAX, 1, 1);
+	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_PIXEL_RATE,
+			  IMX290_PIXEL_RATE, IMX290_PIXEL_RATE, 1,
+			  IMX290_PIXEL_RATE);
 
 	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
 				     V4L2_CID_TEST_PATTERN,
-- 
2.34.1


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

* [PATCH 05/11] media: i2c: imx290: Support 60fps in 2 lane operation
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
                   ` (3 preceding siblings ...)
  2023-01-31 19:20 ` [PATCH 04/11] media: i2c: imx290: Fix the pixel rate at 148.5Mpix/s Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02 10:50   ` Laurent Pinchart
  2023-02-03  8:50   ` Alexander Stein
  2023-01-31 19:20 ` [PATCH 06/11] media: i2c: imx290: Use CSI timings as per datasheet Dave Stevenson
                   ` (6 subsequent siblings)
  11 siblings, 2 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

Commit "97589ad61c73 media: i2c: imx290: Add support for 2 data lanes"
added support for running in two lane mode (instead of 4), but
without changing the link frequency that resulted in a max of 30fps.

Commit "98e0500eadb7 media: i2c: imx290: Add configurable link frequency
and pixel rate" then doubled the link frequency when in 2 lane mode,
but didn't undo the correction for running at only 30fps, just extending
horizontal blanking instead.

Remove the 30fps limit on 2 lane by correcting the register config
in accordance with the datasheet for 60fps operation over 2 lanes.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 17 +++--------------
 1 file changed, 3 insertions(+), 14 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index bd8729aed43c..6bcfa535872f 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -366,7 +366,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
 	{
 		.width = 1920,
 		.height = 1080,
-		.hmax = 4400,
+		.hmax = 2200,
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
@@ -374,7 +374,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
 	{
 		.width = 1280,
 		.height = 720,
-		.hmax = 6600,
+		.hmax = 3300,
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
@@ -518,21 +518,10 @@ static int imx290_set_register_array(struct imx290 *imx290,
 static int imx290_set_data_lanes(struct imx290 *imx290)
 {
 	int ret = 0;
-	u32 frsel;
-
-	switch (imx290->nlanes) {
-	case 2:
-	default:
-		frsel = 0x02;
-		break;
-	case 4:
-		frsel = 0x01;
-		break;
-	}
 
 	imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret);
 	imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret);
-	imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
+	imx290_write(imx290, IMX290_FR_FDG_SEL, 0x01, &ret);
 
 	return ret;
 }
-- 
2.34.1


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

* [PATCH 06/11] media: i2c: imx290: Use CSI timings as per datasheet
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
                   ` (4 preceding siblings ...)
  2023-01-31 19:20 ` [PATCH 05/11] media: i2c: imx290: Support 60fps in 2 lane operation Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02 14:36   ` Laurent Pinchart
  2023-02-03  8:04   ` Alexander Stein
  2023-01-31 19:20 ` [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write Dave Stevenson
                   ` (5 subsequent siblings)
  11 siblings, 2 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

Commit "98e0500eadb7 media: i2c: imx290: Add configurable link frequency
and pixel rate" added support for the increased link frequencies
on 2 data lanes, but didn't update the CSI timing registers in
accordance with the datasheet.

Use the specified settings.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 126 +++++++++++++++++++++++++++++++------
 1 file changed, 106 insertions(+), 20 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 6bcfa535872f..9ddd6382b127 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -174,6 +174,18 @@ struct imx290_mode {
 	u32 data_size;
 };
 
+struct imx290_csi_cfg {
+	u16 repitition;
+	u16 tclkpost;
+	u16 thszero;
+	u16 thsprepare;
+	u16 tclktrail;
+	u16 thstrail;
+	u16 tclkzero;
+	u16 tclkprepare;
+	u16 tlpx;
+};
+
 struct imx290 {
 	struct device *dev;
 	struct clk *xclk;
@@ -273,16 +285,6 @@ static const struct imx290_regval imx290_1080p_settings[] = {
 	{ IMX290_INCKSEL4, 0x01 },
 	{ IMX290_INCKSEL5, 0x1a },
 	{ IMX290_INCKSEL6, 0x1a },
-	/* data rate settings */
-	{ IMX290_REPETITION, 0x10 },
-	{ IMX290_TCLKPOST, 87 },
-	{ IMX290_THSZERO, 55 },
-	{ IMX290_THSPREPARE, 31 },
-	{ IMX290_TCLKTRAIL, 31 },
-	{ IMX290_THSTRAIL, 31 },
-	{ IMX290_TCLKZERO, 119 },
-	{ IMX290_TCLKPREPARE, 31 },
-	{ IMX290_TLPX, 23 },
 };
 
 static const struct imx290_regval imx290_720p_settings[] = {
@@ -298,16 +300,6 @@ static const struct imx290_regval imx290_720p_settings[] = {
 	{ IMX290_INCKSEL4, 0x01 },
 	{ IMX290_INCKSEL5, 0x1a },
 	{ IMX290_INCKSEL6, 0x1a },
-	/* data rate settings */
-	{ IMX290_REPETITION, 0x10 },
-	{ IMX290_TCLKPOST, 79 },
-	{ IMX290_THSZERO, 47 },
-	{ IMX290_THSPREPARE, 23 },
-	{ IMX290_TCLKTRAIL, 23 },
-	{ IMX290_THSTRAIL, 23 },
-	{ IMX290_TCLKZERO, 87 },
-	{ IMX290_TCLKPREPARE, 23 },
-	{ IMX290_TLPX, 23 },
 };
 
 static const struct imx290_regval imx290_10bit_settings[] = {
@@ -328,6 +320,58 @@ static const struct imx290_regval imx290_12bit_settings[] = {
 	{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
 };
 
+static const struct imx290_csi_cfg imx290_csi_222_75mhz = {
+	/* 222.25MHz or 445.5Mbit/s per lane */
+	.repitition = 0x10,
+	.tclkpost = 87,
+	.thszero = 55,
+	.thsprepare = 31,
+	.tclktrail = 31,
+	.thstrail = 31,
+	.tclkzero = 119,
+	.tclkprepare = 31,
+	.tlpx = 23,
+};
+
+static const struct imx290_csi_cfg imx290_csi_445_5mhz = {
+	/* 445.5MHz or 891Mbit/s per lane */
+	.repitition = 0x00,
+	.tclkpost = 119,
+	.thszero = 103,
+	.thsprepare = 71,
+	.tclktrail = 55,
+	.thstrail = 63,
+	.tclkzero = 255,
+	.tclkprepare = 63,
+	.tlpx = 55,
+};
+
+static const struct imx290_csi_cfg imx290_csi_148_5mhz = {
+	/* 148.5MHz or 297Mbit/s per lane */
+	.repitition = 0x10,
+	.tclkpost = 79,
+	.thszero = 47,
+	.thsprepare = 23,
+	.tclktrail = 23,
+	.thstrail = 23,
+	.tclkzero = 87,
+	.tclkprepare = 23,
+	.tlpx = 23,
+};
+
+static const struct imx290_csi_cfg imx290_csi_297mhz = {
+	/* 297MHz or 594Mbit/s per lane */
+	.repitition = 0x00,
+	.tclkpost = 103,
+	.thszero = 87,
+	.thsprepare = 47,
+	.tclktrail = 39,
+	.thstrail = 47,
+	.tclkzero = 191,
+	.tclkprepare = 47,
+	.tlpx = 39,
+};
+
 /* supported link frequencies */
 #define FREQ_INDEX_1080P	0
 #define FREQ_INDEX_720P		1
@@ -536,6 +580,42 @@ static int imx290_set_black_level(struct imx290 *imx290,
 			    black_level >> (16 - bpp), err);
 }
 
+static int imx290_set_csi_config(struct imx290 *imx290)
+{
+	const s64 *link_freqs = imx290_link_freqs_ptr(imx290);
+	const struct imx290_csi_cfg *csi_cfg;
+	int ret = 0;
+
+	switch (link_freqs[imx290->current_mode->link_freq_index]) {
+	case 445500000:
+		csi_cfg = &imx290_csi_445_5mhz;
+		break;
+	case 297000000:
+		csi_cfg = &imx290_csi_297mhz;
+		break;
+	case 222750000:
+		csi_cfg = &imx290_csi_222_75mhz;
+		break;
+	case 148500000:
+		csi_cfg = &imx290_csi_148_5mhz;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	imx290_write(imx290, IMX290_REPETITION, csi_cfg->repitition, &ret);
+	imx290_write(imx290, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret);
+	imx290_write(imx290, IMX290_THSZERO, csi_cfg->thszero, &ret);
+	imx290_write(imx290, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret);
+	imx290_write(imx290, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret);
+	imx290_write(imx290, IMX290_THSTRAIL, csi_cfg->thstrail, &ret);
+	imx290_write(imx290, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret);
+	imx290_write(imx290, IMX290_TCLKPREPARE, csi_cfg->tclkprepare, &ret);
+	imx290_write(imx290, IMX290_TLPX, csi_cfg->tlpx, &ret);
+
+	return ret;
+}
+
 static int imx290_setup_format(struct imx290 *imx290,
 			       const struct v4l2_mbus_framefmt *format)
 {
@@ -748,6 +828,12 @@ static int imx290_start_streaming(struct imx290 *imx290,
 		return ret;
 	}
 
+	ret = imx290_set_csi_config(imx290);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Could not set csi cfg\n");
+		return ret;
+	}
+
 	/* Apply the register values related to current frame format */
 	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
 	ret = imx290_setup_format(imx290, format);
-- 
2.34.1


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

* [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
                   ` (5 preceding siblings ...)
  2023-01-31 19:20 ` [PATCH 06/11] media: i2c: imx290: Use CSI timings as per datasheet Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02 14:58   ` Laurent Pinchart
  2023-02-03  7:19   ` Alexander Stein
  2023-01-31 19:20 ` [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK " Dave Stevenson
                   ` (4 subsequent siblings)
  11 siblings, 2 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

The driver exposed V4L2_CID_HBLANK as a read only control to allow
for exposure calculations and determination of the frame rate.

Convert to a read/write control so that the frame rate can be
controlled.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 33 +++++++++++++++++++--------------
 1 file changed, 19 insertions(+), 14 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 9ddd6382b127..9006be6e5e7c 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -47,6 +47,7 @@
 #define IMX290_GAIN					IMX290_REG_8BIT(0x3014)
 #define IMX290_VMAX					IMX290_REG_24BIT(0x3018)
 #define IMX290_HMAX					IMX290_REG_16BIT(0x301c)
+#define IMX290_HMAX_MAX					0xffff
 #define IMX290_SHS1					IMX290_REG_24BIT(0x3020)
 #define IMX290_WINWV_OB					IMX290_REG_8BIT(0x303a)
 #define IMX290_WINPV					IMX290_REG_16BIT(0x303c)
@@ -167,7 +168,7 @@ struct imx290_regval {
 struct imx290_mode {
 	u32 width;
 	u32 height;
-	u32 hmax;
+	u32 hmax_min;
 	u8 link_freq_index;
 
 	const struct imx290_regval *data;
@@ -410,7 +411,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
 	{
 		.width = 1920,
 		.height = 1080,
-		.hmax = 2200,
+		.hmax_min = 2200,
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
@@ -418,7 +419,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
 	{
 		.width = 1280,
 		.height = 720,
-		.hmax = 3300,
+		.hmax_min = 3300,
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
@@ -429,7 +430,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
 	{
 		.width = 1920,
 		.height = 1080,
-		.hmax = 2200,
+		.hmax_min = 2200,
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
@@ -437,7 +438,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
 	{
 		.width = 1280,
 		.height = 720,
-		.hmax = 3300,
+		.hmax_min = 3300,
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
@@ -686,6 +687,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 		}
 		break;
 
+	case V4L2_CID_HBLANK:
+		ret = imx290_write(imx290, IMX290_HMAX,
+				   ctrl->val + imx290->current_mode->width,
+				   NULL);
+		break;
+
 	default:
 		ret = -EINVAL;
 		break;
@@ -716,12 +723,14 @@ static void imx290_ctrl_update(struct imx290 *imx290,
 			       const struct v4l2_mbus_framefmt *format,
 			       const struct imx290_mode *mode)
 {
-	unsigned int hblank = mode->hmax - mode->width;
+	unsigned int hblank_min = mode->hmax_min - mode->width;
+	unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
 	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
 
 	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
 
-	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
+	__v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
+				 hblank_min);
 	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
 }
 
@@ -778,10 +787,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 				     ARRAY_SIZE(imx290_test_pattern_menu) - 1,
 				     0, 0, imx290_test_pattern_menu);
 
+	/*
+	 * Actual range will be set from imx290_ctrl_update later in the probe.
+	 */
 	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
 					   V4L2_CID_HBLANK, 1, 1, 1, 1);
-	if (imx290->hblank)
-		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
 	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
 					   V4L2_CID_VBLANK, 1, 1, 1, 1);
@@ -850,11 +860,6 @@ static int imx290_start_streaming(struct imx290 *imx290,
 		return ret;
 	}
 
-	ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
-			   NULL);
-	if (ret)
-		return ret;
-
 	/* Apply customized values from user */
 	ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
 	if (ret) {
-- 
2.34.1


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

* [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK to read/write
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
                   ` (6 preceding siblings ...)
  2023-01-31 19:20 ` [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02 15:40   ` Alexander Stein
  2023-02-02 16:01   ` Laurent Pinchart
  2023-01-31 19:20 ` [PATCH 09/11] media: i2c: imx290: Remove duplicated write to IMX290_CTRL_07 Dave Stevenson
                   ` (3 subsequent siblings)
  11 siblings, 2 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

The driver exposed V4L2_CID_HBLANK as a read only control to allow
for exposure calculations and determination of the frame rate.

Convert to a read/write control so that the frame rate can be
controlled.
V4L2_CID_VBLANK also sets the limits for the exposure control,
therefore exposure ranges have to be updated when vblank changes.

This also updates the default VMAX in 720p mode from 1125 to
750 to achieve 60fps and follow the datasheet.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 57 ++++++++++++++++++++++++++++++--------
 1 file changed, 45 insertions(+), 12 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 9006be6e5e7c..3413d83369ba 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -46,6 +46,7 @@
 #define IMX290_BLKLEVEL					IMX290_REG_16BIT(0x300a)
 #define IMX290_GAIN					IMX290_REG_8BIT(0x3014)
 #define IMX290_VMAX					IMX290_REG_24BIT(0x3018)
+#define IMX290_VMAX_MAX					0x3ffff
 #define IMX290_HMAX					IMX290_REG_16BIT(0x301c)
 #define IMX290_HMAX_MAX					0xffff
 #define IMX290_SHS1					IMX290_REG_24BIT(0x3020)
@@ -106,8 +107,6 @@
 #define IMX290_PGCTRL_THRU				BIT(1)
 #define IMX290_PGCTRL_MODE(n)				((n) << 4)
 
-#define IMX290_VMAX_DEFAULT				1125
-
 #define IMX290_PIXEL_RATE				148500000
 /*
  * The IMX290 pixel array is organized as follows:
@@ -169,6 +168,7 @@ struct imx290_mode {
 	u32 width;
 	u32 height;
 	u32 hmax_min;
+	u32 vmax_min;
 	u8 link_freq_index;
 
 	const struct imx290_regval *data;
@@ -206,6 +206,7 @@ struct imx290 {
 	struct v4l2_ctrl *link_freq;
 	struct v4l2_ctrl *hblank;
 	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *exposure;
 };
 
 static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
@@ -219,7 +220,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
 
 static const struct imx290_regval imx290_global_init_settings[] = {
 	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
-	{ IMX290_VMAX, IMX290_VMAX_DEFAULT },
 	{ IMX290_EXTCK_FREQ, 0x2520 },
 	{ IMX290_WINWV_OB, 12 },
 	{ IMX290_WINPH, 0 },
@@ -412,6 +412,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
 		.width = 1920,
 		.height = 1080,
 		.hmax_min = 2200,
+		.vmax_min = 1125,
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
@@ -420,6 +421,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
 		.width = 1280,
 		.height = 720,
 		.hmax_min = 3300,
+		.vmax_min = 750,
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
@@ -431,6 +433,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
 		.width = 1920,
 		.height = 1080,
 		.hmax_min = 2200,
+		.vmax_min = 1125,
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
@@ -439,6 +442,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
 		.width = 1280,
 		.height = 720,
 		.hmax_min = 3300,
+		.vmax_min = 750,
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
@@ -645,7 +649,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 					     struct imx290, ctrls);
 	const struct v4l2_mbus_framefmt *format;
 	struct v4l2_subdev_state *state;
-	int ret = 0;
+	int ret = 0, vmax;
 
 	/*
 	 * Return immediately for controls that don't need to be applied to the
@@ -654,6 +658,18 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 	if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
 		return 0;
 
+	if (ctrl->id == V4L2_CID_VBLANK) {
+		u32 vmax = ctrl->val + imx290->current_mode->height;
+
+		/*
+		 * Changing vblank changes the allowed range for exposure.
+		 * We don't supply the current exposure as default here as it
+		 * may lie outside the new range. We will reset it just below.
+		 */
+		__v4l2_ctrl_modify_range(imx290->exposure,
+					 1, vmax - 2, 1, vmax - 2);
+	}
+
 	/* V4L2 controls values will be applied only when power is already up */
 	if (!pm_runtime_get_if_in_use(imx290->dev))
 		return 0;
@@ -666,9 +682,23 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 		ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
 		break;
 
+	case V4L2_CID_VBLANK:
+		ret = imx290_write(imx290, IMX290_VMAX,
+				   ctrl->val + imx290->current_mode->height,
+				   NULL);
+		/*
+		 * Due to the way that exposure is programmed in this sensor in
+		 * relation to VMAX, we have to reprogramme it whenever VMAX is
+		 * changed.
+		 * Update ctrl so that the V4L2_CID_EXPOSURE case can refer to
+		 * it.
+		 */
+		ctrl = imx290->exposure;
+		fallthrough;
 	case V4L2_CID_EXPOSURE:
+		vmax = imx290->vblank->val + imx290->current_mode->height;
 		ret = imx290_write(imx290, IMX290_SHS1,
-				   IMX290_VMAX_DEFAULT - ctrl->val - 1, NULL);
+				   vmax - ctrl->val - 1, NULL);
 		break;
 
 	case V4L2_CID_TEST_PATTERN:
@@ -725,13 +755,15 @@ static void imx290_ctrl_update(struct imx290 *imx290,
 {
 	unsigned int hblank_min = mode->hmax_min - mode->width;
 	unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
-	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
+	unsigned int vblank_min = mode->vmax_min - mode->height;
+	unsigned int vblank_max = IMX290_VMAX_MAX - mode->height;
 
 	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
 
 	__v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
 				 hblank_min);
-	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
+	__v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max, 1,
+				 vblank_min);
 }
 
 static int imx290_ctrl_init(struct imx290 *imx290)
@@ -761,9 +793,12 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
 			  V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
 
-	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
-			  V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
-			  IMX290_VMAX_DEFAULT - 2);
+	/*
+	 * Correct range will be determined through imx290_ctrl_update setting
+	 * V4L2_CID_VBLANK.
+	 */
+	imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					     V4L2_CID_EXPOSURE, 1, 1, 1, 1);
 
 	/*
 	 * Set the link frequency, pixel rate, horizontal blanking and vertical
@@ -795,8 +830,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 
 	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
 					   V4L2_CID_VBLANK, 1, 1, 1, 1);
-	if (imx290->vblank)
-		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
 	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
 					&props);
-- 
2.34.1


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

* [PATCH 09/11] media: i2c: imx290: Remove duplicated write to IMX290_CTRL_07
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
                   ` (7 preceding siblings ...)
  2023-01-31 19:20 ` [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK " Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02 16:04   ` Laurent Pinchart
  2023-02-03  7:45   ` Alexander Stein
  2023-01-31 19:20 ` [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock Dave Stevenson
                   ` (2 subsequent siblings)
  11 siblings, 2 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

IMX290_CTRL_07 was written from both imx290_global_init_settings
and imx290_1080p_settings and imx290_720p_settings.

Remove it from imx290_global_init_settings as the setting varies
based on the mode.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 3413d83369ba..5202ef3cc3e6 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -219,7 +219,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
  */
 
 static const struct imx290_regval imx290_global_init_settings[] = {
-	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
 	{ IMX290_EXTCK_FREQ, 0x2520 },
 	{ IMX290_WINWV_OB, 12 },
 	{ IMX290_WINPH, 0 },
-- 
2.34.1


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

* [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
                   ` (8 preceding siblings ...)
  2023-01-31 19:20 ` [PATCH 09/11] media: i2c: imx290: Remove duplicated write to IMX290_CTRL_07 Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02 22:03   ` Laurent Pinchart
  2023-02-03  7:44   ` Alexander Stein
  2023-01-31 19:20 ` [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips Dave Stevenson
  2023-02-02 22:16 ` [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Laurent Pinchart
  11 siblings, 2 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

The sensor supports either a 37.125 or 74.25MHz external, but
the driver only supported 37.125MHz.

Add the relevant register configuration for either clock
frequency option.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 120 +++++++++++++++++++++++++++++++------
 1 file changed, 103 insertions(+), 17 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 5202ef3cc3e6..7f6746f74040 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -102,6 +102,7 @@
 #define IMX290_TCLKPREPARE				IMX290_REG_16BIT(0x3452)
 #define IMX290_TLPX					IMX290_REG_16BIT(0x3454)
 #define IMX290_X_OUT_SIZE				IMX290_REG_16BIT(0x3472)
+#define IMX290_INCKSEL7					IMX290_REG_8BIT(0x3480)
 
 #define IMX290_PGCTRL_REGEN				BIT(0)
 #define IMX290_PGCTRL_THRU				BIT(1)
@@ -159,11 +160,27 @@
 
 #define IMX290_NUM_SUPPLIES				3
 
+#define CLK_37_125	0
+#define CLK_74_25	1
+#define NUM_CLK		2
+
 struct imx290_regval {
 	u32 reg;
 	u32 val;
 };
 
+/*
+ * Clock configuration for registers INCKSEL1 to INCKSEL6.
+ */
+struct imx290_clk_cfg {
+	u8 incksel1;
+	u8 incksel2;
+	u8 incksel3;
+	u8 incksel4;
+	u8 incksel5;
+	u8 incksel6;
+};
+
 struct imx290_mode {
 	u32 width;
 	u32 height;
@@ -173,6 +190,8 @@ struct imx290_mode {
 
 	const struct imx290_regval *data;
 	u32 data_size;
+
+	const struct imx290_clk_cfg *clk_cfg;
 };
 
 struct imx290_csi_cfg {
@@ -191,6 +210,7 @@ struct imx290 {
 	struct device *dev;
 	struct clk *xclk;
 	struct regmap *regmap;
+	u32 xclk_freq;
 	u8 nlanes;
 	u8 mono;
 
@@ -219,7 +239,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
  */
 
 static const struct imx290_regval imx290_global_init_settings[] = {
-	{ IMX290_EXTCK_FREQ, 0x2520 },
 	{ IMX290_WINWV_OB, 12 },
 	{ IMX290_WINPH, 0 },
 	{ IMX290_WINPV, 0 },
@@ -269,7 +288,16 @@ static const struct imx290_regval imx290_global_init_settings[] = {
 	{ IMX290_REG_8BIT(0x33b0), 0x50 },
 	{ IMX290_REG_8BIT(0x33b2), 0x1a },
 	{ IMX290_REG_8BIT(0x33b3), 0x04 },
-	{ IMX290_REG_8BIT(0x3480), 0x49 },
+};
+
+static const struct imx290_regval imx290_37_125mhz_clock[] = {
+	{ IMX290_EXTCK_FREQ, 0x2520 },
+	{ IMX290_INCKSEL7, 0x49 },
+};
+
+static const struct imx290_regval imx290_74_25mhz_clock[] = {
+	{ IMX290_EXTCK_FREQ, 0x4a40 },
+	{ IMX290_INCKSEL7, 0x92 },
 };
 
 static const struct imx290_regval imx290_1080p_settings[] = {
@@ -279,12 +307,6 @@ static const struct imx290_regval imx290_1080p_settings[] = {
 	{ IMX290_OPB_SIZE_V, 10 },
 	{ IMX290_X_OUT_SIZE, 1920 },
 	{ IMX290_Y_OUT_SIZE, 1080 },
-	{ IMX290_INCKSEL1, 0x18 },
-	{ IMX290_INCKSEL2, 0x03 },
-	{ IMX290_INCKSEL3, 0x20 },
-	{ IMX290_INCKSEL4, 0x01 },
-	{ IMX290_INCKSEL5, 0x1a },
-	{ IMX290_INCKSEL6, 0x1a },
 };
 
 static const struct imx290_regval imx290_720p_settings[] = {
@@ -294,12 +316,6 @@ static const struct imx290_regval imx290_720p_settings[] = {
 	{ IMX290_OPB_SIZE_V, 4 },
 	{ IMX290_X_OUT_SIZE, 1280 },
 	{ IMX290_Y_OUT_SIZE, 720 },
-	{ IMX290_INCKSEL1, 0x20 },
-	{ IMX290_INCKSEL2, 0x00 },
-	{ IMX290_INCKSEL3, 0x20 },
-	{ IMX290_INCKSEL4, 0x01 },
-	{ IMX290_INCKSEL5, 0x1a },
-	{ IMX290_INCKSEL6, 0x1a },
 };
 
 static const struct imx290_regval imx290_10bit_settings[] = {
@@ -405,6 +421,48 @@ static inline int imx290_link_freqs_num(const struct imx290 *imx290)
 		return ARRAY_SIZE(imx290_link_freq_4lanes);
 }
 
+static const struct imx290_clk_cfg imx290_1080p_clock_config[NUM_CLK] = {
+	[CLK_37_125] = {
+		/* 37.125MHz clock config */
+		.incksel1 = 0x18,
+		.incksel2 = 0x03,
+		.incksel3 = 0x20,
+		.incksel4 = 0x01,
+		.incksel5 = 0x1a,
+		.incksel6 = 0x1a,
+	},
+	[CLK_74_25] = {
+		/* 74.25MHz clock config */
+		.incksel1 = 0x0c,
+		.incksel2 = 0x03,
+		.incksel3 = 0x10,
+		.incksel4 = 0x01,
+		.incksel5 = 0x1b,
+		.incksel6 = 0x1b,
+	},
+};
+
+static const struct imx290_clk_cfg imx290_720p_clock_config[NUM_CLK] = {
+	[CLK_37_125] = {
+		/* 37.125MHz clock config */
+		.incksel1 = 0x20,
+		.incksel2 = 0x00,
+		.incksel3 = 0x20,
+		.incksel4 = 0x01,
+		.incksel5 = 0x1a,
+		.incksel6 = 0x1a,
+	},
+	[CLK_74_25] = {
+		/* 74.25MHz clock config */
+		.incksel1 = 0x10,
+		.incksel2 = 0x00,
+		.incksel3 = 0x10,
+		.incksel4 = 0x01,
+		.incksel5 = 0x1b,
+		.incksel6 = 0x1b,
+	},
+};
+
 /* Mode configs */
 static const struct imx290_mode imx290_modes_2lanes[] = {
 	{
@@ -415,6 +473,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
+		.clk_cfg = imx290_1080p_clock_config,
 	},
 	{
 		.width = 1280,
@@ -424,6 +483,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
+		.clk_cfg = imx290_720p_clock_config,
 	},
 };
 
@@ -436,6 +496,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
 		.link_freq_index = FREQ_INDEX_1080P,
 		.data = imx290_1080p_settings,
 		.data_size = ARRAY_SIZE(imx290_1080p_settings),
+		.clk_cfg = imx290_1080p_clock_config,
 	},
 	{
 		.width = 1280,
@@ -445,6 +506,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
 		.link_freq_index = FREQ_INDEX_720P,
 		.data = imx290_720p_settings,
 		.data_size = ARRAY_SIZE(imx290_720p_settings),
+		.clk_cfg = imx290_720p_clock_config,
 	},
 };
 
@@ -563,6 +625,23 @@ static int imx290_set_register_array(struct imx290 *imx290,
 	return 0;
 }
 
+static int imx290_set_clock(struct imx290 *imx290)
+{
+	int clk_idx = (imx290->xclk_freq == 37125000) ? 0 : 1;
+	const struct imx290_mode *mode = imx290->current_mode;
+	const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[clk_idx];
+	int ret = 0;
+
+	imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret);
+	imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret);
+	imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret);
+	imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret);
+	imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret);
+	imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret);
+
+	return ret;
+}
+
 static int imx290_set_data_lanes(struct imx290 *imx290)
 {
 	int ret = 0;
@@ -863,6 +942,13 @@ static int imx290_start_streaming(struct imx290 *imx290,
 		return ret;
 	}
 
+	/* Set clock parameters based on mode and xclk */
+	ret = imx290_set_clock(imx290);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Could not set clocks\n");
+		return ret;
+	}
+
 	/* Set data lane count */
 	ret = imx290_set_data_lanes(imx290);
 	if (ret < 0) {
@@ -1259,14 +1345,14 @@ static int imx290_init_clk(struct imx290 *imx290)
 	int ret;
 
 	ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
-				       "clock-frequency", &xclk_freq);
+				       "clock-frequency", &imx290->xclk_freq);
 	if (ret) {
 		dev_err(imx290->dev, "Could not get xclk frequency\n");
 		return ret;
 	}
 
-	/* external clock must be 37.125 MHz */
-	if (xclk_freq != 37125000) {
+	/* external clock must be 37.125 MHz or 74.25MHz */
+	if (imx290->xclk_freq != 37125000 && imx290->xclk_freq != 74250000) {
 		dev_err(imx290->dev, "External clock frequency %u is not supported\n",
 			xclk_freq);
 		return -EINVAL;
-- 
2.34.1


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

* [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
                   ` (9 preceding siblings ...)
  2023-01-31 19:20 ` [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock Dave Stevenson
@ 2023-01-31 19:20 ` Dave Stevenson
  2023-02-02 22:10   ` Laurent Pinchart
  2023-02-03  7:35   ` Alexander Stein
  2023-02-02 22:16 ` [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Laurent Pinchart
  11 siblings, 2 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-01-31 19:20 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media
  Cc: Dave Stevenson

The sensor supports H & V flips, so add the relevant hooks for
V4L2_CID_HFLIP and V4L2_CID_VFLIP to configure them.

Note that the Bayer order is maintained as the readout area
shifts by 1 pixel in the appropriate direction (note the
comment about the top margin being 8 pixels whilst the bottom
margin is 9). The V4L2_SEL_TGT_CROP region is therefore
adjusted appropriately.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 drivers/media/i2c/imx290.c | 37 ++++++++++++++++++++++++++++++++++---
 1 file changed, 34 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 7f6746f74040..d2b7534f2c51 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -227,6 +227,8 @@ struct imx290 {
 	struct v4l2_ctrl *hblank;
 	struct v4l2_ctrl *vblank;
 	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vflip;
 };
 
 static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
@@ -801,6 +803,24 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 				   NULL);
 		break;
 
+	case V4L2_CID_HFLIP:
+	case V4L2_CID_VFLIP:
+	{
+		u32 reg;
+
+		/* WINMODE is in bits [6:4], so need to read-modify-write */
+		ret = imx290_read(imx290, IMX290_CTRL_07, &reg);
+		if (ret)
+			break;
+		reg &= ~(IMX290_HREVERSE | IMX290_VREVERSE);
+		if (imx290->hflip->val)
+			reg |= IMX290_HREVERSE;
+		if (imx290->vflip->val)
+			reg |= IMX290_VREVERSE;
+		ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL);
+		break;
+	}
+
 	default:
 		ret = -EINVAL;
 		break;
@@ -853,7 +873,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 	if (ret < 0)
 		return ret;
 
-	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
+	v4l2_ctrl_handler_init(&imx290->ctrls, 11);
 
 	/*
 	 * The sensor has an analog gain and a digital gain, both controlled
@@ -909,6 +929,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
 					   V4L2_CID_VBLANK, 1, 1, 1, 1);
 
+	imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					  V4L2_CID_HFLIP, 0, 1, 1, 0);
+	imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					  V4L2_CID_VFLIP, 0, 1, 1, 0);
+
 	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
 					&props);
 
@@ -1030,6 +1055,9 @@ static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
 		pm_runtime_put_autosuspend(imx290->dev);
 	}
 
+	/* vflip and hflip cannot change during streaming */
+	__v4l2_ctrl_grab(imx290->vflip, enable);
+	__v4l2_ctrl_grab(imx290->hflip, enable);
 unlock:
 	v4l2_subdev_unlock_state(state);
 	return ret;
@@ -1115,6 +1143,7 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
 				struct v4l2_subdev_state *sd_state,
 				struct v4l2_subdev_selection *sel)
 {
+	struct imx290 *imx290 = to_imx290(sd);
 	struct v4l2_mbus_framefmt *format;
 
 	switch (sel->target) {
@@ -1122,9 +1151,11 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
 		format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
 
 		sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
-			   + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2;
+			   + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2
+			   + imx290->vflip->val;
 		sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT
-			    + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2;
+			    + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2
+			    + imx290->hflip->val;
 		sel->r.width = format->width;
 		sel->r.height = format->height;
 
-- 
2.34.1


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

* Re: [PATCH 01/11] media: i2c: imx290: Match kernel coding style on whitespace
  2023-01-31 19:20 ` [PATCH 01/11] media: i2c: imx290: Match kernel coding style on whitespace Dave Stevenson
@ 2023-02-02  0:21   ` Laurent Pinchart
  0 siblings, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02  0:21 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:06PM +0000, Dave Stevenson wrote:
> Fix up a couple of coding style issues regarding missing blank
> lines after declarations, double blank lines, and incorrect
> indentation.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
>  drivers/media/i2c/imx290.c | 9 ++++-----
>  1 file changed, 4 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index a370f1102334..88c7201510a2 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -106,7 +106,6 @@
>  
>  #define IMX290_VMAX_DEFAULT				1125
>  
> -
>  /*
>   * The IMX290 pixel array is organized as follows:
>   *
> @@ -335,6 +334,7 @@ static const s64 imx290_link_freq_2lanes[] = {
>  	[FREQ_INDEX_1080P] = 445500000,
>  	[FREQ_INDEX_720P] = 297000000,
>  };
> +
>  static const s64 imx290_link_freq_4lanes[] = {
>  	[FREQ_INDEX_1080P] = 222750000,
>  	[FREQ_INDEX_720P] = 148500000,
> @@ -465,7 +465,7 @@ static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *val
>  			      data, (addr >> IMX290_REG_SIZE_SHIFT) & 3);
>  	if (ret < 0) {
>  		dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n",
> -			 ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
> +			((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
>  			 addr & IMX290_REG_ADDR_MASK, ret);
>  		return ret;
>  	}
> @@ -486,7 +486,7 @@ static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err)
>  			       data, (addr >> IMX290_REG_SIZE_SHIFT) & 3);
>  	if (ret < 0) {
>  		dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n",
> -			 ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
> +			((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8,
>  			 addr & IMX290_REG_ADDR_MASK, ret);
>  		if (err)
>  			*err = ret;
> @@ -752,8 +752,7 @@ static int imx290_start_streaming(struct imx290 *imx290,
>  
>  	/* Set init register settings */
>  	ret = imx290_set_register_array(imx290, imx290_global_init_settings,
> -					ARRAY_SIZE(
> -						imx290_global_init_settings));
> +					ARRAY_SIZE(imx290_global_init_settings));
>  	if (ret < 0) {
>  		dev_err(imx290->dev, "Could not set init registers\n");
>  		return ret;

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 02/11] media: i2c: imx290: Set the colorspace fields in the format
  2023-01-31 19:20 ` [PATCH 02/11] media: i2c: imx290: Set the colorspace fields in the format Dave Stevenson
@ 2023-02-02  0:29   ` Laurent Pinchart
  0 siblings, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02  0:29 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:07PM +0000, Dave Stevenson wrote:
> The colorspace fields were left untouched in imx290_set_fmt
> which lead to a v4l2-compliance failure.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 88c7201510a2..bf96fd914303 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -897,6 +897,14 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
>  		fmt->format.code = imx290_formats[0].code[imx290->mono];
>  
>  	fmt->format.field = V4L2_FIELD_NONE;
> +	fmt->format.colorspace = V4L2_COLORSPACE_RAW;
> +	fmt->format.ycbcr_enc =
> +		V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace);
> +	fmt->format.quantization =
> +		V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->format.colorspace,
> +					      fmt->format.ycbcr_enc);
> +	fmt->format.xfer_func =
> +		V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace);

Given that all of these are hardcoded, I think it would be more readable
to write

	fmt->format.colorspace = V4L2_COLORSPACE_RAW;
	fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601;
	fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
	fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;

>  	format = v4l2_subdev_get_pad_format(sd, sd_state, 0);

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 03/11] media: i2c: imx290: Add V4L2_SUBDEV_FL_HAS_EVENTS and subscribe hooks
  2023-01-31 19:20 ` [PATCH 03/11] media: i2c: imx290: Add V4L2_SUBDEV_FL_HAS_EVENTS and subscribe hooks Dave Stevenson
@ 2023-02-02  0:29   ` Laurent Pinchart
  2023-02-03  8:54   ` Alexander Stein
  1 sibling, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02  0:29 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:08PM +0000, Dave Stevenson wrote:
> Any V4L2 subdevice that implements controls and declares
> V4L2_SUBDEV_FL_HAS_DEVNODE should also declare V4L2_SUBDEV_FL_HAS_EVENTS
> and implement subscribe_event and unsubscribe_event hooks.
> 
> This driver didn't and would therefore fail v4l2-compliance
> testing.
> 
> Add the relevant hooks.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
>  drivers/media/i2c/imx290.c | 10 +++++++++-
>  1 file changed, 9 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index bf96fd914303..12946ca9d8d2 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -20,6 +20,7 @@
>  #include <media/media-entity.h>
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-device.h>
> +#include <media/v4l2-event.h>
>  #include <media/v4l2-fwnode.h>
>  #include <media/v4l2-subdev.h>
>  
> @@ -977,6 +978,11 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
>  	return 0;
>  }
>  
> +static const struct v4l2_subdev_core_ops imx290_core_ops = {
> +	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
> +	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
> +};
> +
>  static const struct v4l2_subdev_video_ops imx290_video_ops = {
>  	.s_stream = imx290_set_stream,
>  };
> @@ -991,6 +997,7 @@ static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
>  };
>  
>  static const struct v4l2_subdev_ops imx290_subdev_ops = {
> +	.core = &imx290_core_ops,
>  	.video = &imx290_video_ops,
>  	.pad = &imx290_pad_ops,
>  };
> @@ -1009,7 +1016,8 @@ static int imx290_subdev_init(struct imx290 *imx290)
>  	imx290->current_mode = &imx290_modes_ptr(imx290)[0];
>  
>  	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> -	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
> +			    V4L2_SUBDEV_FL_HAS_EVENTS;
>  	imx290->sd.dev = imx290->dev;
>  	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
>  	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 04/11] media: i2c: imx290: Fix the pixel rate at 148.5Mpix/s
  2023-01-31 19:20 ` [PATCH 04/11] media: i2c: imx290: Fix the pixel rate at 148.5Mpix/s Dave Stevenson
@ 2023-02-02  8:13   ` Laurent Pinchart
  0 siblings, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02  8:13 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:09PM +0000, Dave Stevenson wrote:
> The datasheet lists the link frequency changes between
> 1080p and 720p modes. This is correct that the link frequency
> changes as measured on an oscilloscope.
> 
> Link frequency is not necessarily the same as pixel rate.

That's right. The model where

	htotal * vtotal * frame rate = pixel rate

applies to the pixel array. Things can be retimed when transmitted over
CSI-2 (or over anything, really), with pixels transmitted at a higher or
lower rate, with smaller or larger blanking, as long as it matches the
total horizontal line length in time units. I suppose this could in
theory be done vertically too, but that would require too much memory.

I hope we'll never have a need, on the receiver side, to know the active
line length in time units as sent over CSI-2 :-)

> The datasheet gives standard configurations for 1080p and 720p
> modes at a number of frame rates.
> Looking at the 1080p mode it gives:
> HMAX = 0x898 = 2200
> VMAX = 0x465 = 1125
> 2200 * 1125 * 60fps = 148.5MPix/s
> 
> Looking at the 720p mode it gives:
> HMAX = 0xce4 = 3300
> VMAX = 0x2ee = 750
> 3300 * 750 * 60fps = 148.5Mpix/s
> 
> This driver currently scales the pixel rate proportionally to the
> link frequency, however the above shows that this is not the
> correct thing to do, and currently all frame rate and exposure
> calculations give incorrect results.
> 
> Correctly report the pixel rate as being 148.5MPix/s under any
> mode.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 15 ++++-----------
>  1 file changed, 4 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 12946ca9d8d2..bd8729aed43c 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -107,6 +107,7 @@
>  
>  #define IMX290_VMAX_DEFAULT				1125
>  
> +#define IMX290_PIXEL_RATE				148500000

A blank line is missing here. With this fixed,

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

>  /*
>   * The IMX290 pixel array is organized as follows:
>   *
> @@ -190,7 +191,6 @@ struct imx290 {
>  
>  	struct v4l2_ctrl_handler ctrls;
>  	struct v4l2_ctrl *link_freq;
> -	struct v4l2_ctrl *pixel_rate;
>  	struct v4l2_ctrl *hblank;
>  	struct v4l2_ctrl *vblank;
>  };
> @@ -649,15 +649,8 @@ static void imx290_ctrl_update(struct imx290 *imx290,
>  {
>  	unsigned int hblank = mode->hmax - mode->width;
>  	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> -	s64 link_freq = imx290_link_freqs_ptr(imx290)[mode->link_freq_index];
> -	u64 pixel_rate;
> -
> -	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
> -	pixel_rate = link_freq * 2 * imx290->nlanes;
> -	do_div(pixel_rate, imx290_format_info(format->code)->bpp);
>  
>  	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> -	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, pixel_rate);
>  
>  	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
>  	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> @@ -707,9 +700,9 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  	if (imx290->link_freq)
>  		imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>  
> -	imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> -					       V4L2_CID_PIXEL_RATE,
> -					       1, INT_MAX, 1, 1);
> +	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_PIXEL_RATE,
> +			  IMX290_PIXEL_RATE, IMX290_PIXEL_RATE, 1,
> +			  IMX290_PIXEL_RATE);
>  
>  	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
>  				     V4L2_CID_TEST_PATTERN,

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 05/11] media: i2c: imx290: Support 60fps in 2 lane operation
  2023-01-31 19:20 ` [PATCH 05/11] media: i2c: imx290: Support 60fps in 2 lane operation Dave Stevenson
@ 2023-02-02 10:50   ` Laurent Pinchart
  2023-02-03  8:50   ` Alexander Stein
  1 sibling, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02 10:50 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:10PM +0000, Dave Stevenson wrote:
> Commit "97589ad61c73 media: i2c: imx290: Add support for 2 data lanes"
> added support for running in two lane mode (instead of 4), but
> without changing the link frequency that resulted in a max of 30fps.
> 
> Commit "98e0500eadb7 media: i2c: imx290: Add configurable link frequency
> and pixel rate" then doubled the link frequency when in 2 lane mode,
> but didn't undo the correction for running at only 30fps, just extending
> horizontal blanking instead.
> 
> Remove the 30fps limit on 2 lane by correcting the register config
> in accordance with the datasheet for 60fps operation over 2 lanes.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

Looks reasonable. We lose support for 30fps in 2 data lanes, but we get
a coherent behaviour, and we can implement support for lower frame rates
on top (which I suspect may be in further patches in this series :-)).

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
>  drivers/media/i2c/imx290.c | 17 +++--------------
>  1 file changed, 3 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index bd8729aed43c..6bcfa535872f 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -366,7 +366,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
>  	{
>  		.width = 1920,
>  		.height = 1080,
> -		.hmax = 4400,
> +		.hmax = 2200,
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> @@ -374,7 +374,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
>  	{
>  		.width = 1280,
>  		.height = 720,
> -		.hmax = 6600,
> +		.hmax = 3300,
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> @@ -518,21 +518,10 @@ static int imx290_set_register_array(struct imx290 *imx290,
>  static int imx290_set_data_lanes(struct imx290 *imx290)
>  {
>  	int ret = 0;
> -	u32 frsel;
> -
> -	switch (imx290->nlanes) {
> -	case 2:
> -	default:
> -		frsel = 0x02;
> -		break;
> -	case 4:
> -		frsel = 0x01;
> -		break;
> -	}
>  
>  	imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret);
>  	imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret);
> -	imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
> +	imx290_write(imx290, IMX290_FR_FDG_SEL, 0x01, &ret);
>  
>  	return ret;
>  }

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 06/11] media: i2c: imx290: Use CSI timings as per datasheet
  2023-01-31 19:20 ` [PATCH 06/11] media: i2c: imx290: Use CSI timings as per datasheet Dave Stevenson
@ 2023-02-02 14:36   ` Laurent Pinchart
  2023-02-03  8:04   ` Alexander Stein
  1 sibling, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02 14:36 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:11PM +0000, Dave Stevenson wrote:
> Commit "98e0500eadb7 media: i2c: imx290: Add configurable link frequency
> and pixel rate" added support for the increased link frequencies
> on 2 data lanes, but didn't update the CSI timing registers in
> accordance with the datasheet.
> 
> Use the specified settings.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 126 +++++++++++++++++++++++++++++++------
>  1 file changed, 106 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 6bcfa535872f..9ddd6382b127 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -174,6 +174,18 @@ struct imx290_mode {
>  	u32 data_size;
>  };
>  
> +struct imx290_csi_cfg {
> +	u16 repitition;

s/repitition/repetition/

I wonder what this it.

> +	u16 tclkpost;
> +	u16 thszero;
> +	u16 thsprepare;
> +	u16 tclktrail;
> +	u16 thstrail;
> +	u16 tclkzero;
> +	u16 tclkprepare;
> +	u16 tlpx;
> +};
> +
>  struct imx290 {
>  	struct device *dev;
>  	struct clk *xclk;
> @@ -273,16 +285,6 @@ static const struct imx290_regval imx290_1080p_settings[] = {
>  	{ IMX290_INCKSEL4, 0x01 },
>  	{ IMX290_INCKSEL5, 0x1a },
>  	{ IMX290_INCKSEL6, 0x1a },
> -	/* data rate settings */
> -	{ IMX290_REPETITION, 0x10 },
> -	{ IMX290_TCLKPOST, 87 },
> -	{ IMX290_THSZERO, 55 },
> -	{ IMX290_THSPREPARE, 31 },
> -	{ IMX290_TCLKTRAIL, 31 },
> -	{ IMX290_THSTRAIL, 31 },
> -	{ IMX290_TCLKZERO, 119 },
> -	{ IMX290_TCLKPREPARE, 31 },
> -	{ IMX290_TLPX, 23 },
>  };
>  
>  static const struct imx290_regval imx290_720p_settings[] = {
> @@ -298,16 +300,6 @@ static const struct imx290_regval imx290_720p_settings[] = {
>  	{ IMX290_INCKSEL4, 0x01 },
>  	{ IMX290_INCKSEL5, 0x1a },
>  	{ IMX290_INCKSEL6, 0x1a },
> -	/* data rate settings */
> -	{ IMX290_REPETITION, 0x10 },
> -	{ IMX290_TCLKPOST, 79 },
> -	{ IMX290_THSZERO, 47 },
> -	{ IMX290_THSPREPARE, 23 },
> -	{ IMX290_TCLKTRAIL, 23 },
> -	{ IMX290_THSTRAIL, 23 },
> -	{ IMX290_TCLKZERO, 87 },
> -	{ IMX290_TCLKPREPARE, 23 },
> -	{ IMX290_TLPX, 23 },
>  };
>  
>  static const struct imx290_regval imx290_10bit_settings[] = {
> @@ -328,6 +320,58 @@ static const struct imx290_regval imx290_12bit_settings[] = {
>  	{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
>  };
>  
> +static const struct imx290_csi_cfg imx290_csi_222_75mhz = {
> +	/* 222.25MHz or 445.5Mbit/s per lane */
> +	.repitition = 0x10,
> +	.tclkpost = 87,
> +	.thszero = 55,
> +	.thsprepare = 31,
> +	.tclktrail = 31,
> +	.thstrail = 31,
> +	.tclkzero = 119,
> +	.tclkprepare = 31,
> +	.tlpx = 23,
> +};
> +
> +static const struct imx290_csi_cfg imx290_csi_445_5mhz = {
> +	/* 445.5MHz or 891Mbit/s per lane */
> +	.repitition = 0x00,
> +	.tclkpost = 119,
> +	.thszero = 103,
> +	.thsprepare = 71,
> +	.tclktrail = 55,
> +	.thstrail = 63,
> +	.tclkzero = 255,
> +	.tclkprepare = 63,
> +	.tlpx = 55,
> +};
> +
> +static const struct imx290_csi_cfg imx290_csi_148_5mhz = {
> +	/* 148.5MHz or 297Mbit/s per lane */
> +	.repitition = 0x10,
> +	.tclkpost = 79,
> +	.thszero = 47,
> +	.thsprepare = 23,
> +	.tclktrail = 23,
> +	.thstrail = 23,
> +	.tclkzero = 87,
> +	.tclkprepare = 23,
> +	.tlpx = 23,
> +};
> +
> +static const struct imx290_csi_cfg imx290_csi_297mhz = {
> +	/* 297MHz or 594Mbit/s per lane */
> +	.repitition = 0x00,
> +	.tclkpost = 103,
> +	.thszero = 87,
> +	.thsprepare = 47,
> +	.tclktrail = 39,
> +	.thstrail = 47,
> +	.tclkzero = 191,
> +	.tclkprepare = 47,
> +	.tlpx = 39,
> +};
> +
>  /* supported link frequencies */
>  #define FREQ_INDEX_1080P	0
>  #define FREQ_INDEX_720P		1
> @@ -536,6 +580,42 @@ static int imx290_set_black_level(struct imx290 *imx290,
>  			    black_level >> (16 - bpp), err);
>  }
>  
> +static int imx290_set_csi_config(struct imx290 *imx290)
> +{
> +	const s64 *link_freqs = imx290_link_freqs_ptr(imx290);
> +	const struct imx290_csi_cfg *csi_cfg;
> +	int ret = 0;
> +
> +	switch (link_freqs[imx290->current_mode->link_freq_index]) {
> +	case 445500000:
> +		csi_cfg = &imx290_csi_445_5mhz;
> +		break;
> +	case 297000000:
> +		csi_cfg = &imx290_csi_297mhz;
> +		break;
> +	case 222750000:
> +		csi_cfg = &imx290_csi_222_75mhz;
> +		break;
> +	case 148500000:
> +		csi_cfg = &imx290_csi_148_5mhz;
> +		break;

There's probably a way to rework the link_freqs arrays and FREQ_INDEX_*
macros to simplify the constructs and avoid a switch based on the
frequency value, but that can also be done later.

> +	default:
> +		return -EINVAL;
> +	}
> +
> +	imx290_write(imx290, IMX290_REPETITION, csi_cfg->repitition, &ret);
> +	imx290_write(imx290, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret);
> +	imx290_write(imx290, IMX290_THSZERO, csi_cfg->thszero, &ret);
> +	imx290_write(imx290, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret);
> +	imx290_write(imx290, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret);
> +	imx290_write(imx290, IMX290_THSTRAIL, csi_cfg->thstrail, &ret);
> +	imx290_write(imx290, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret);
> +	imx290_write(imx290, IMX290_TCLKPREPARE, csi_cfg->tclkprepare, &ret);
> +	imx290_write(imx290, IMX290_TLPX, csi_cfg->tlpx, &ret);
> +
> +	return ret;
> +}
> +
>  static int imx290_setup_format(struct imx290 *imx290,
>  			       const struct v4l2_mbus_framefmt *format)
>  {
> @@ -748,6 +828,12 @@ static int imx290_start_streaming(struct imx290 *imx290,
>  		return ret;
>  	}
>  
> +	ret = imx290_set_csi_config(imx290);
> +	if (ret < 0) {
> +		dev_err(imx290->dev, "Could not set csi cfg\n");

Maybe adding the error code to the message ?

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> +		return ret;
> +	}
> +
>  	/* Apply the register values related to current frame format */
>  	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
>  	ret = imx290_setup_format(imx290, format);

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write
  2023-01-31 19:20 ` [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write Dave Stevenson
@ 2023-02-02 14:58   ` Laurent Pinchart
  2023-02-02 15:48     ` Dave Stevenson
  2023-02-03  7:19   ` Alexander Stein
  1 sibling, 1 reply; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02 14:58 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:12PM +0000, Dave Stevenson wrote:
> The driver exposed V4L2_CID_HBLANK as a read only control to allow
> for exposure calculations and determination of the frame rate.
> 
> Convert to a read/write control so that the frame rate can be
> controlled.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 33 +++++++++++++++++++--------------
>  1 file changed, 19 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 9ddd6382b127..9006be6e5e7c 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -47,6 +47,7 @@
>  #define IMX290_GAIN					IMX290_REG_8BIT(0x3014)
>  #define IMX290_VMAX					IMX290_REG_24BIT(0x3018)
>  #define IMX290_HMAX					IMX290_REG_16BIT(0x301c)
> +#define IMX290_HMAX_MAX					0xffff
>  #define IMX290_SHS1					IMX290_REG_24BIT(0x3020)
>  #define IMX290_WINWV_OB					IMX290_REG_8BIT(0x303a)
>  #define IMX290_WINPV					IMX290_REG_16BIT(0x303c)
> @@ -167,7 +168,7 @@ struct imx290_regval {
>  struct imx290_mode {
>  	u32 width;
>  	u32 height;
> -	u32 hmax;
> +	u32 hmax_min;
>  	u8 link_freq_index;
>  
>  	const struct imx290_regval *data;
> @@ -410,7 +411,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
>  	{
>  		.width = 1920,
>  		.height = 1080,
> -		.hmax = 2200,
> +		.hmax_min = 2200,

I didn't find in the documentation a mention that these values are the
minimum the sensor supports. Did you use them as such based on the fact
that anything higher than the nominal hblank value reported by the
datasheet is hopefully guaranteed to work, and lower values are
uncharted territory, or are these the real minimums ? I'm fine using
them as minimum for now even if we could possibly go lower, leaving that
for future patches. I would however mention this in a comment or in the
commit message.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> @@ -418,7 +419,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
>  	{
>  		.width = 1280,
>  		.height = 720,
> -		.hmax = 3300,
> +		.hmax_min = 3300,
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> @@ -429,7 +430,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
>  	{
>  		.width = 1920,
>  		.height = 1080,
> -		.hmax = 2200,
> +		.hmax_min = 2200,
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> @@ -437,7 +438,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
>  	{
>  		.width = 1280,
>  		.height = 720,
> -		.hmax = 3300,
> +		.hmax_min = 3300,
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> @@ -686,6 +687,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  		}
>  		break;
>  
> +	case V4L2_CID_HBLANK:
> +		ret = imx290_write(imx290, IMX290_HMAX,
> +				   ctrl->val + imx290->current_mode->width,
> +				   NULL);
> +		break;
> +
>  	default:
>  		ret = -EINVAL;
>  		break;
> @@ -716,12 +723,14 @@ static void imx290_ctrl_update(struct imx290 *imx290,
>  			       const struct v4l2_mbus_framefmt *format,
>  			       const struct imx290_mode *mode)
>  {
> -	unsigned int hblank = mode->hmax - mode->width;
> +	unsigned int hblank_min = mode->hmax_min - mode->width;
> +	unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
>  	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
>  
>  	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
>  
> -	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
> +	__v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
> +				 hblank_min);
>  	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
>  }
>  
> @@ -778,10 +787,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  				     ARRAY_SIZE(imx290_test_pattern_menu) - 1,
>  				     0, 0, imx290_test_pattern_menu);
>  
> +	/*
> +	 * Actual range will be set from imx290_ctrl_update later in the probe.
> +	 */
>  	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>  					   V4L2_CID_HBLANK, 1, 1, 1, 1);
> -	if (imx290->hblank)
> -		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>  
>  	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>  					   V4L2_CID_VBLANK, 1, 1, 1, 1);
> @@ -850,11 +860,6 @@ static int imx290_start_streaming(struct imx290 *imx290,
>  		return ret;
>  	}
>  
> -	ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
> -			   NULL);
> -	if (ret)
> -		return ret;
> -
>  	/* Apply customized values from user */
>  	ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
>  	if (ret) {

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK to read/write
  2023-01-31 19:20 ` [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK " Dave Stevenson
@ 2023-02-02 15:40   ` Alexander Stein
  2023-02-02 16:04     ` Dave Stevenson
  2023-02-02 16:01   ` Laurent Pinchart
  1 sibling, 1 reply; 49+ messages in thread
From: Alexander Stein @ 2023-02-02 15:40 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Dave Stevenson

Hi Dave,

thanks for working on this.

Am Dienstag, 31. Januar 2023, 20:20:13 CET schrieb Dave Stevenson:
> The driver exposed V4L2_CID_HBLANK as a read only control to allow
> for exposure calculations and determination of the frame rate.
> 
> Convert to a read/write control so that the frame rate can be
> controlled.
> V4L2_CID_VBLANK also sets the limits for the exposure control,
> therefore exposure ranges have to be updated when vblank changes.
> 
> This also updates the default VMAX in 720p mode from 1125 to
> 750 to achieve 60fps and follow the datasheet.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 57 ++++++++++++++++++++++++++++++--------
>  1 file changed, 45 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 9006be6e5e7c..3413d83369ba 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -46,6 +46,7 @@
>  #define IMX290_BLKLEVEL					
IMX290_REG_16BIT(0x300a)
>  #define IMX290_GAIN					
IMX290_REG_8BIT(0x3014)
>  #define IMX290_VMAX					
IMX290_REG_24BIT(0x3018)
> +#define IMX290_VMAX_MAX					0x3ffff
>  #define IMX290_HMAX					
IMX290_REG_16BIT(0x301c)
>  #define IMX290_HMAX_MAX					0xffff
>  #define IMX290_SHS1					
IMX290_REG_24BIT(0x3020)
> @@ -106,8 +107,6 @@
>  #define IMX290_PGCTRL_THRU				BIT(1)
>  #define IMX290_PGCTRL_MODE(n)				((n) << 
4)
> 
> -#define IMX290_VMAX_DEFAULT				1125
> -
>  #define IMX290_PIXEL_RATE				148500000
>  /*
>   * The IMX290 pixel array is organized as follows:
> @@ -169,6 +168,7 @@ struct imx290_mode {
>  	u32 width;
>  	u32 height;
>  	u32 hmax_min;
> +	u32 vmax_min;
>  	u8 link_freq_index;
> 
>  	const struct imx290_regval *data;
> @@ -206,6 +206,7 @@ struct imx290 {
>  	struct v4l2_ctrl *link_freq;
>  	struct v4l2_ctrl *hblank;
>  	struct v4l2_ctrl *vblank;
> +	struct v4l2_ctrl *exposure;
>  };
> 
>  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> @@ -219,7 +220,6 @@ static inline struct imx290 *to_imx290(struct
> v4l2_subdev *_sd)
> 
>  static const struct imx290_regval imx290_global_init_settings[] = {
>  	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
> -	{ IMX290_VMAX, IMX290_VMAX_DEFAULT },
>  	{ IMX290_EXTCK_FREQ, 0x2520 },
>  	{ IMX290_WINWV_OB, 12 },
>  	{ IMX290_WINPH, 0 },
> @@ -412,6 +412,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> { .width = 1920,
>  		.height = 1080,
>  		.hmax_min = 2200,
> +		.vmax_min = 1125,
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> @@ -420,6 +421,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> { .width = 1280,
>  		.height = 720,
>  		.hmax_min = 3300,
> +		.vmax_min = 750,
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> @@ -431,6 +433,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> { .width = 1920,
>  		.height = 1080,
>  		.hmax_min = 2200,
> +		.vmax_min = 1125,
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> @@ -439,6 +442,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> { .width = 1280,
>  		.height = 720,
>  		.hmax_min = 3300,
> +		.vmax_min = 750,
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> @@ -645,7 +649,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  					     struct imx290, ctrls);
>  	const struct v4l2_mbus_framefmt *format;
>  	struct v4l2_subdev_state *state;
> -	int ret = 0;
> +	int ret = 0, vmax;
> 
>  	/*
>  	 * Return immediately for controls that don't need to be applied to 
the
> @@ -654,6 +658,18 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  	if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
>  		return 0;
> 
> +	if (ctrl->id == V4L2_CID_VBLANK) {
> +		u32 vmax = ctrl->val + imx290->current_mode->height;
> +
> +		/*
> +		 * Changing vblank changes the allowed range for exposure.
> +		 * We don't supply the current exposure as default here as 
it
> +		 * may lie outside the new range. We will reset it just 
below.
> +		 */
> +		__v4l2_ctrl_modify_range(imx290->exposure,
> +					 1, vmax - 2, 1, vmax - 2);
> +	}
> +
>  	/* V4L2 controls values will be applied only when power is already 
up */
>  	if (!pm_runtime_get_if_in_use(imx290->dev))
>  		return 0;
> @@ -666,9 +682,23 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  		ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
>  		break;
> 
> +	case V4L2_CID_VBLANK:
> +		ret = imx290_write(imx290, IMX290_VMAX,
> +				   ctrl->val + imx290->current_mode-
>height,
> +				   NULL);
> +		/*
> +		 * Due to the way that exposure is programmed in this 
sensor in
> +		 * relation to VMAX, we have to reprogramme it whenever 
VMAX is
> +		 * changed.
> +		 * Update ctrl so that the V4L2_CID_EXPOSURE case can 
refer to
> +		 * it.
> +		 */
> +		ctrl = imx290->exposure;
> +		fallthrough;
>  	case V4L2_CID_EXPOSURE:
> +		vmax = imx290->vblank->val + imx290->current_mode->height;
>  		ret = imx290_write(imx290, IMX290_SHS1,
> -				   IMX290_VMAX_DEFAULT - ctrl->val - 
1, NULL);
> +				   vmax - ctrl->val - 1, NULL);
>  		break;
> 
>  	case V4L2_CID_TEST_PATTERN:
> @@ -725,13 +755,15 @@ static void imx290_ctrl_update(struct imx290 *imx290,
>  {
>  	unsigned int hblank_min = mode->hmax_min - mode->width;
>  	unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
> -	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> +	unsigned int vblank_min = mode->vmax_min - mode->height;
> +	unsigned int vblank_max = IMX290_VMAX_MAX - mode->height;
> 
>  	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> 
>  	__v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
>  				 hblank_min);
> -	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> +	__v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max, 1,
> +				 vblank_min);

You are missing setting the range for V4L2_CID_EXPOSURE. So it stays at 1 
resulting in a black image.

Best regards,
Alexander

>  }
> 
>  static int imx290_ctrl_init(struct imx290 *imx290)
> @@ -761,9 +793,12 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>  			  V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
> 
> -	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> -			  V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 
1,
> -			  IMX290_VMAX_DEFAULT - 2);
> +	/*
> +	 * Correct range will be determined through imx290_ctrl_update 
setting
> +	 * V4L2_CID_VBLANK.
> +	 */
> +	imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls, 
&imx290_ctrl_ops,
> +					     V4L2_CID_EXPOSURE, 1, 
1, 1, 1);
> 
>  	/*
>  	 * Set the link frequency, pixel rate, horizontal blanking and 
vertical
> @@ -795,8 +830,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> 
>  	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>  					   V4L2_CID_VBLANK, 1, 1, 1, 
1);
> -	if (imx290->vblank)
> -		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> 
>  	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
>  					&props);





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

* Re: [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write
  2023-02-02 14:58   ` Laurent Pinchart
@ 2023-02-02 15:48     ` Dave Stevenson
  2023-02-02 22:14       ` Laurent Pinchart
  0 siblings, 1 reply; 49+ messages in thread
From: Dave Stevenson @ 2023-02-02 15:48 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Laurent

On Thu, 2 Feb 2023 at 14:58, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Dave,
>
> Thank you for the patch.
>
> On Tue, Jan 31, 2023 at 07:20:12PM +0000, Dave Stevenson wrote:
> > The driver exposed V4L2_CID_HBLANK as a read only control to allow
> > for exposure calculations and determination of the frame rate.
> >
> > Convert to a read/write control so that the frame rate can be
> > controlled.
> >
> > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > ---
> >  drivers/media/i2c/imx290.c | 33 +++++++++++++++++++--------------
> >  1 file changed, 19 insertions(+), 14 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 9ddd6382b127..9006be6e5e7c 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -47,6 +47,7 @@
> >  #define IMX290_GAIN                                  IMX290_REG_8BIT(0x3014)
> >  #define IMX290_VMAX                                  IMX290_REG_24BIT(0x3018)
> >  #define IMX290_HMAX                                  IMX290_REG_16BIT(0x301c)
> > +#define IMX290_HMAX_MAX                                      0xffff
> >  #define IMX290_SHS1                                  IMX290_REG_24BIT(0x3020)
> >  #define IMX290_WINWV_OB                                      IMX290_REG_8BIT(0x303a)
> >  #define IMX290_WINPV                                 IMX290_REG_16BIT(0x303c)
> > @@ -167,7 +168,7 @@ struct imx290_regval {
> >  struct imx290_mode {
> >       u32 width;
> >       u32 height;
> > -     u32 hmax;
> > +     u32 hmax_min;
> >       u8 link_freq_index;
> >
> >       const struct imx290_regval *data;
> > @@ -410,7 +411,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
> >       {
> >               .width = 1920,
> >               .height = 1080,
> > -             .hmax = 2200,
> > +             .hmax_min = 2200,
>
> I didn't find in the documentation a mention that these values are the
> minimum the sensor supports. Did you use them as such based on the fact
> that anything higher than the nominal hblank value reported by the
> datasheet is hopefully guaranteed to work, and lower values are
> uncharted territory, or are these the real minimums ? I'm fine using
> them as minimum for now even if we could possibly go lower, leaving that
> for future patches. I would however mention this in a comment or in the
> commit message.

The datasheet shows varying HMAX to control frame rate.
Table "List of Setting Register for CSI-2 serial output" for all-pixel
mode gives HMAX values of :
0x14a0 for 25fps
0x1130 for 30fps
0x0a50 for 50fps
0x0898 for 60 fps
0x0528 for 100fps (needs FRSEL = 0 and higher link frequency)
0x044c for 120fps (ditto)

VMAX is fixed at 0x465 (1125 decimal) for all frame rates in all-pixel
(1080p) mode.

The 2200 listed here is the 0x898 for 60fps - the nominal max with the
configured lane / link frequency combinations.

Doing the maths, 4 lanes at 445.5Mbit/s/lane = 1782Mbit/s.
2200 * 1125 * 12bpp * 60fps = 1782Mbit/s. Pixel clock is therefore the
same as link bandwidth at this point. I have no information about
blanking and clock lane behaviour for this sensor, but can believe
that it needs that time for low level CSI2 transitions.

AIUI you'll only be able to decrease this further if you add support
for 891Mbit/s/lane (445.5MHz link freq) on 4 lanes, and drop to 10 bit
readout. Programming HMAX also then becomes more entertaining as it
appears to be half the expected value (0x44c = 1100 decimal, which is
smaller than the width), so it'll need a fair amount of messing to get
all the controls to behave as expected.
That's all outside the scope of this patch set - 60fps was the only
frame rate previously supported, and we've expanded on that with
slower frame rate support.


FWIW I have checked with Sony over the 120fps modes, and experimented a little.
Sony will only support 120fps in 10bit. The flyer for IMX462 that
implies it does 1920x1080@120fps in 12 bit mode is incorrect.
Experimentation had 720p120 working in 12bit, but trying to work
through limiting the various bit depth and resolution options is going
to get really ugly in the driver.

  Dave

> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>
> >               .link_freq_index = FREQ_INDEX_1080P,
> >               .data = imx290_1080p_settings,
> >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > @@ -418,7 +419,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
> >       {
> >               .width = 1280,
> >               .height = 720,
> > -             .hmax = 3300,
> > +             .hmax_min = 3300,
> >               .link_freq_index = FREQ_INDEX_720P,
> >               .data = imx290_720p_settings,
> >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > @@ -429,7 +430,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
> >       {
> >               .width = 1920,
> >               .height = 1080,
> > -             .hmax = 2200,
> > +             .hmax_min = 2200,
> >               .link_freq_index = FREQ_INDEX_1080P,
> >               .data = imx290_1080p_settings,
> >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > @@ -437,7 +438,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
> >       {
> >               .width = 1280,
> >               .height = 720,
> > -             .hmax = 3300,
> > +             .hmax_min = 3300,
> >               .link_freq_index = FREQ_INDEX_720P,
> >               .data = imx290_720p_settings,
> >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > @@ -686,6 +687,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> >               }
> >               break;
> >
> > +     case V4L2_CID_HBLANK:
> > +             ret = imx290_write(imx290, IMX290_HMAX,
> > +                                ctrl->val + imx290->current_mode->width,
> > +                                NULL);
> > +             break;
> > +
> >       default:
> >               ret = -EINVAL;
> >               break;
> > @@ -716,12 +723,14 @@ static void imx290_ctrl_update(struct imx290 *imx290,
> >                              const struct v4l2_mbus_framefmt *format,
> >                              const struct imx290_mode *mode)
> >  {
> > -     unsigned int hblank = mode->hmax - mode->width;
> > +     unsigned int hblank_min = mode->hmax_min - mode->width;
> > +     unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
> >       unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> >
> >       __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> >
> > -     __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
> > +     __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
> > +                              hblank_min);
> >       __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> >  }
> >
> > @@ -778,10 +787,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >                                    ARRAY_SIZE(imx290_test_pattern_menu) - 1,
> >                                    0, 0, imx290_test_pattern_menu);
> >
> > +     /*
> > +      * Actual range will be set from imx290_ctrl_update later in the probe.
> > +      */
> >       imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> >                                          V4L2_CID_HBLANK, 1, 1, 1, 1);
> > -     if (imx290->hblank)
> > -             imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> >
> >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> >                                          V4L2_CID_VBLANK, 1, 1, 1, 1);
> > @@ -850,11 +860,6 @@ static int imx290_start_streaming(struct imx290 *imx290,
> >               return ret;
> >       }
> >
> > -     ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
> > -                        NULL);
> > -     if (ret)
> > -             return ret;
> > -
> >       /* Apply customized values from user */
> >       ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> >       if (ret) {
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK to read/write
  2023-01-31 19:20 ` [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK " Dave Stevenson
  2023-02-02 15:40   ` Alexander Stein
@ 2023-02-02 16:01   ` Laurent Pinchart
  1 sibling, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02 16:01 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:13PM +0000, Dave Stevenson wrote:
> The driver exposed V4L2_CID_HBLANK as a read only control to allow

s/HBLANK/VBLANK/

> for exposure calculations and determination of the frame rate.
> 
> Convert to a read/write control so that the frame rate can be
> controlled.
> V4L2_CID_VBLANK also sets the limits for the exposure control,
> therefore exposure ranges have to be updated when vblank changes.
> 
> This also updates the default VMAX in 720p mode from 1125 to
> 750 to achieve 60fps and follow the datasheet.

I would have split this to a separate patch.

> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 57 ++++++++++++++++++++++++++++++--------
>  1 file changed, 45 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 9006be6e5e7c..3413d83369ba 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -46,6 +46,7 @@
>  #define IMX290_BLKLEVEL					IMX290_REG_16BIT(0x300a)
>  #define IMX290_GAIN					IMX290_REG_8BIT(0x3014)
>  #define IMX290_VMAX					IMX290_REG_24BIT(0x3018)
> +#define IMX290_VMAX_MAX					0x3ffff
>  #define IMX290_HMAX					IMX290_REG_16BIT(0x301c)
>  #define IMX290_HMAX_MAX					0xffff
>  #define IMX290_SHS1					IMX290_REG_24BIT(0x3020)
> @@ -106,8 +107,6 @@
>  #define IMX290_PGCTRL_THRU				BIT(1)
>  #define IMX290_PGCTRL_MODE(n)				((n) << 4)
>  
> -#define IMX290_VMAX_DEFAULT				1125
> -
>  #define IMX290_PIXEL_RATE				148500000
>  /*
>   * The IMX290 pixel array is organized as follows:
> @@ -169,6 +168,7 @@ struct imx290_mode {
>  	u32 width;
>  	u32 height;
>  	u32 hmax_min;
> +	u32 vmax_min;
>  	u8 link_freq_index;
>  
>  	const struct imx290_regval *data;
> @@ -206,6 +206,7 @@ struct imx290 {
>  	struct v4l2_ctrl *link_freq;
>  	struct v4l2_ctrl *hblank;
>  	struct v4l2_ctrl *vblank;
> +	struct v4l2_ctrl *exposure;
>  };
>  
>  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> @@ -219,7 +220,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
>  
>  static const struct imx290_regval imx290_global_init_settings[] = {
>  	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
> -	{ IMX290_VMAX, IMX290_VMAX_DEFAULT },
>  	{ IMX290_EXTCK_FREQ, 0x2520 },
>  	{ IMX290_WINWV_OB, 12 },
>  	{ IMX290_WINPH, 0 },
> @@ -412,6 +412,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
>  		.width = 1920,
>  		.height = 1080,
>  		.hmax_min = 2200,
> +		.vmax_min = 1125,
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> @@ -420,6 +421,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
>  		.width = 1280,
>  		.height = 720,
>  		.hmax_min = 3300,
> +		.vmax_min = 750,
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> @@ -431,6 +433,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
>  		.width = 1920,
>  		.height = 1080,
>  		.hmax_min = 2200,
> +		.vmax_min = 1125,
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> @@ -439,6 +442,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
>  		.width = 1280,
>  		.height = 720,
>  		.hmax_min = 3300,
> +		.vmax_min = 750,
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> @@ -645,7 +649,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  					     struct imx290, ctrls);
>  	const struct v4l2_mbus_framefmt *format;
>  	struct v4l2_subdev_state *state;
> -	int ret = 0;
> +	int ret = 0, vmax;
>  
>  	/*
>  	 * Return immediately for controls that don't need to be applied to the
> @@ -654,6 +658,18 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  	if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
>  		return 0;
>  
> +	if (ctrl->id == V4L2_CID_VBLANK) {
> +		u32 vmax = ctrl->val + imx290->current_mode->height;
> +
> +		/*
> +		 * Changing vblank changes the allowed range for exposure.
> +		 * We don't supply the current exposure as default here as it
> +		 * may lie outside the new range. We will reset it just below.
> +		 */
> +		__v4l2_ctrl_modify_range(imx290->exposure,
> +					 1, vmax - 2, 1, vmax - 2);
> +	}
> +
>  	/* V4L2 controls values will be applied only when power is already up */
>  	if (!pm_runtime_get_if_in_use(imx290->dev))
>  		return 0;
> @@ -666,9 +682,23 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  		ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
>  		break;
>  
> +	case V4L2_CID_VBLANK:
> +		ret = imx290_write(imx290, IMX290_VMAX,
> +				   ctrl->val + imx290->current_mode->height,
> +				   NULL);
> +		/*
> +		 * Due to the way that exposure is programmed in this sensor in
> +		 * relation to VMAX, we have to reprogramme it whenever VMAX is
> +		 * changed.
> +		 * Update ctrl so that the V4L2_CID_EXPOSURE case can refer to
> +		 * it.
> +		 */
> +		ctrl = imx290->exposure;

You could skip this and use imx290->exposure below instead. I see pros
and cons, up to you.

> +		fallthrough;
>  	case V4L2_CID_EXPOSURE:
> +		vmax = imx290->vblank->val + imx290->current_mode->height;
>  		ret = imx290_write(imx290, IMX290_SHS1,
> -				   IMX290_VMAX_DEFAULT - ctrl->val - 1, NULL);
> +				   vmax - ctrl->val - 1, NULL);
>  		break;

The relationship between blanking and exposure is painful to handle :-(
Simplifying it is probably my number one wish for a good V4L2 raw sensor
helper. Just a bit of ranting, not a candidate for this series of
course.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

>  
>  	case V4L2_CID_TEST_PATTERN:
> @@ -725,13 +755,15 @@ static void imx290_ctrl_update(struct imx290 *imx290,
>  {
>  	unsigned int hblank_min = mode->hmax_min - mode->width;
>  	unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
> -	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> +	unsigned int vblank_min = mode->vmax_min - mode->height;
> +	unsigned int vblank_max = IMX290_VMAX_MAX - mode->height;
>  
>  	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
>  
>  	__v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
>  				 hblank_min);
> -	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> +	__v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max, 1,
> +				 vblank_min);
>  }
>  
>  static int imx290_ctrl_init(struct imx290 *imx290)
> @@ -761,9 +793,12 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>  			  V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
>  
> -	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> -			  V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
> -			  IMX290_VMAX_DEFAULT - 2);
> +	/*
> +	 * Correct range will be determined through imx290_ctrl_update setting
> +	 * V4L2_CID_VBLANK.
> +	 */
> +	imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> +					     V4L2_CID_EXPOSURE, 1, 1, 1, 1);
>  
>  	/*
>  	 * Set the link frequency, pixel rate, horizontal blanking and vertical
> @@ -795,8 +830,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  
>  	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>  					   V4L2_CID_VBLANK, 1, 1, 1, 1);
> -	if (imx290->vblank)
> -		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>  
>  	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
>  					&props);

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK to read/write
  2023-02-02 15:40   ` Alexander Stein
@ 2023-02-02 16:04     ` Dave Stevenson
  2023-02-02 17:42       ` Dave Stevenson
  0 siblings, 1 reply; 49+ messages in thread
From: Dave Stevenson @ 2023-02-02 16:04 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media

Hi Alexander

On Thu, 2 Feb 2023 at 15:40, Alexander Stein
<alexander.stein@ew.tq-group.com> wrote:
>
> Hi Dave,
>
> thanks for working on this.
>
> Am Dienstag, 31. Januar 2023, 20:20:13 CET schrieb Dave Stevenson:
> > The driver exposed V4L2_CID_HBLANK as a read only control to allow
> > for exposure calculations and determination of the frame rate.
> >
> > Convert to a read/write control so that the frame rate can be
> > controlled.
> > V4L2_CID_VBLANK also sets the limits for the exposure control,
> > therefore exposure ranges have to be updated when vblank changes.
> >
> > This also updates the default VMAX in 720p mode from 1125 to
> > 750 to achieve 60fps and follow the datasheet.
> >
> > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > ---
> >  drivers/media/i2c/imx290.c | 57 ++++++++++++++++++++++++++++++--------
> >  1 file changed, 45 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 9006be6e5e7c..3413d83369ba 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -46,6 +46,7 @@
> >  #define IMX290_BLKLEVEL
> IMX290_REG_16BIT(0x300a)
> >  #define IMX290_GAIN
> IMX290_REG_8BIT(0x3014)
> >  #define IMX290_VMAX
> IMX290_REG_24BIT(0x3018)
> > +#define IMX290_VMAX_MAX                                      0x3ffff
> >  #define IMX290_HMAX
> IMX290_REG_16BIT(0x301c)
> >  #define IMX290_HMAX_MAX                                      0xffff
> >  #define IMX290_SHS1
> IMX290_REG_24BIT(0x3020)
> > @@ -106,8 +107,6 @@
> >  #define IMX290_PGCTRL_THRU                           BIT(1)
> >  #define IMX290_PGCTRL_MODE(n)                                ((n) <<
> 4)
> >
> > -#define IMX290_VMAX_DEFAULT                          1125
> > -
> >  #define IMX290_PIXEL_RATE                            148500000
> >  /*
> >   * The IMX290 pixel array is organized as follows:
> > @@ -169,6 +168,7 @@ struct imx290_mode {
> >       u32 width;
> >       u32 height;
> >       u32 hmax_min;
> > +     u32 vmax_min;
> >       u8 link_freq_index;
> >
> >       const struct imx290_regval *data;
> > @@ -206,6 +206,7 @@ struct imx290 {
> >       struct v4l2_ctrl *link_freq;
> >       struct v4l2_ctrl *hblank;
> >       struct v4l2_ctrl *vblank;
> > +     struct v4l2_ctrl *exposure;
> >  };
> >
> >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > @@ -219,7 +220,6 @@ static inline struct imx290 *to_imx290(struct
> > v4l2_subdev *_sd)
> >
> >  static const struct imx290_regval imx290_global_init_settings[] = {
> >       { IMX290_CTRL_07, IMX290_WINMODE_1080P },
> > -     { IMX290_VMAX, IMX290_VMAX_DEFAULT },
> >       { IMX290_EXTCK_FREQ, 0x2520 },
> >       { IMX290_WINWV_OB, 12 },
> >       { IMX290_WINPH, 0 },
> > @@ -412,6 +412,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> > { .width = 1920,
> >               .height = 1080,
> >               .hmax_min = 2200,
> > +             .vmax_min = 1125,
> >               .link_freq_index = FREQ_INDEX_1080P,
> >               .data = imx290_1080p_settings,
> >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > @@ -420,6 +421,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> > { .width = 1280,
> >               .height = 720,
> >               .hmax_min = 3300,
> > +             .vmax_min = 750,
> >               .link_freq_index = FREQ_INDEX_720P,
> >               .data = imx290_720p_settings,
> >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > @@ -431,6 +433,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> > { .width = 1920,
> >               .height = 1080,
> >               .hmax_min = 2200,
> > +             .vmax_min = 1125,
> >               .link_freq_index = FREQ_INDEX_1080P,
> >               .data = imx290_1080p_settings,
> >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > @@ -439,6 +442,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> > { .width = 1280,
> >               .height = 720,
> >               .hmax_min = 3300,
> > +             .vmax_min = 750,
> >               .link_freq_index = FREQ_INDEX_720P,
> >               .data = imx290_720p_settings,
> >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > @@ -645,7 +649,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> >                                            struct imx290, ctrls);
> >       const struct v4l2_mbus_framefmt *format;
> >       struct v4l2_subdev_state *state;
> > -     int ret = 0;
> > +     int ret = 0, vmax;
> >
> >       /*
> >        * Return immediately for controls that don't need to be applied to
> the
> > @@ -654,6 +658,18 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> >       if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
> >               return 0;
> >
> > +     if (ctrl->id == V4L2_CID_VBLANK) {
> > +             u32 vmax = ctrl->val + imx290->current_mode->height;
> > +
> > +             /*
> > +              * Changing vblank changes the allowed range for exposure.
> > +              * We don't supply the current exposure as default here as
> it
> > +              * may lie outside the new range. We will reset it just
> below.
> > +              */
> > +             __v4l2_ctrl_modify_range(imx290->exposure,
> > +                                      1, vmax - 2, 1, vmax - 2);
> > +     }
> > +
> >       /* V4L2 controls values will be applied only when power is already
> up */
> >       if (!pm_runtime_get_if_in_use(imx290->dev))
> >               return 0;
> > @@ -666,9 +682,23 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> >               ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
> >               break;
> >
> > +     case V4L2_CID_VBLANK:
> > +             ret = imx290_write(imx290, IMX290_VMAX,
> > +                                ctrl->val + imx290->current_mode-
> >height,
> > +                                NULL);
> > +             /*
> > +              * Due to the way that exposure is programmed in this
> sensor in
> > +              * relation to VMAX, we have to reprogramme it whenever
> VMAX is
> > +              * changed.
> > +              * Update ctrl so that the V4L2_CID_EXPOSURE case can
> refer to
> > +              * it.
> > +              */
> > +             ctrl = imx290->exposure;
> > +             fallthrough;
> >       case V4L2_CID_EXPOSURE:
> > +             vmax = imx290->vblank->val + imx290->current_mode->height;
> >               ret = imx290_write(imx290, IMX290_SHS1,
> > -                                IMX290_VMAX_DEFAULT - ctrl->val -
> 1, NULL);
> > +                                vmax - ctrl->val - 1, NULL);
> >               break;
> >
> >       case V4L2_CID_TEST_PATTERN:
> > @@ -725,13 +755,15 @@ static void imx290_ctrl_update(struct imx290 *imx290,
> >  {
> >       unsigned int hblank_min = mode->hmax_min - mode->width;
> >       unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
> > -     unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> > +     unsigned int vblank_min = mode->vmax_min - mode->height;
> > +     unsigned int vblank_max = IMX290_VMAX_MAX - mode->height;
> >
> >       __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> >
> >       __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
> >                                hblank_min);
> > -     __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> > +     __v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max, 1,
> > +                              vblank_min);
>
> You are missing setting the range for V4L2_CID_EXPOSURE. So it stays at 1
> resulting in a black image.

You're right.
I'm always testing with libcamera which will set V4L2_CID_VBLANK to
configure the frame rate, and that will update the exposure range.

  Dave

> Best regards,
> Alexander
>
> >  }
> >
> >  static int imx290_ctrl_init(struct imx290 *imx290)
> > @@ -761,9 +793,12 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >       v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> >                         V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
> >
> > -     v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > -                       V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2,
> 1,
> > -                       IMX290_VMAX_DEFAULT - 2);
> > +     /*
> > +      * Correct range will be determined through imx290_ctrl_update
> setting
> > +      * V4L2_CID_VBLANK.
> > +      */
> > +     imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls,
> &imx290_ctrl_ops,
> > +                                          V4L2_CID_EXPOSURE, 1,
> 1, 1, 1);
> >
> >       /*
> >        * Set the link frequency, pixel rate, horizontal blanking and
> vertical
> > @@ -795,8 +830,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >
> >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> >                                          V4L2_CID_VBLANK, 1, 1, 1,
> 1);
> > -     if (imx290->vblank)
> > -             imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> >
> >       v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
> >                                       &props);
>
>
>
>

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

* Re: [PATCH 09/11] media: i2c: imx290: Remove duplicated write to IMX290_CTRL_07
  2023-01-31 19:20 ` [PATCH 09/11] media: i2c: imx290: Remove duplicated write to IMX290_CTRL_07 Dave Stevenson
@ 2023-02-02 16:04   ` Laurent Pinchart
  2023-02-03  7:45   ` Alexander Stein
  1 sibling, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02 16:04 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:14PM +0000, Dave Stevenson wrote:
> IMX290_CTRL_07 was written from both imx290_global_init_settings
> and imx290_1080p_settings and imx290_720p_settings.
> 
> Remove it from imx290_global_init_settings as the setting varies
> based on the mode.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
>  drivers/media/i2c/imx290.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 3413d83369ba..5202ef3cc3e6 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -219,7 +219,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
>   */
>  
>  static const struct imx290_regval imx290_global_init_settings[] = {
> -	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
>  	{ IMX290_EXTCK_FREQ, 0x2520 },
>  	{ IMX290_WINWV_OB, 12 },
>  	{ IMX290_WINPH, 0 },

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK to read/write
  2023-02-02 16:04     ` Dave Stevenson
@ 2023-02-02 17:42       ` Dave Stevenson
  2023-02-03  7:16         ` Alexander Stein
  0 siblings, 1 reply; 49+ messages in thread
From: Dave Stevenson @ 2023-02-02 17:42 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media

On Thu, 2 Feb 2023 at 16:04, Dave Stevenson
<dave.stevenson@raspberrypi.com> wrote:
>
> Hi Alexander
>
> On Thu, 2 Feb 2023 at 15:40, Alexander Stein
> <alexander.stein@ew.tq-group.com> wrote:
> >
> > Hi Dave,
> >
> > thanks for working on this.
> >
> > Am Dienstag, 31. Januar 2023, 20:20:13 CET schrieb Dave Stevenson:
> > > The driver exposed V4L2_CID_HBLANK as a read only control to allow
> > > for exposure calculations and determination of the frame rate.
> > >
> > > Convert to a read/write control so that the frame rate can be
> > > controlled.
> > > V4L2_CID_VBLANK also sets the limits for the exposure control,
> > > therefore exposure ranges have to be updated when vblank changes.
> > >
> > > This also updates the default VMAX in 720p mode from 1125 to
> > > 750 to achieve 60fps and follow the datasheet.
> > >
> > > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > > ---
> > >  drivers/media/i2c/imx290.c | 57 ++++++++++++++++++++++++++++++--------
> > >  1 file changed, 45 insertions(+), 12 deletions(-)
> > >
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index 9006be6e5e7c..3413d83369ba 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -46,6 +46,7 @@
> > >  #define IMX290_BLKLEVEL
> > IMX290_REG_16BIT(0x300a)
> > >  #define IMX290_GAIN
> > IMX290_REG_8BIT(0x3014)
> > >  #define IMX290_VMAX
> > IMX290_REG_24BIT(0x3018)
> > > +#define IMX290_VMAX_MAX                                      0x3ffff
> > >  #define IMX290_HMAX
> > IMX290_REG_16BIT(0x301c)
> > >  #define IMX290_HMAX_MAX                                      0xffff
> > >  #define IMX290_SHS1
> > IMX290_REG_24BIT(0x3020)
> > > @@ -106,8 +107,6 @@
> > >  #define IMX290_PGCTRL_THRU                           BIT(1)
> > >  #define IMX290_PGCTRL_MODE(n)                                ((n) <<
> > 4)
> > >
> > > -#define IMX290_VMAX_DEFAULT                          1125
> > > -
> > >  #define IMX290_PIXEL_RATE                            148500000
> > >  /*
> > >   * The IMX290 pixel array is organized as follows:
> > > @@ -169,6 +168,7 @@ struct imx290_mode {
> > >       u32 width;
> > >       u32 height;
> > >       u32 hmax_min;
> > > +     u32 vmax_min;
> > >       u8 link_freq_index;
> > >
> > >       const struct imx290_regval *data;
> > > @@ -206,6 +206,7 @@ struct imx290 {
> > >       struct v4l2_ctrl *link_freq;
> > >       struct v4l2_ctrl *hblank;
> > >       struct v4l2_ctrl *vblank;
> > > +     struct v4l2_ctrl *exposure;
> > >  };
> > >
> > >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > > @@ -219,7 +220,6 @@ static inline struct imx290 *to_imx290(struct
> > > v4l2_subdev *_sd)
> > >
> > >  static const struct imx290_regval imx290_global_init_settings[] = {
> > >       { IMX290_CTRL_07, IMX290_WINMODE_1080P },
> > > -     { IMX290_VMAX, IMX290_VMAX_DEFAULT },
> > >       { IMX290_EXTCK_FREQ, 0x2520 },
> > >       { IMX290_WINWV_OB, 12 },
> > >       { IMX290_WINPH, 0 },
> > > @@ -412,6 +412,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> > > { .width = 1920,
> > >               .height = 1080,
> > >               .hmax_min = 2200,
> > > +             .vmax_min = 1125,
> > >               .link_freq_index = FREQ_INDEX_1080P,
> > >               .data = imx290_1080p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > @@ -420,6 +421,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> > > { .width = 1280,
> > >               .height = 720,
> > >               .hmax_min = 3300,
> > > +             .vmax_min = 750,
> > >               .link_freq_index = FREQ_INDEX_720P,
> > >               .data = imx290_720p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > @@ -431,6 +433,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> > > { .width = 1920,
> > >               .height = 1080,
> > >               .hmax_min = 2200,
> > > +             .vmax_min = 1125,
> > >               .link_freq_index = FREQ_INDEX_1080P,
> > >               .data = imx290_1080p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > @@ -439,6 +442,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> > > { .width = 1280,
> > >               .height = 720,
> > >               .hmax_min = 3300,
> > > +             .vmax_min = 750,
> > >               .link_freq_index = FREQ_INDEX_720P,
> > >               .data = imx290_720p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > @@ -645,7 +649,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > >                                            struct imx290, ctrls);
> > >       const struct v4l2_mbus_framefmt *format;
> > >       struct v4l2_subdev_state *state;
> > > -     int ret = 0;
> > > +     int ret = 0, vmax;
> > >
> > >       /*
> > >        * Return immediately for controls that don't need to be applied to
> > the
> > > @@ -654,6 +658,18 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > >       if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
> > >               return 0;
> > >
> > > +     if (ctrl->id == V4L2_CID_VBLANK) {
> > > +             u32 vmax = ctrl->val + imx290->current_mode->height;
> > > +
> > > +             /*
> > > +              * Changing vblank changes the allowed range for exposure.
> > > +              * We don't supply the current exposure as default here as
> > it
> > > +              * may lie outside the new range. We will reset it just
> > below.
> > > +              */
> > > +             __v4l2_ctrl_modify_range(imx290->exposure,
> > > +                                      1, vmax - 2, 1, vmax - 2);
> > > +     }
> > > +
> > >       /* V4L2 controls values will be applied only when power is already
> > up */
> > >       if (!pm_runtime_get_if_in_use(imx290->dev))
> > >               return 0;
> > > @@ -666,9 +682,23 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > >               ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
> > >               break;
> > >
> > > +     case V4L2_CID_VBLANK:
> > > +             ret = imx290_write(imx290, IMX290_VMAX,
> > > +                                ctrl->val + imx290->current_mode-
> > >height,
> > > +                                NULL);
> > > +             /*
> > > +              * Due to the way that exposure is programmed in this
> > sensor in
> > > +              * relation to VMAX, we have to reprogramme it whenever
> > VMAX is
> > > +              * changed.
> > > +              * Update ctrl so that the V4L2_CID_EXPOSURE case can
> > refer to
> > > +              * it.
> > > +              */
> > > +             ctrl = imx290->exposure;
> > > +             fallthrough;
> > >       case V4L2_CID_EXPOSURE:
> > > +             vmax = imx290->vblank->val + imx290->current_mode->height;
> > >               ret = imx290_write(imx290, IMX290_SHS1,
> > > -                                IMX290_VMAX_DEFAULT - ctrl->val -
> > 1, NULL);
> > > +                                vmax - ctrl->val - 1, NULL);
> > >               break;
> > >
> > >       case V4L2_CID_TEST_PATTERN:
> > > @@ -725,13 +755,15 @@ static void imx290_ctrl_update(struct imx290 *imx290,
> > >  {
> > >       unsigned int hblank_min = mode->hmax_min - mode->width;
> > >       unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
> > > -     unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> > > +     unsigned int vblank_min = mode->vmax_min - mode->height;
> > > +     unsigned int vblank_max = IMX290_VMAX_MAX - mode->height;
> > >
> > >       __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> > >
> > >       __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
> > >                                hblank_min);
> > > -     __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> > > +     __v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max, 1,
> > > +                              vblank_min);
> >
> > You are missing setting the range for V4L2_CID_EXPOSURE. So it stays at 1
> > resulting in a black image.
>
> You're right.
> I'm always testing with libcamera which will set V4L2_CID_VBLANK to
> configure the frame rate, and that will update the exposure range.

Actually I'm going to backtrack somewhat, although I may have
misunderstood your comment.

imx290_subdev_init calls imx290_ctrl_update. VBLANK was created with
min/max/def all set to 1. The update sets a range of 45 to 261063, so
the old value is now invalid and the new default is adopted. That
means set_ctrl is called for VBLANK, and that triggers the exposure
range to be updated. There is no need to update the exposure range in
imx290_ctrl_update on that basis.
It is missed when changing mode as the current VBLANK value is still
within range, therefore it doesn't need to change, and doesn't update
the exposure range. I'll fix that.


Are you relying on the initial exposure to be some particular value?
Is the initial value for any setting actually guaranteed in the spec?
If your application is expecting max exposure by default, then that
sounds very fragile and not portable to other sensors, but it can be
fixed up.

Do note that switching to 1280x720 mode will limit the exposure range
to 1-763. Switching back to 1920x1080 mode will increase the range
again, but as the current value is still in range the exposure setting
will not be changed. What controls get reset when changing mode is
undefined, but AIUI it should update the ranges and leave everything
else alone, which is what is done here. Some drivers pepper
v4l2_ctrl_s_ctrl calls around the place, but what value to adopt seems
random.

  Dave

>   Dave
>
> > Best regards,
> > Alexander
> >
> > >  }
> > >
> > >  static int imx290_ctrl_init(struct imx290 *imx290)
> > > @@ -761,9 +793,12 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > >       v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > >                         V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
> > >
> > > -     v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > > -                       V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2,
> > 1,
> > > -                       IMX290_VMAX_DEFAULT - 2);
> > > +     /*
> > > +      * Correct range will be determined through imx290_ctrl_update
> > setting
> > > +      * V4L2_CID_VBLANK.
> > > +      */
> > > +     imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls,
> > &imx290_ctrl_ops,
> > > +                                          V4L2_CID_EXPOSURE, 1,
> > 1, 1, 1);
> > >
> > >       /*
> > >        * Set the link frequency, pixel rate, horizontal blanking and
> > vertical
> > > @@ -795,8 +830,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > >
> > >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > >                                          V4L2_CID_VBLANK, 1, 1, 1,
> > 1);
> > > -     if (imx290->vblank)
> > > -             imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > >
> > >       v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
> > >                                       &props);
> >
> >
> >
> >

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

* Re: [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock
  2023-01-31 19:20 ` [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock Dave Stevenson
@ 2023-02-02 22:03   ` Laurent Pinchart
  2023-02-03 17:04     ` Dave Stevenson
  2023-02-03  7:44   ` Alexander Stein
  1 sibling, 1 reply; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02 22:03 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:15PM +0000, Dave Stevenson wrote:
> The sensor supports either a 37.125 or 74.25MHz external, but
> the driver only supported 37.125MHz.
> 
> Add the relevant register configuration for either clock
> frequency option.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 120 +++++++++++++++++++++++++++++++------
>  1 file changed, 103 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 5202ef3cc3e6..7f6746f74040 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -102,6 +102,7 @@
>  #define IMX290_TCLKPREPARE				IMX290_REG_16BIT(0x3452)
>  #define IMX290_TLPX					IMX290_REG_16BIT(0x3454)
>  #define IMX290_X_OUT_SIZE				IMX290_REG_16BIT(0x3472)
> +#define IMX290_INCKSEL7					IMX290_REG_8BIT(0x3480)
>  
>  #define IMX290_PGCTRL_REGEN				BIT(0)
>  #define IMX290_PGCTRL_THRU				BIT(1)
> @@ -159,11 +160,27 @@
>  
>  #define IMX290_NUM_SUPPLIES				3
>  
> +#define CLK_37_125	0
> +#define CLK_74_25	1
> +#define NUM_CLK		2

Please add an IMX290 prefer to avoid future namespace clashes.

> +
>  struct imx290_regval {
>  	u32 reg;
>  	u32 val;
>  };
>  
> +/*
> + * Clock configuration for registers INCKSEL1 to INCKSEL6.
> + */
> +struct imx290_clk_cfg {
> +	u8 incksel1;
> +	u8 incksel2;
> +	u8 incksel3;
> +	u8 incksel4;
> +	u8 incksel5;
> +	u8 incksel6;
> +};
> +
>  struct imx290_mode {
>  	u32 width;
>  	u32 height;
> @@ -173,6 +190,8 @@ struct imx290_mode {
>  
>  	const struct imx290_regval *data;
>  	u32 data_size;
> +
> +	const struct imx290_clk_cfg *clk_cfg;
>  };
>  
>  struct imx290_csi_cfg {
> @@ -191,6 +210,7 @@ struct imx290 {
>  	struct device *dev;
>  	struct clk *xclk;
>  	struct regmap *regmap;
> +	u32 xclk_freq;
>  	u8 nlanes;
>  	u8 mono;
>  
> @@ -219,7 +239,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
>   */
>  
>  static const struct imx290_regval imx290_global_init_settings[] = {
> -	{ IMX290_EXTCK_FREQ, 0x2520 },
>  	{ IMX290_WINWV_OB, 12 },
>  	{ IMX290_WINPH, 0 },
>  	{ IMX290_WINPV, 0 },
> @@ -269,7 +288,16 @@ static const struct imx290_regval imx290_global_init_settings[] = {
>  	{ IMX290_REG_8BIT(0x33b0), 0x50 },
>  	{ IMX290_REG_8BIT(0x33b2), 0x1a },
>  	{ IMX290_REG_8BIT(0x33b3), 0x04 },
> -	{ IMX290_REG_8BIT(0x3480), 0x49 },

One less unnamed register, only 42 to go :-D

> +};
> +
> +static const struct imx290_regval imx290_37_125mhz_clock[] = {
> +	{ IMX290_EXTCK_FREQ, 0x2520 },
> +	{ IMX290_INCKSEL7, 0x49 },
> +};
> +
> +static const struct imx290_regval imx290_74_25mhz_clock[] = {
> +	{ IMX290_EXTCK_FREQ, 0x4a40 },
> +	{ IMX290_INCKSEL7, 0x92 },
>  };

Those two arrays are not used, which I assume is not normal :-) A rebase
problem maybe ?

How about moving the INCKSEL7 value to the imx290_clk_cfg structure for
consistency ?

>  
>  static const struct imx290_regval imx290_1080p_settings[] = {
> @@ -279,12 +307,6 @@ static const struct imx290_regval imx290_1080p_settings[] = {
>  	{ IMX290_OPB_SIZE_V, 10 },
>  	{ IMX290_X_OUT_SIZE, 1920 },
>  	{ IMX290_Y_OUT_SIZE, 1080 },
> -	{ IMX290_INCKSEL1, 0x18 },
> -	{ IMX290_INCKSEL2, 0x03 },
> -	{ IMX290_INCKSEL3, 0x20 },
> -	{ IMX290_INCKSEL4, 0x01 },
> -	{ IMX290_INCKSEL5, 0x1a },
> -	{ IMX290_INCKSEL6, 0x1a },
>  };
>  
>  static const struct imx290_regval imx290_720p_settings[] = {
> @@ -294,12 +316,6 @@ static const struct imx290_regval imx290_720p_settings[] = {
>  	{ IMX290_OPB_SIZE_V, 4 },
>  	{ IMX290_X_OUT_SIZE, 1280 },
>  	{ IMX290_Y_OUT_SIZE, 720 },
> -	{ IMX290_INCKSEL1, 0x20 },
> -	{ IMX290_INCKSEL2, 0x00 },
> -	{ IMX290_INCKSEL3, 0x20 },
> -	{ IMX290_INCKSEL4, 0x01 },
> -	{ IMX290_INCKSEL5, 0x1a },
> -	{ IMX290_INCKSEL6, 0x1a },
>  };
>  
>  static const struct imx290_regval imx290_10bit_settings[] = {
> @@ -405,6 +421,48 @@ static inline int imx290_link_freqs_num(const struct imx290 *imx290)
>  		return ARRAY_SIZE(imx290_link_freq_4lanes);
>  }
>  
> +static const struct imx290_clk_cfg imx290_1080p_clock_config[NUM_CLK] = {
> +	[CLK_37_125] = {
> +		/* 37.125MHz clock config */
> +		.incksel1 = 0x18,
> +		.incksel2 = 0x03,
> +		.incksel3 = 0x20,
> +		.incksel4 = 0x01,
> +		.incksel5 = 0x1a,
> +		.incksel6 = 0x1a,
> +	},

As the incksel[0-6] fields are only used in one place, to write all 6 of
them to the device, you could also drop the imx290_clk_cfg structure and
turn this into a imx290_regval array. Entirely up to you.

> +	[CLK_74_25] = {
> +		/* 74.25MHz clock config */
> +		.incksel1 = 0x0c,
> +		.incksel2 = 0x03,
> +		.incksel3 = 0x10,
> +		.incksel4 = 0x01,
> +		.incksel5 = 0x1b,
> +		.incksel6 = 0x1b,
> +	},
> +};
> +
> +static const struct imx290_clk_cfg imx290_720p_clock_config[NUM_CLK] = {
> +	[CLK_37_125] = {
> +		/* 37.125MHz clock config */
> +		.incksel1 = 0x20,
> +		.incksel2 = 0x00,
> +		.incksel3 = 0x20,
> +		.incksel4 = 0x01,
> +		.incksel5 = 0x1a,
> +		.incksel6 = 0x1a,
> +	},
> +	[CLK_74_25] = {
> +		/* 74.25MHz clock config */
> +		.incksel1 = 0x10,
> +		.incksel2 = 0x00,
> +		.incksel3 = 0x10,
> +		.incksel4 = 0x01,
> +		.incksel5 = 0x1b,
> +		.incksel6 = 0x1b,
> +	},
> +};
> +
>  /* Mode configs */
>  static const struct imx290_mode imx290_modes_2lanes[] = {
>  	{
> @@ -415,6 +473,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> +		.clk_cfg = imx290_1080p_clock_config,
>  	},
>  	{
>  		.width = 1280,
> @@ -424,6 +483,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> +		.clk_cfg = imx290_720p_clock_config,
>  	},
>  };
>  
> @@ -436,6 +496,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> +		.clk_cfg = imx290_1080p_clock_config,
>  	},
>  	{
>  		.width = 1280,
> @@ -445,6 +506,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> +		.clk_cfg = imx290_720p_clock_config,
>  	},
>  };
>  
> @@ -563,6 +625,23 @@ static int imx290_set_register_array(struct imx290 *imx290,
>  	return 0;
>  }
>  
> +static int imx290_set_clock(struct imx290 *imx290)
> +{
> +	int clk_idx = (imx290->xclk_freq == 37125000) ? 0 : 1;
> +	const struct imx290_mode *mode = imx290->current_mode;
> +	const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[clk_idx];
> +	int ret = 0;

How about turning the clock freq macros into an enum:

enum imx290_clk_freq {
	IMX290_CLK_37_125 = 0,
	IMX290_CLK_74_25 = 1,
};

and replacing in struct imx290

-	u32 xclk_freq;
+	enum imx290_clk_freq xclk_freq;

? Then you could could simply write

	const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[imx290->xclk_freq];

(you could also name the field xclk_freq_idx if desired). Up to you, if
you find that messier you can ignore the comment.

> +
> +	imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret);
> +	imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret);
> +	imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret);
> +	imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret);
> +	imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret);
> +	imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret);
> +
> +	return ret;
> +}
> +
>  static int imx290_set_data_lanes(struct imx290 *imx290)
>  {
>  	int ret = 0;
> @@ -863,6 +942,13 @@ static int imx290_start_streaming(struct imx290 *imx290,
>  		return ret;
>  	}
>  
> +	/* Set clock parameters based on mode and xclk */
> +	ret = imx290_set_clock(imx290);
> +	if (ret < 0) {
> +		dev_err(imx290->dev, "Could not set clocks\n");
> +		return ret;
> +	}
> +
>  	/* Set data lane count */
>  	ret = imx290_set_data_lanes(imx290);
>  	if (ret < 0) {
> @@ -1259,14 +1345,14 @@ static int imx290_init_clk(struct imx290 *imx290)
>  	int ret;
>  
>  	ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
> -				       "clock-frequency", &xclk_freq);
> +				       "clock-frequency", &imx290->xclk_freq);
>  	if (ret) {
>  		dev_err(imx290->dev, "Could not get xclk frequency\n");
>  		return ret;
>  	}
>  
> -	/* external clock must be 37.125 MHz */
> -	if (xclk_freq != 37125000) {
> +	/* external clock must be 37.125 MHz or 74.25MHz */
> +	if (imx290->xclk_freq != 37125000 && imx290->xclk_freq != 74250000) {
>  		dev_err(imx290->dev, "External clock frequency %u is not supported\n",
>  			xclk_freq);
>  		return -EINVAL;

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips
  2023-01-31 19:20 ` [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips Dave Stevenson
@ 2023-02-02 22:10   ` Laurent Pinchart
  2023-02-03 10:21     ` Dave Stevenson
  2023-02-03  7:35   ` Alexander Stein
  1 sibling, 1 reply; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02 22:10 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

Thank you for the patch.

On Tue, Jan 31, 2023 at 07:20:16PM +0000, Dave Stevenson wrote:
> The sensor supports H & V flips, so add the relevant hooks for
> V4L2_CID_HFLIP and V4L2_CID_VFLIP to configure them.
> 
> Note that the Bayer order is maintained as the readout area
> shifts by 1 pixel in the appropriate direction (note the
> comment about the top margin being 8 pixels whilst the bottom
> margin is 9).

That's why ! Now it makes sense to me.

> The V4L2_SEL_TGT_CROP region is therefore
> adjusted appropriately.

I'm not sure I like when sensors try to be clever... Out of curiosity,
do you know if this automatic shift of the crop rectangle can be
disabled ?

> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 37 ++++++++++++++++++++++++++++++++++---
>  1 file changed, 34 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 7f6746f74040..d2b7534f2c51 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -227,6 +227,8 @@ struct imx290 {
>  	struct v4l2_ctrl *hblank;
>  	struct v4l2_ctrl *vblank;
>  	struct v4l2_ctrl *exposure;
> +	struct v4l2_ctrl *hflip;
> +	struct v4l2_ctrl *vflip;
>  };
>  
>  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> @@ -801,6 +803,24 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  				   NULL);
>  		break;
>  
> +	case V4L2_CID_HFLIP:
> +	case V4L2_CID_VFLIP:
> +	{
> +		u32 reg;
> +
> +		/* WINMODE is in bits [6:4], so need to read-modify-write */

You could also cache the value of the register in struct imx290 to avoid
the read.

> +		ret = imx290_read(imx290, IMX290_CTRL_07, &reg);
> +		if (ret)
> +			break;
> +		reg &= ~(IMX290_HREVERSE | IMX290_VREVERSE);
> +		if (imx290->hflip->val)
> +			reg |= IMX290_HREVERSE;
> +		if (imx290->vflip->val)
> +			reg |= IMX290_VREVERSE;
> +		ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL);
> +		break;

As you always write those two controls together, they should be put in a
cluster to have a single call to imx290_set_ctrl() when both are set in
the same VIDIOC_S_EXT_CTRLS call.

> +	}
> +
>  	default:
>  		ret = -EINVAL;
>  		break;
> @@ -853,7 +873,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  	if (ret < 0)
>  		return ret;
>  
> -	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> +	v4l2_ctrl_handler_init(&imx290->ctrls, 11);
>  
>  	/*
>  	 * The sensor has an analog gain and a digital gain, both controlled
> @@ -909,6 +929,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>  					   V4L2_CID_VBLANK, 1, 1, 1, 1);
>  
> +	imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> +					  V4L2_CID_HFLIP, 0, 1, 1, 0);
> +	imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> +					  V4L2_CID_VFLIP, 0, 1, 1, 0);
> +
>  	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
>  					&props);
>  
> @@ -1030,6 +1055,9 @@ static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
>  		pm_runtime_put_autosuspend(imx290->dev);
>  	}
>  
> +	/* vflip and hflip cannot change during streaming */
> +	__v4l2_ctrl_grab(imx290->vflip, enable);
> +	__v4l2_ctrl_grab(imx290->hflip, enable);

A blank line would be nice.

>  unlock:
>  	v4l2_subdev_unlock_state(state);
>  	return ret;
> @@ -1115,6 +1143,7 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
>  				struct v4l2_subdev_state *sd_state,
>  				struct v4l2_subdev_selection *sel)
>  {
> +	struct imx290 *imx290 = to_imx290(sd);
>  	struct v4l2_mbus_framefmt *format;
>  
>  	switch (sel->target) {
> @@ -1122,9 +1151,11 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
>  		format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
>  

A comment to explain here why the crop rectangle is adjusted would be
nice.

>  		sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
> -			   + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2;
> +			   + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2
> +			   + imx290->vflip->val;
>  		sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT
> -			    + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2;
> +			    + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2
> +			    + imx290->hflip->val;
>  		sel->r.width = format->width;
>  		sel->r.height = format->height;
>  

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write
  2023-02-02 15:48     ` Dave Stevenson
@ 2023-02-02 22:14       ` Laurent Pinchart
  0 siblings, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02 22:14 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

On Thu, Feb 02, 2023 at 03:48:19PM +0000, Dave Stevenson wrote:
> On Thu, 2 Feb 2023 at 14:58, Laurent Pinchart wrote:
> > On Tue, Jan 31, 2023 at 07:20:12PM +0000, Dave Stevenson wrote:
> > > The driver exposed V4L2_CID_HBLANK as a read only control to allow
> > > for exposure calculations and determination of the frame rate.
> > >
> > > Convert to a read/write control so that the frame rate can be
> > > controlled.
> > >
> > > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > > ---
> > >  drivers/media/i2c/imx290.c | 33 +++++++++++++++++++--------------
> > >  1 file changed, 19 insertions(+), 14 deletions(-)
> > >
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index 9ddd6382b127..9006be6e5e7c 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -47,6 +47,7 @@
> > >  #define IMX290_GAIN                                  IMX290_REG_8BIT(0x3014)
> > >  #define IMX290_VMAX                                  IMX290_REG_24BIT(0x3018)
> > >  #define IMX290_HMAX                                  IMX290_REG_16BIT(0x301c)
> > > +#define IMX290_HMAX_MAX                                      0xffff
> > >  #define IMX290_SHS1                                  IMX290_REG_24BIT(0x3020)
> > >  #define IMX290_WINWV_OB                                      IMX290_REG_8BIT(0x303a)
> > >  #define IMX290_WINPV                                 IMX290_REG_16BIT(0x303c)
> > > @@ -167,7 +168,7 @@ struct imx290_regval {
> > >  struct imx290_mode {
> > >       u32 width;
> > >       u32 height;
> > > -     u32 hmax;
> > > +     u32 hmax_min;
> > >       u8 link_freq_index;
> > >
> > >       const struct imx290_regval *data;
> > > @@ -410,7 +411,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
> > >       {
> > >               .width = 1920,
> > >               .height = 1080,
> > > -             .hmax = 2200,
> > > +             .hmax_min = 2200,
> >
> > I didn't find in the documentation a mention that these values are the
> > minimum the sensor supports. Did you use them as such based on the fact
> > that anything higher than the nominal hblank value reported by the
> > datasheet is hopefully guaranteed to work, and lower values are
> > uncharted territory, or are these the real minimums ? I'm fine using
> > them as minimum for now even if we could possibly go lower, leaving that
> > for future patches. I would however mention this in a comment or in the
> > commit message.
> 
> The datasheet shows varying HMAX to control frame rate.
> Table "List of Setting Register for CSI-2 serial output" for all-pixel
> mode gives HMAX values of :
> 0x14a0 for 25fps
> 0x1130 for 30fps
> 0x0a50 for 50fps
> 0x0898 for 60 fps
> 0x0528 for 100fps (needs FRSEL = 0 and higher link frequency)
> 0x044c for 120fps (ditto)
> 
> VMAX is fixed at 0x465 (1125 decimal) for all frame rates in all-pixel
> (1080p) mode.
> 
> The 2200 listed here is the 0x898 for 60fps - the nominal max with the
> configured lane / link frequency combinations.
> 
> Doing the maths, 4 lanes at 445.5Mbit/s/lane = 1782Mbit/s.
> 2200 * 1125 * 12bpp * 60fps = 1782Mbit/s. Pixel clock is therefore the
> same as link bandwidth at this point. I have no information about
> blanking and clock lane behaviour for this sensor, but can believe
> that it needs that time for low level CSI2 transitions.
> 
> AIUI you'll only be able to decrease this further if you add support
> for 891Mbit/s/lane (445.5MHz link freq) on 4 lanes, and drop to 10 bit
> readout. Programming HMAX also then becomes more entertaining as it
> appears to be half the expected value (0x44c = 1100 decimal, which is
> smaller than the width), so it'll need a fair amount of messing to get
> all the controls to behave as expected.
> That's all outside the scope of this patch set - 60fps was the only
> frame rate previously supported, and we've expanded on that with
> slower frame rate support.

I'm totally fine with that. Thanks for the explanation.

> FWIW I have checked with Sony over the 120fps modes, and experimented a little.
> Sony will only support 120fps in 10bit. The flyer for IMX462 that
> implies it does 1920x1080@120fps in 12 bit mode is incorrect.
> Experimentation had 720p120 working in 12bit, but trying to work
> through limiting the various bit depth and resolution options is going
> to get really ugly in the driver.

Let's enjoy the beauty of your patch set today and leave the ugliness
for tomorrow :-)

> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >
> > >               .link_freq_index = FREQ_INDEX_1080P,
> > >               .data = imx290_1080p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > @@ -418,7 +419,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
> > >       {
> > >               .width = 1280,
> > >               .height = 720,
> > > -             .hmax = 3300,
> > > +             .hmax_min = 3300,
> > >               .link_freq_index = FREQ_INDEX_720P,
> > >               .data = imx290_720p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > @@ -429,7 +430,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
> > >       {
> > >               .width = 1920,
> > >               .height = 1080,
> > > -             .hmax = 2200,
> > > +             .hmax_min = 2200,
> > >               .link_freq_index = FREQ_INDEX_1080P,
> > >               .data = imx290_1080p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > @@ -437,7 +438,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
> > >       {
> > >               .width = 1280,
> > >               .height = 720,
> > > -             .hmax = 3300,
> > > +             .hmax_min = 3300,
> > >               .link_freq_index = FREQ_INDEX_720P,
> > >               .data = imx290_720p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > @@ -686,6 +687,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > >               }
> > >               break;
> > >
> > > +     case V4L2_CID_HBLANK:
> > > +             ret = imx290_write(imx290, IMX290_HMAX,
> > > +                                ctrl->val + imx290->current_mode->width,
> > > +                                NULL);
> > > +             break;
> > > +
> > >       default:
> > >               ret = -EINVAL;
> > >               break;
> > > @@ -716,12 +723,14 @@ static void imx290_ctrl_update(struct imx290 *imx290,
> > >                              const struct v4l2_mbus_framefmt *format,
> > >                              const struct imx290_mode *mode)
> > >  {
> > > -     unsigned int hblank = mode->hmax - mode->width;
> > > +     unsigned int hblank_min = mode->hmax_min - mode->width;
> > > +     unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
> > >       unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> > >
> > >       __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> > >
> > > -     __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
> > > +     __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
> > > +                              hblank_min);
> > >       __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> > >  }
> > >
> > > @@ -778,10 +787,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > >                                    ARRAY_SIZE(imx290_test_pattern_menu) - 1,
> > >                                    0, 0, imx290_test_pattern_menu);
> > >
> > > +     /*
> > > +      * Actual range will be set from imx290_ctrl_update later in the probe.
> > > +      */
> > >       imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > >                                          V4L2_CID_HBLANK, 1, 1, 1, 1);
> > > -     if (imx290->hblank)
> > > -             imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > >
> > >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > >                                          V4L2_CID_VBLANK, 1, 1, 1, 1);
> > > @@ -850,11 +860,6 @@ static int imx290_start_streaming(struct imx290 *imx290,
> > >               return ret;
> > >       }
> > >
> > > -     ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
> > > -                        NULL);
> > > -     if (ret)
> > > -             return ret;
> > > -
> > >       /* Apply customized values from user */
> > >       ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> > >       if (ret) {

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls
  2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
                   ` (10 preceding siblings ...)
  2023-01-31 19:20 ` [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips Dave Stevenson
@ 2023-02-02 22:16 ` Laurent Pinchart
  11 siblings, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-02 22:16 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

On Tue, Jan 31, 2023 at 07:20:05PM +0000, Dave Stevenson wrote:
> Hi All
> 
> This is a small patch set that fixes a number of issues, adds in support
> for the alternate input clock frequency option, and expands the control support
> for flips and VBLANK/HBLANK.
> 
> My source tree has the 2 patches I've just sent for mono support first, but I
> believe the two series should apply in either order.

Thank you for taking the time to upstream these, much appreciated. I'll
give the series a try with my IMX327, but I'm afraid it will have to
wait until next week after I come back from the FOSDEM.

> Dave Stevenson (11):
>   media: i2c: imx290: Match kernel coding style on whitespace
>   media: i2c: imx290: Set the colorspace fields in the format
>   media: i2c: imx290: Add V4L2_SUBDEV_FL_HAS_EVENTS and subscribe hooks
>   media: i2c: imx290: Fix the pixel rate at 148.5Mpix/s
>   media: i2c: imx290: Support 60fps in 2 lane operation
>   media: i2c: imx290: Use CSI timings as per datasheet
>   media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write
>   media: i2c: imx290: Convert V4L2_CID_VBLANK to read/write
>   media: i2c: imx290: Remove duplicated write to IMX290_CTRL_07
>   media: i2c: imx290: Add support for 74.25MHz external clock
>   media: i2c: imx290: Add support for H & V Flips
> 
>  drivers/media/i2c/imx290.c | 429 ++++++++++++++++++++++++++++---------
>  1 file changed, 333 insertions(+), 96 deletions(-)

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK to read/write
  2023-02-02 17:42       ` Dave Stevenson
@ 2023-02-03  7:16         ` Alexander Stein
  0 siblings, 0 replies; 49+ messages in thread
From: Alexander Stein @ 2023-02-03  7:16 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media

Am Donnerstag, 2. Februar 2023, 18:42:43 CET schrieb Dave Stevenson:
> On Thu, 2 Feb 2023 at 16:04, Dave Stevenson
> 
> <dave.stevenson@raspberrypi.com> wrote:
> > Hi Alexander
> > 
> > On Thu, 2 Feb 2023 at 15:40, Alexander Stein
> > 
> > <alexander.stein@ew.tq-group.com> wrote:
> > > Hi Dave,
> > > 
> > > thanks for working on this.
> > > 
> > > Am Dienstag, 31. Januar 2023, 20:20:13 CET schrieb Dave Stevenson:
> > > > The driver exposed V4L2_CID_HBLANK as a read only control to allow
> > > > for exposure calculations and determination of the frame rate.
> > > > 
> > > > Convert to a read/write control so that the frame rate can be
> > > > controlled.
> > > > V4L2_CID_VBLANK also sets the limits for the exposure control,
> > > > therefore exposure ranges have to be updated when vblank changes.
> > > > 
> > > > This also updates the default VMAX in 720p mode from 1125 to
> > > > 750 to achieve 60fps and follow the datasheet.
> > > > 
> > > > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > > > ---
> > > > 
> > > >  drivers/media/i2c/imx290.c | 57
> > > >  ++++++++++++++++++++++++++++++--------
> > > >  1 file changed, 45 insertions(+), 12 deletions(-)
> > > > 
> > > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > > index 9006be6e5e7c..3413d83369ba 100644
> > > > --- a/drivers/media/i2c/imx290.c
> > > > +++ b/drivers/media/i2c/imx290.c
> > > > @@ -46,6 +46,7 @@
> > > > 
> > > >  #define IMX290_BLKLEVEL
> > > 
> > > IMX290_REG_16BIT(0x300a)
> > > 
> > > >  #define IMX290_GAIN
> > > 
> > > IMX290_REG_8BIT(0x3014)
> > > 
> > > >  #define IMX290_VMAX
> > > 
> > > IMX290_REG_24BIT(0x3018)
> > > 
> > > > +#define IMX290_VMAX_MAX                                      0x3ffff
> > > > 
> > > >  #define IMX290_HMAX
> > > 
> > > IMX290_REG_16BIT(0x301c)
> > > 
> > > >  #define IMX290_HMAX_MAX                                      0xffff
> > > >  #define IMX290_SHS1
> > > 
> > > IMX290_REG_24BIT(0x3020)
> > > 
> > > > @@ -106,8 +107,6 @@
> > > > 
> > > >  #define IMX290_PGCTRL_THRU                           BIT(1)
> > > >  #define IMX290_PGCTRL_MODE(n)                                ((n) <<
> > > 
> > > 4)
> > > 
> > > > -#define IMX290_VMAX_DEFAULT                          1125
> > > > -
> > > > 
> > > >  #define IMX290_PIXEL_RATE                            148500000
> > > >  /*
> > > >  
> > > >   * The IMX290 pixel array is organized as follows:
> > > > @@ -169,6 +168,7 @@ struct imx290_mode {
> > > > 
> > > >       u32 width;
> > > >       u32 height;
> > > >       u32 hmax_min;
> > > > 
> > > > +     u32 vmax_min;
> > > > 
> > > >       u8 link_freq_index;
> > > >       
> > > >       const struct imx290_regval *data;
> > > > 
> > > > @@ -206,6 +206,7 @@ struct imx290 {
> > > > 
> > > >       struct v4l2_ctrl *link_freq;
> > > >       struct v4l2_ctrl *hblank;
> > > >       struct v4l2_ctrl *vblank;
> > > > 
> > > > +     struct v4l2_ctrl *exposure;
> > > > 
> > > >  };
> > > >  
> > > >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > > > 
> > > > @@ -219,7 +220,6 @@ static inline struct imx290 *to_imx290(struct
> > > > v4l2_subdev *_sd)
> > > > 
> > > >  static const struct imx290_regval imx290_global_init_settings[] = {
> > > >  
> > > >       { IMX290_CTRL_07, IMX290_WINMODE_1080P },
> > > > 
> > > > -     { IMX290_VMAX, IMX290_VMAX_DEFAULT },
> > > > 
> > > >       { IMX290_EXTCK_FREQ, 0x2520 },
> > > >       { IMX290_WINWV_OB, 12 },
> > > >       { IMX290_WINPH, 0 },
> > > > 
> > > > @@ -412,6 +412,7 @@ static const struct imx290_mode
> > > > imx290_modes_2lanes[] =
> > > > { .width = 1920,
> > > > 
> > > >               .height = 1080,
> > > >               .hmax_min = 2200,
> > > > 
> > > > +             .vmax_min = 1125,
> > > > 
> > > >               .link_freq_index = FREQ_INDEX_1080P,
> > > >               .data = imx290_1080p_settings,
> > > >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > > 
> > > > @@ -420,6 +421,7 @@ static const struct imx290_mode
> > > > imx290_modes_2lanes[] =
> > > > { .width = 1280,
> > > > 
> > > >               .height = 720,
> > > >               .hmax_min = 3300,
> > > > 
> > > > +             .vmax_min = 750,
> > > > 
> > > >               .link_freq_index = FREQ_INDEX_720P,
> > > >               .data = imx290_720p_settings,
> > > >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > > 
> > > > @@ -431,6 +433,7 @@ static const struct imx290_mode
> > > > imx290_modes_4lanes[] =
> > > > { .width = 1920,
> > > > 
> > > >               .height = 1080,
> > > >               .hmax_min = 2200,
> > > > 
> > > > +             .vmax_min = 1125,
> > > > 
> > > >               .link_freq_index = FREQ_INDEX_1080P,
> > > >               .data = imx290_1080p_settings,
> > > >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > > 
> > > > @@ -439,6 +442,7 @@ static const struct imx290_mode
> > > > imx290_modes_4lanes[] =
> > > > { .width = 1280,
> > > > 
> > > >               .height = 720,
> > > >               .hmax_min = 3300,
> > > > 
> > > > +             .vmax_min = 750,
> > > > 
> > > >               .link_freq_index = FREQ_INDEX_720P,
> > > >               .data = imx290_720p_settings,
> > > >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > > 
> > > > @@ -645,7 +649,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > > 
> > > >                                            struct imx290, ctrls);
> > > >       
> > > >       const struct v4l2_mbus_framefmt *format;
> > > >       struct v4l2_subdev_state *state;
> > > > 
> > > > -     int ret = 0;
> > > > +     int ret = 0, vmax;
> > > > 
> > > >       /*
> > > >       
> > > >        * Return immediately for controls that don't need to be applied
> > > >        to
> > > 
> > > the
> > > 
> > > > @@ -654,6 +658,18 @@ static int imx290_set_ctrl(struct v4l2_ctrl
> > > > *ctrl)
> > > > 
> > > >       if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
> > > >       
> > > >               return 0;
> > > > 
> > > > +     if (ctrl->id == V4L2_CID_VBLANK) {
> > > > +             u32 vmax = ctrl->val + imx290->current_mode->height;
> > > > +
> > > > +             /*
> > > > +              * Changing vblank changes the allowed range for
> > > > exposure.
> > > > +              * We don't supply the current exposure as default here
> > > > as
> > > 
> > > it
> > > 
> > > > +              * may lie outside the new range. We will reset it just
> > > 
> > > below.
> > > 
> > > > +              */
> > > > +             __v4l2_ctrl_modify_range(imx290->exposure,
> > > > +                                      1, vmax - 2, 1, vmax - 2);
> > > > +     }
> > > > +
> > > > 
> > > >       /* V4L2 controls values will be applied only when power is
> > > >       already
> > > 
> > > up */
> > > 
> > > >       if (!pm_runtime_get_if_in_use(imx290->dev))
> > > >       
> > > >               return 0;
> > > > 
> > > > @@ -666,9 +682,23 @@ static int imx290_set_ctrl(struct v4l2_ctrl
> > > > *ctrl)
> > > > 
> > > >               ret = imx290_write(imx290, IMX290_GAIN, ctrl->val,
> > > >               NULL);
> > > >               break;
> > > > 
> > > > +     case V4L2_CID_VBLANK:
> > > > +             ret = imx290_write(imx290, IMX290_VMAX,
> > > > +                                ctrl->val + imx290->current_mode-
> > > >
> > > >height,
> > > >
> > > > +                                NULL);
> > > > +             /*
> > > > +              * Due to the way that exposure is programmed in this
> > > 
> > > sensor in
> > > 
> > > > +              * relation to VMAX, we have to reprogramme it whenever
> > > 
> > > VMAX is
> > > 
> > > > +              * changed.
> > > > +              * Update ctrl so that the V4L2_CID_EXPOSURE case can
> > > 
> > > refer to
> > > 
> > > > +              * it.
> > > > +              */
> > > > +             ctrl = imx290->exposure;
> > > > +             fallthrough;
> > > > 
> > > >       case V4L2_CID_EXPOSURE:
> > > > +             vmax = imx290->vblank->val +
> > > > imx290->current_mode->height;
> > > > 
> > > >               ret = imx290_write(imx290, IMX290_SHS1,
> > > > 
> > > > -                                IMX290_VMAX_DEFAULT - ctrl->val -
> > > 
> > > 1, NULL);
> > > 
> > > > +                                vmax - ctrl->val - 1, NULL);
> > > > 
> > > >               break;
> > > >       
> > > >       case V4L2_CID_TEST_PATTERN:
> > > > @@ -725,13 +755,15 @@ static void imx290_ctrl_update(struct imx290
> > > > *imx290,
> > > > 
> > > >  {
> > > >  
> > > >       unsigned int hblank_min = mode->hmax_min - mode->width;
> > > >       unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
> > > > 
> > > > -     unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> > > > +     unsigned int vblank_min = mode->vmax_min - mode->height;
> > > > +     unsigned int vblank_max = IMX290_VMAX_MAX - mode->height;
> > > > 
> > > >       __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> > > >       
> > > >       __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max,
> > > >       1,
> > > >       
> > > >                                hblank_min);
> > > > 
> > > > -     __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1,
> > > > vblank);
> > > > +     __v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max,
> > > > 1,
> > > > +                              vblank_min);
> > > 
> > > You are missing setting the range for V4L2_CID_EXPOSURE. So it stays at
> > > 1
> > > resulting in a black image.
> > 
> > You're right.
> > I'm always testing with libcamera which will set V4L2_CID_VBLANK to
> > configure the frame rate, and that will update the exposure range.
> 
> Actually I'm going to backtrack somewhat, although I may have
> misunderstood your comment.
> 
> imx290_subdev_init calls imx290_ctrl_update. VBLANK was created with
> min/max/def all set to 1. The update sets a range of 45 to 261063, so
> the old value is now invalid and the new default is adopted. That
> means set_ctrl is called for VBLANK, and that triggers the exposure
> range to be updated. There is no need to update the exposure range in
> imx290_ctrl_update on that basis.

This is how it should be and in imx290_set_ctrl is the bug. There is a 'switch 
(ctrl->id)' for fast return on ctrl which don't need writes to the device. But 
this changed on your series. Removing the fast return for both VBLANK and 
HBLANK I get proper settings for exposure.

> It is missed when changing mode as the current VBLANK value is still
> within range, therefore it doesn't need to change, and doesn't update
> the exposure range. I'll fix that.

But the default VBLANK of 1 is not in range of the minimum of 45, so there is 
definitely an update.

> Are you relying on the initial exposure to be some particular value?
> Is the initial value for any setting actually guaranteed in the spec?
> If your application is expecting max exposure by default, then that
> sounds very fragile and not portable to other sensors, but it can be
> fixed up.

I'm not relying on any specific value, everything is configured/controlled by 
libcamera.

Best regards,
Alexander

> Do note that switching to 1280x720 mode will limit the exposure range
> to 1-763. Switching back to 1920x1080 mode will increase the range
> again, but as the current value is still in range the exposure setting
> will not be changed. What controls get reset when changing mode is
> undefined, but AIUI it should update the ranges and leave everything
> else alone, which is what is done here. Some drivers pepper
> v4l2_ctrl_s_ctrl calls around the place, but what value to adopt seems
> random.
> 
>   Dave
> 
> >   Dave
> >   
> > > Best regards,
> > > Alexander
> > > 
> > > >  }
> > > >  
> > > >  static int imx290_ctrl_init(struct imx290 *imx290)
> > > > 
> > > > @@ -761,9 +793,12 @@ static int imx290_ctrl_init(struct imx290
> > > > *imx290)
> > > > 
> > > >       v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > > >       
> > > >                         V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
> > > > 
> > > > -     v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > > > -                       V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2,
> > > 
> > > 1,
> > > 
> > > > -                       IMX290_VMAX_DEFAULT - 2);
> > > > +     /*
> > > > +      * Correct range will be determined through imx290_ctrl_update
> > > 
> > > setting
> > > 
> > > > +      * V4L2_CID_VBLANK.
> > > > +      */
> > > > +     imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls,
> > > 
> > > &imx290_ctrl_ops,
> > > 
> > > > +                                          V4L2_CID_EXPOSURE, 1,
> > > 
> > > 1, 1, 1);
> > > 
> > > >       /*
> > > >       
> > > >        * Set the link frequency, pixel rate, horizontal blanking and
> > > 
> > > vertical
> > > 
> > > > @@ -795,8 +830,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > > 
> > > >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls,
> > > >       &imx290_ctrl_ops,
> > > >       
> > > >                                          V4L2_CID_VBLANK, 1, 1, 1,
> > > 
> > > 1);
> > > 
> > > > -     if (imx290->vblank)
> > > > -             imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > > > 
> > > >       v4l2_ctrl_new_fwnode_properties(&imx290->ctrls,
> > > >       &imx290_ctrl_ops,
> > > >       
> > > >                                       &props);





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

* Re: [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write
  2023-01-31 19:20 ` [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write Dave Stevenson
  2023-02-02 14:58   ` Laurent Pinchart
@ 2023-02-03  7:19   ` Alexander Stein
  2023-02-03  8:05     ` Dave Stevenson
  1 sibling, 1 reply; 49+ messages in thread
From: Alexander Stein @ 2023-02-03  7:19 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Dave Stevenson
  Cc: Dave Stevenson

Hi Dave,

thanks for the patch.

Am Dienstag, 31. Januar 2023, 20:20:12 CET schrieb Dave Stevenson:
> The driver exposed V4L2_CID_HBLANK as a read only control to allow
> for exposure calculations and determination of the frame rate.
> 
> Convert to a read/write control so that the frame rate can be
> controlled.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 33 +++++++++++++++++++--------------
>  1 file changed, 19 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 9ddd6382b127..9006be6e5e7c 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -47,6 +47,7 @@
>  #define IMX290_GAIN					
IMX290_REG_8BIT(0x3014)
>  #define IMX290_VMAX					
IMX290_REG_24BIT(0x3018)
>  #define IMX290_HMAX					
IMX290_REG_16BIT(0x301c)
> +#define IMX290_HMAX_MAX					0xffff
>  #define IMX290_SHS1					
IMX290_REG_24BIT(0x3020)
>  #define IMX290_WINWV_OB					
IMX290_REG_8BIT(0x303a)
>  #define IMX290_WINPV					
IMX290_REG_16BIT(0x303c)
> @@ -167,7 +168,7 @@ struct imx290_regval {
>  struct imx290_mode {
>  	u32 width;
>  	u32 height;
> -	u32 hmax;
> +	u32 hmax_min;
>  	u8 link_freq_index;
> 
>  	const struct imx290_regval *data;
> @@ -410,7 +411,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> { {
>  		.width = 1920,
>  		.height = 1080,
> -		.hmax = 2200,
> +		.hmax_min = 2200,
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> @@ -418,7 +419,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> { {
>  		.width = 1280,
>  		.height = 720,
> -		.hmax = 3300,
> +		.hmax_min = 3300,
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> @@ -429,7 +430,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> { {
>  		.width = 1920,
>  		.height = 1080,
> -		.hmax = 2200,
> +		.hmax_min = 2200,
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> @@ -437,7 +438,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> { {
>  		.width = 1280,
>  		.height = 720,
> -		.hmax = 3300,
> +		.hmax_min = 3300,
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> @@ -686,6 +687,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)

You will need to remove V4L2_CID_HBLANK on the immediately return check at the 
beginning of the function. Otherwise this setting will never reach the device.

Best regards
Alexander

>  		}
>  		break;
> 
> +	case V4L2_CID_HBLANK:
> +		ret = imx290_write(imx290, IMX290_HMAX,
> +				   ctrl->val + imx290->current_mode-
>width,
> +				   NULL);
> +		break;
> +
>  	default:
>  		ret = -EINVAL;
>  		break;
> @@ -716,12 +723,14 @@ static void imx290_ctrl_update(struct imx290 *imx290,
>  			       const struct v4l2_mbus_framefmt *format,
>  			       const struct imx290_mode *mode)
>  {
> -	unsigned int hblank = mode->hmax - mode->width;
> +	unsigned int hblank_min = mode->hmax_min - mode->width;
> +	unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
>  	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> 
>  	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> 
> -	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
> +	__v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
> +				 hblank_min);
>  	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
>  }
> 
> @@ -778,10 +787,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  				     
ARRAY_SIZE(imx290_test_pattern_menu) - 1,
>  				     0, 0, imx290_test_pattern_menu);
> 
> +	/*
> +	 * Actual range will be set from imx290_ctrl_update later in the 
probe.
> +	 */
>  	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>  					   V4L2_CID_HBLANK, 1, 1, 1, 
1);
> -	if (imx290->hblank)
> -		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> 
>  	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>  					   V4L2_CID_VBLANK, 1, 1, 1, 
1);
> @@ -850,11 +860,6 @@ static int imx290_start_streaming(struct imx290
> *imx290, return ret;
>  	}
> 
> -	ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
> -			   NULL);
> -	if (ret)
> -		return ret;
> -
>  	/* Apply customized values from user */
>  	ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
>  	if (ret) {





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

* Re: [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips
  2023-01-31 19:20 ` [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips Dave Stevenson
  2023-02-02 22:10   ` Laurent Pinchart
@ 2023-02-03  7:35   ` Alexander Stein
  2023-02-03  7:57     ` Dave Stevenson
  1 sibling, 1 reply; 49+ messages in thread
From: Alexander Stein @ 2023-02-03  7:35 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Dave Stevenson
  Cc: Dave Stevenson

Hi Dave,

thanks for the patch.

Am Dienstag, 31. Januar 2023, 20:20:16 CET schrieb Dave Stevenson:
> The sensor supports H & V flips, so add the relevant hooks for
> V4L2_CID_HFLIP and V4L2_CID_VFLIP to configure them.
> 
> Note that the Bayer order is maintained as the readout area
> shifts by 1 pixel in the appropriate direction (note the
> comment about the top margin being 8 pixels whilst the bottom
> margin is 9). The V4L2_SEL_TGT_CROP region is therefore
> adjusted appropriately.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 37 ++++++++++++++++++++++++++++++++++---
>  1 file changed, 34 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 7f6746f74040..d2b7534f2c51 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -227,6 +227,8 @@ struct imx290 {
>  	struct v4l2_ctrl *hblank;
>  	struct v4l2_ctrl *vblank;
>  	struct v4l2_ctrl *exposure;
> +	struct v4l2_ctrl *hflip;
> +	struct v4l2_ctrl *vflip;
>  };
> 
>  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> @@ -801,6 +803,24 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  				   NULL);
>  		break;
> 
> +	case V4L2_CID_HFLIP:
> +	case V4L2_CID_VFLIP:
> +	{
> +		u32 reg;
> +
> +		/* WINMODE is in bits [6:4], so need to read-modify-write 
*/
> +		ret = imx290_read(imx290, IMX290_CTRL_07, &reg);
> +		if (ret)
> +			break;
> +		reg &= ~(IMX290_HREVERSE | IMX290_VREVERSE);
> +		if (imx290->hflip->val)
> +			reg |= IMX290_HREVERSE;
> +		if (imx290->vflip->val)
> +			reg |= IMX290_VREVERSE;
> +		ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL);
> +		break;
> +	}

Given the grab while streaming is on, it can't be changed while streaming. But 
then again the pm_runtime check above will prevent setting the registers while 
streaming is off.

> +
>  	default:
>  		ret = -EINVAL;
>  		break;
> @@ -853,7 +873,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  	if (ret < 0)
>  		return ret;
> 
> -	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> +	v4l2_ctrl_handler_init(&imx290->ctrls, 11);
> 
>  	/*
>  	 * The sensor has an analog gain and a digital gain, both controlled
> @@ -909,6 +929,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>  					   V4L2_CID_VBLANK, 1, 1, 1, 
1);
> 
> +	imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> +					  V4L2_CID_HFLIP, 0, 1, 1, 
0);
> +	imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> +					  V4L2_CID_VFLIP, 0, 1, 1, 
0);
> +
>  	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
>  					&props);
> 
> @@ -1030,6 +1055,9 @@ static int imx290_set_stream(struct v4l2_subdev *sd,
> int enable) pm_runtime_put_autosuspend(imx290->dev);
>  	}
> 
> +	/* vflip and hflip cannot change during streaming */
> +	__v4l2_ctrl_grab(imx290->vflip, enable);
> +	__v4l2_ctrl_grab(imx290->hflip, enable);

Why is this grab necessary? While trying to remove these lines, I can flip the 
image while streaming.

Best regards,
Alexander

>  unlock:
>  	v4l2_subdev_unlock_state(state);
>  	return ret;
> @@ -1115,6 +1143,7 @@ static int imx290_get_selection(struct v4l2_subdev
> *sd, struct v4l2_subdev_state *sd_state,
>  				struct v4l2_subdev_selection *sel)
>  {
> +	struct imx290 *imx290 = to_imx290(sd);
>  	struct v4l2_mbus_framefmt *format;
> 
>  	switch (sel->target) {
> @@ -1122,9 +1151,11 @@ static int imx290_get_selection(struct v4l2_subdev
> *sd, format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> 
>  		sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
> -			   + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - 
format->height) / 2;
> +			   + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - 
format->height) / 2
> +			   + imx290->vflip->val;
>  		sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT
> -			    + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - 
format->width) / 2;
> +			    + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - 
format->width) / 2
> +			    + imx290->hflip->val;
>  		sel->r.width = format->width;
>  		sel->r.height = format->height;





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

* Re: [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock
  2023-01-31 19:20 ` [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock Dave Stevenson
  2023-02-02 22:03   ` Laurent Pinchart
@ 2023-02-03  7:44   ` Alexander Stein
  2023-02-03  8:59     ` Laurent Pinchart
  1 sibling, 1 reply; 49+ messages in thread
From: Alexander Stein @ 2023-02-03  7:44 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Dave Stevenson
  Cc: Dave Stevenson

Hi Dave,

thanks for the patch.

Am Dienstag, 31. Januar 2023, 20:20:15 CET schrieb Dave Stevenson:
> The sensor supports either a 37.125 or 74.25MHz external, but
> the driver only supported 37.125MHz.
> 
> Add the relevant register configuration for either clock
> frequency option.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 120 +++++++++++++++++++++++++++++++------
>  1 file changed, 103 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 5202ef3cc3e6..7f6746f74040 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -102,6 +102,7 @@
>  #define IMX290_TCLKPREPARE				
IMX290_REG_16BIT(0x3452)
>  #define IMX290_TLPX					
IMX290_REG_16BIT(0x3454)
>  #define IMX290_X_OUT_SIZE				
IMX290_REG_16BIT(0x3472)
> +#define IMX290_INCKSEL7					
IMX290_REG_8BIT(0x3480)

Please add also defines for the clock setting, e.g.
#define IMX290_INCKSEL7_INCK_37_125	0x49
#define IMX290_INCKSEL7_INCK_74_25	0x92

> 
>  #define IMX290_PGCTRL_REGEN				BIT(0)
>  #define IMX290_PGCTRL_THRU				BIT(1)
> @@ -159,11 +160,27 @@
> 
>  #define IMX290_NUM_SUPPLIES				3
> 
> +#define CLK_37_125	0
> +#define CLK_74_25	1
> +#define NUM_CLK		2
> +

How about using an enum?

enum imx290_clk_freq {
	CLK_37_125 = 0,
	CLK_74_25 = 1,
	NUM_CLK
};

>  struct imx290_regval {
>  	u32 reg;
>  	u32 val;
>  };
> 
> +/*
> + * Clock configuration for registers INCKSEL1 to INCKSEL6.
> + */
> +struct imx290_clk_cfg {
> +	u8 incksel1;
> +	u8 incksel2;
> +	u8 incksel3;
> +	u8 incksel4;
> +	u8 incksel5;
> +	u8 incksel6;
> +};
> +
>  struct imx290_mode {
>  	u32 width;
>  	u32 height;
> @@ -173,6 +190,8 @@ struct imx290_mode {
> 
>  	const struct imx290_regval *data;
>  	u32 data_size;
> +
> +	const struct imx290_clk_cfg *clk_cfg;
>  };
> 
>  struct imx290_csi_cfg {
> @@ -191,6 +210,7 @@ struct imx290 {
>  	struct device *dev;
>  	struct clk *xclk;
>  	struct regmap *regmap;
> +	u32 xclk_freq;
>  	u8 nlanes;
>  	u8 mono;
> 
> @@ -219,7 +239,6 @@ static inline struct imx290 *to_imx290(struct
> v4l2_subdev *_sd) */
> 
>  static const struct imx290_regval imx290_global_init_settings[] = {
> -	{ IMX290_EXTCK_FREQ, 0x2520 },
>  	{ IMX290_WINWV_OB, 12 },
>  	{ IMX290_WINPH, 0 },
>  	{ IMX290_WINPV, 0 },
> @@ -269,7 +288,16 @@ static const struct imx290_regval
> imx290_global_init_settings[] = { { IMX290_REG_8BIT(0x33b0), 0x50 },
>  	{ IMX290_REG_8BIT(0x33b2), 0x1a },
>  	{ IMX290_REG_8BIT(0x33b3), 0x04 },
> -	{ IMX290_REG_8BIT(0x3480), 0x49 },
> +};
> +
> +static const struct imx290_regval imx290_37_125mhz_clock[] = {
> +	{ IMX290_EXTCK_FREQ, 0x2520 },

Please also add defines for these magic numbers.

Best regards,
Alexander

> +	{ IMX290_INCKSEL7, 0x49 },
> +};
> +
> +static const struct imx290_regval imx290_74_25mhz_clock[] = {
> +	{ IMX290_EXTCK_FREQ, 0x4a40 },
> +	{ IMX290_INCKSEL7, 0x92 },
>  };
> 
>  static const struct imx290_regval imx290_1080p_settings[] = {
> @@ -279,12 +307,6 @@ static const struct imx290_regval
> imx290_1080p_settings[] = { { IMX290_OPB_SIZE_V, 10 },
>  	{ IMX290_X_OUT_SIZE, 1920 },
>  	{ IMX290_Y_OUT_SIZE, 1080 },
> -	{ IMX290_INCKSEL1, 0x18 },
> -	{ IMX290_INCKSEL2, 0x03 },
> -	{ IMX290_INCKSEL3, 0x20 },
> -	{ IMX290_INCKSEL4, 0x01 },
> -	{ IMX290_INCKSEL5, 0x1a },
> -	{ IMX290_INCKSEL6, 0x1a },
>  };
> 
>  static const struct imx290_regval imx290_720p_settings[] = {
> @@ -294,12 +316,6 @@ static const struct imx290_regval
> imx290_720p_settings[] = { { IMX290_OPB_SIZE_V, 4 },
>  	{ IMX290_X_OUT_SIZE, 1280 },
>  	{ IMX290_Y_OUT_SIZE, 720 },
> -	{ IMX290_INCKSEL1, 0x20 },
> -	{ IMX290_INCKSEL2, 0x00 },
> -	{ IMX290_INCKSEL3, 0x20 },
> -	{ IMX290_INCKSEL4, 0x01 },
> -	{ IMX290_INCKSEL5, 0x1a },
> -	{ IMX290_INCKSEL6, 0x1a },
>  };
> 
>  static const struct imx290_regval imx290_10bit_settings[] = {
> @@ -405,6 +421,48 @@ static inline int imx290_link_freqs_num(const struct
> imx290 *imx290) return ARRAY_SIZE(imx290_link_freq_4lanes);
>  }
> 
> +static const struct imx290_clk_cfg imx290_1080p_clock_config[NUM_CLK] = {
> +	[CLK_37_125] = {
> +		/* 37.125MHz clock config */
> +		.incksel1 = 0x18,
> +		.incksel2 = 0x03,
> +		.incksel3 = 0x20,
> +		.incksel4 = 0x01,
> +		.incksel5 = 0x1a,
> +		.incksel6 = 0x1a,
> +	},
> +	[CLK_74_25] = {
> +		/* 74.25MHz clock config */
> +		.incksel1 = 0x0c,
> +		.incksel2 = 0x03,
> +		.incksel3 = 0x10,
> +		.incksel4 = 0x01,
> +		.incksel5 = 0x1b,
> +		.incksel6 = 0x1b,
> +	},
> +};
> +
> +static const struct imx290_clk_cfg imx290_720p_clock_config[NUM_CLK] = {
> +	[CLK_37_125] = {
> +		/* 37.125MHz clock config */
> +		.incksel1 = 0x20,
> +		.incksel2 = 0x00,
> +		.incksel3 = 0x20,
> +		.incksel4 = 0x01,
> +		.incksel5 = 0x1a,
> +		.incksel6 = 0x1a,
> +	},
> +	[CLK_74_25] = {
> +		/* 74.25MHz clock config */
> +		.incksel1 = 0x10,
> +		.incksel2 = 0x00,
> +		.incksel3 = 0x10,
> +		.incksel4 = 0x01,
> +		.incksel5 = 0x1b,
> +		.incksel6 = 0x1b,
> +	},
> +};
> +
>  /* Mode configs */
>  static const struct imx290_mode imx290_modes_2lanes[] = {
>  	{
> @@ -415,6 +473,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> { .link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> +		.clk_cfg = imx290_1080p_clock_config,
>  	},
>  	{
>  		.width = 1280,
> @@ -424,6 +483,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> { .link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> +		.clk_cfg = imx290_720p_clock_config,
>  	},
>  };
> 
> @@ -436,6 +496,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> { .link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> +		.clk_cfg = imx290_1080p_clock_config,
>  	},
>  	{
>  		.width = 1280,
> @@ -445,6 +506,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> { .link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> +		.clk_cfg = imx290_720p_clock_config,
>  	},
>  };
> 
> @@ -563,6 +625,23 @@ static int imx290_set_register_array(struct imx290
> *imx290, return 0;
>  }
> 
> +static int imx290_set_clock(struct imx290 *imx290)
> +{
> +	int clk_idx = (imx290->xclk_freq == 37125000) ? 0 : 1;
> +	const struct imx290_mode *mode = imx290->current_mode;
> +	const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[clk_idx];
> +	int ret = 0;
> +
> +	imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret);
> +	imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret);
> +	imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret);
> +	imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret);
> +	imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret);
> +	imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret);
> +
> +	return ret;
> +}
> +
>  static int imx290_set_data_lanes(struct imx290 *imx290)
>  {
>  	int ret = 0;
> @@ -863,6 +942,13 @@ static int imx290_start_streaming(struct imx290
> *imx290, return ret;
>  	}
> 
> +	/* Set clock parameters based on mode and xclk */
> +	ret = imx290_set_clock(imx290);
> +	if (ret < 0) {
> +		dev_err(imx290->dev, "Could not set clocks\n");
> +		return ret;
> +	}
> +
>  	/* Set data lane count */
>  	ret = imx290_set_data_lanes(imx290);
>  	if (ret < 0) {
> @@ -1259,14 +1345,14 @@ static int imx290_init_clk(struct imx290 *imx290)
>  	int ret;
> 
>  	ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
> -				       "clock-frequency", &xclk_freq);
> +				       "clock-frequency", &imx290-
>xclk_freq);
>  	if (ret) {
>  		dev_err(imx290->dev, "Could not get xclk frequency\n");
>  		return ret;
>  	}
> 
> -	/* external clock must be 37.125 MHz */
> -	if (xclk_freq != 37125000) {
> +	/* external clock must be 37.125 MHz or 74.25MHz */
> +	if (imx290->xclk_freq != 37125000 && imx290->xclk_freq != 74250000) 
{
>  		dev_err(imx290->dev, "External clock frequency %u is not 
supported\n",
>  			xclk_freq);
>  		return -EINVAL;





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

* Re: [PATCH 09/11] media: i2c: imx290: Remove duplicated write to IMX290_CTRL_07
  2023-01-31 19:20 ` [PATCH 09/11] media: i2c: imx290: Remove duplicated write to IMX290_CTRL_07 Dave Stevenson
  2023-02-02 16:04   ` Laurent Pinchart
@ 2023-02-03  7:45   ` Alexander Stein
  1 sibling, 0 replies; 49+ messages in thread
From: Alexander Stein @ 2023-02-03  7:45 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Dave Stevenson
  Cc: Dave Stevenson

Hi Dave,

thank you for the patch.

Am Dienstag, 31. Januar 2023, 20:20:14 CET schrieb Dave Stevenson:
> IMX290_CTRL_07 was written from both imx290_global_init_settings
> and imx290_1080p_settings and imx290_720p_settings.
> 
> Remove it from imx290_global_init_settings as the setting varies
> based on the mode.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 3413d83369ba..5202ef3cc3e6 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -219,7 +219,6 @@ static inline struct imx290 *to_imx290(struct
> v4l2_subdev *_sd) */
> 
>  static const struct imx290_regval imx290_global_init_settings[] = {
> -	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
>  	{ IMX290_EXTCK_FREQ, 0x2520 },
>  	{ IMX290_WINWV_OB, 12 },
>  	{ IMX290_WINPH, 0 },

Reviewed-by: Alexander Stein <alexander.stein@ew.tq-group.com>



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

* Re: [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips
  2023-02-03  7:35   ` Alexander Stein
@ 2023-02-03  7:57     ` Dave Stevenson
  2023-02-03  8:33       ` Alexander Stein
  0 siblings, 1 reply; 49+ messages in thread
From: Dave Stevenson @ 2023-02-03  7:57 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media

Hi Alexander

On Fri, 3 Feb 2023 at 07:35, Alexander Stein
<alexander.stein@ew.tq-group.com> wrote:
>
> Hi Dave,
>
> thanks for the patch.
>
> Am Dienstag, 31. Januar 2023, 20:20:16 CET schrieb Dave Stevenson:
> > The sensor supports H & V flips, so add the relevant hooks for
> > V4L2_CID_HFLIP and V4L2_CID_VFLIP to configure them.
> >
> > Note that the Bayer order is maintained as the readout area
> > shifts by 1 pixel in the appropriate direction (note the
> > comment about the top margin being 8 pixels whilst the bottom
> > margin is 9). The V4L2_SEL_TGT_CROP region is therefore
> > adjusted appropriately.
> >
> > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > ---
> >  drivers/media/i2c/imx290.c | 37 ++++++++++++++++++++++++++++++++++---
> >  1 file changed, 34 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 7f6746f74040..d2b7534f2c51 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -227,6 +227,8 @@ struct imx290 {
> >       struct v4l2_ctrl *hblank;
> >       struct v4l2_ctrl *vblank;
> >       struct v4l2_ctrl *exposure;
> > +     struct v4l2_ctrl *hflip;
> > +     struct v4l2_ctrl *vflip;
> >  };
> >
> >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > @@ -801,6 +803,24 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> >                                  NULL);
> >               break;
> >
> > +     case V4L2_CID_HFLIP:
> > +     case V4L2_CID_VFLIP:
> > +     {
> > +             u32 reg;
> > +
> > +             /* WINMODE is in bits [6:4], so need to read-modify-write
> */
> > +             ret = imx290_read(imx290, IMX290_CTRL_07, &reg);
> > +             if (ret)
> > +                     break;
> > +             reg &= ~(IMX290_HREVERSE | IMX290_VREVERSE);
> > +             if (imx290->hflip->val)
> > +                     reg |= IMX290_HREVERSE;
> > +             if (imx290->vflip->val)
> > +                     reg |= IMX290_VREVERSE;
> > +             ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL);
> > +             break;
> > +     }
>
> Given the grab while streaming is on, it can't be changed while streaming. But
> then again the pm_runtime check above will prevent setting the registers while
> streaming is off.

But __v4l2_ctrl_handler_setup is called from imx290_start_streaming
after pm_runtime_resume_and_get, and so will programme the hardware.

Writing the flips from the set_ctrl even if they have been grabbed due
to changing the Bayer order is the normal approach with many other
drivers. See imx319, imx219, imx335, imx415, and probably others.

> > +
> >       default:
> >               ret = -EINVAL;
> >               break;
> > @@ -853,7 +873,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >       if (ret < 0)
> >               return ret;
> >
> > -     v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> > +     v4l2_ctrl_handler_init(&imx290->ctrls, 11);
> >
> >       /*
> >        * The sensor has an analog gain and a digital gain, both controlled
> > @@ -909,6 +929,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> >                                          V4L2_CID_VBLANK, 1, 1, 1,
> 1);
> >
> > +     imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > +                                       V4L2_CID_HFLIP, 0, 1, 1,
> 0);
> > +     imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > +                                       V4L2_CID_VFLIP, 0, 1, 1,
> 0);
> > +
> >       v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
> >                                       &props);
> >
> > @@ -1030,6 +1055,9 @@ static int imx290_set_stream(struct v4l2_subdev *sd,
> > int enable) pm_runtime_put_autosuspend(imx290->dev);
> >       }
> >
> > +     /* vflip and hflip cannot change during streaming */
> > +     __v4l2_ctrl_grab(imx290->vflip, enable);
> > +     __v4l2_ctrl_grab(imx290->hflip, enable);
>
> Why is this grab necessary? While trying to remove these lines, I can flip the
> image while streaming.

IMX290 Datasheet section "Normal and inverted operation":
"One invalid frame is generated when reading immediately after the
readout direction is changed in order to switch the normal operation
and inversion of frames."

There is no synchronisation between CSI2 receiver and the subdev, so
no way to signal that corrupt frame.
Is it permitted for sources to knowingly deliver corrupt frames? My
understanding is not.

  Dave

> Best regards,
> Alexander
>
> >  unlock:
> >       v4l2_subdev_unlock_state(state);
> >       return ret;
> > @@ -1115,6 +1143,7 @@ static int imx290_get_selection(struct v4l2_subdev
> > *sd, struct v4l2_subdev_state *sd_state,
> >                               struct v4l2_subdev_selection *sel)
> >  {
> > +     struct imx290 *imx290 = to_imx290(sd);
> >       struct v4l2_mbus_framefmt *format;
> >
> >       switch (sel->target) {
> > @@ -1122,9 +1151,11 @@ static int imx290_get_selection(struct v4l2_subdev
> > *sd, format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> >
> >               sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
> > -                        + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT -
> format->height) / 2;
> > +                        + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT -
> format->height) / 2
> > +                        + imx290->vflip->val;
> >               sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT
> > -                         + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH -
> format->width) / 2;
> > +                         + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH -
> format->width) / 2
> > +                         + imx290->hflip->val;
> >               sel->r.width = format->width;
> >               sel->r.height = format->height;
>
>
>
>

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

* Re: [PATCH 06/11] media: i2c: imx290: Use CSI timings as per datasheet
  2023-01-31 19:20 ` [PATCH 06/11] media: i2c: imx290: Use CSI timings as per datasheet Dave Stevenson
  2023-02-02 14:36   ` Laurent Pinchart
@ 2023-02-03  8:04   ` Alexander Stein
  2023-02-03  9:09     ` Dave Stevenson
  1 sibling, 1 reply; 49+ messages in thread
From: Alexander Stein @ 2023-02-03  8:04 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Dave Stevenson
  Cc: Dave Stevenson

Hi Dave,

thanks for the patch.

Am Dienstag, 31. Januar 2023, 20:20:11 CET schrieb Dave Stevenson:
> Commit "98e0500eadb7 media: i2c: imx290: Add configurable link frequency
> and pixel rate" added support for the increased link frequencies
> on 2 data lanes, but didn't update the CSI timing registers in
> accordance with the datasheet.
> 
> Use the specified settings.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 126 +++++++++++++++++++++++++++++++------
>  1 file changed, 106 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 6bcfa535872f..9ddd6382b127 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -174,6 +174,18 @@ struct imx290_mode {
>  	u32 data_size;
>  };
> 
> +struct imx290_csi_cfg {
> +	u16 repitition;
> +	u16 tclkpost;
> +	u16 thszero;
> +	u16 thsprepare;
> +	u16 tclktrail;
> +	u16 thstrail;
> +	u16 tclkzero;
> +	u16 tclkprepare;
> +	u16 tlpx;
> +};
> +
>  struct imx290 {
>  	struct device *dev;
>  	struct clk *xclk;
> @@ -273,16 +285,6 @@ static const struct imx290_regval
> imx290_1080p_settings[] = { { IMX290_INCKSEL4, 0x01 },
>  	{ IMX290_INCKSEL5, 0x1a },
>  	{ IMX290_INCKSEL6, 0x1a },
> -	/* data rate settings */
> -	{ IMX290_REPETITION, 0x10 },
> -	{ IMX290_TCLKPOST, 87 },
> -	{ IMX290_THSZERO, 55 },
> -	{ IMX290_THSPREPARE, 31 },
> -	{ IMX290_TCLKTRAIL, 31 },
> -	{ IMX290_THSTRAIL, 31 },
> -	{ IMX290_TCLKZERO, 119 },
> -	{ IMX290_TCLKPREPARE, 31 },
> -	{ IMX290_TLPX, 23 },
>  };
> 
>  static const struct imx290_regval imx290_720p_settings[] = {
> @@ -298,16 +300,6 @@ static const struct imx290_regval
> imx290_720p_settings[] = { { IMX290_INCKSEL4, 0x01 },
>  	{ IMX290_INCKSEL5, 0x1a },
>  	{ IMX290_INCKSEL6, 0x1a },
> -	/* data rate settings */
> -	{ IMX290_REPETITION, 0x10 },
> -	{ IMX290_TCLKPOST, 79 },
> -	{ IMX290_THSZERO, 47 },
> -	{ IMX290_THSPREPARE, 23 },
> -	{ IMX290_TCLKTRAIL, 23 },
> -	{ IMX290_THSTRAIL, 23 },
> -	{ IMX290_TCLKZERO, 87 },
> -	{ IMX290_TCLKPREPARE, 23 },
> -	{ IMX290_TLPX, 23 },
>  };
> 
>  static const struct imx290_regval imx290_10bit_settings[] = {
> @@ -328,6 +320,58 @@ static const struct imx290_regval
> imx290_12bit_settings[] = { { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
>  };
> 
> +static const struct imx290_csi_cfg imx290_csi_222_75mhz = {
> +	/* 222.25MHz or 445.5Mbit/s per lane */

222.75MHz. This is also 891 MBit/s per lane, no? This link frequency is used 
for 4 lane setup only.

> +	.repitition = 0x10,
> +	.tclkpost = 87,
> +	.thszero = 55,
> +	.thsprepare = 31,
> +	.tclktrail = 31,
> +	.thstrail = 31,
> +	.tclkzero = 119,
> +	.tclkprepare = 31,
> +	.tlpx = 23,
> +};
> +
> +static const struct imx290_csi_cfg imx290_csi_445_5mhz = {
> +	/* 445.5MHz or 891Mbit/s per lane */
> +	.repitition = 0x00,
> +	.tclkpost = 119,
> +	.thszero = 103,
> +	.thsprepare = 71,
> +	.tclktrail = 55,
> +	.thstrail = 63,
> +	.tclkzero = 255,
> +	.tclkprepare = 63,
> +	.tlpx = 55,
> +};
> +
> +static const struct imx290_csi_cfg imx290_csi_148_5mhz = {
> +	/* 148.5MHz or 297Mbit/s per lane */

594 MBit/s per lane, no? This link freq is only used for 4 lanes

> +	.repitition = 0x10,

Ah, this is so confusing. This structure is used depending on the link_freq, 
but this value is defined on the data rate (for both 2 & 4 lanes separately).

Best regards,
Alexander

> +	.tclkpost = 79,
> +	.thszero = 47,
> +	.thsprepare = 23,
> +	.tclktrail = 23,
> +	.thstrail = 23,
> +	.tclkzero = 87,
> +	.tclkprepare = 23,
> +	.tlpx = 23,
> +};
> +
> +static const struct imx290_csi_cfg imx290_csi_297mhz = {
> +	/* 297MHz or 594Mbit/s per lane */
> +	.repitition = 0x00,
> +	.tclkpost = 103,
> +	.thszero = 87,
> +	.thsprepare = 47,
> +	.tclktrail = 39,
> +	.thstrail = 47,
> +	.tclkzero = 191,
> +	.tclkprepare = 47,
> +	.tlpx = 39,
> +};
> +
>  /* supported link frequencies */
>  #define FREQ_INDEX_1080P	0
>  #define FREQ_INDEX_720P		1
> @@ -536,6 +580,42 @@ static int imx290_set_black_level(struct imx290
> *imx290, black_level >> (16 - bpp), err);
>  }
> 
> +static int imx290_set_csi_config(struct imx290 *imx290)
> +{
> +	const s64 *link_freqs = imx290_link_freqs_ptr(imx290);
> +	const struct imx290_csi_cfg *csi_cfg;
> +	int ret = 0;
> +
> +	switch (link_freqs[imx290->current_mode->link_freq_index]) {
> +	case 445500000:
> +		csi_cfg = &imx290_csi_445_5mhz;
> +		break;
> +	case 297000000:
> +		csi_cfg = &imx290_csi_297mhz;
> +		break;
> +	case 222750000:
> +		csi_cfg = &imx290_csi_222_75mhz;
> +		break;
> +	case 148500000:
> +		csi_cfg = &imx290_csi_148_5mhz;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	imx290_write(imx290, IMX290_REPETITION, csi_cfg->repitition, &ret);
> +	imx290_write(imx290, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret);
> +	imx290_write(imx290, IMX290_THSZERO, csi_cfg->thszero, &ret);
> +	imx290_write(imx290, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret);
> +	imx290_write(imx290, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret);
> +	imx290_write(imx290, IMX290_THSTRAIL, csi_cfg->thstrail, &ret);
> +	imx290_write(imx290, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret);
> +	imx290_write(imx290, IMX290_TCLKPREPARE, csi_cfg->tclkprepare, 
&ret);
> +	imx290_write(imx290, IMX290_TLPX, csi_cfg->tlpx, &ret);
> +
> +	return ret;
> +}
> +
>  static int imx290_setup_format(struct imx290 *imx290,
>  			       const struct v4l2_mbus_framefmt *format)
>  {
> @@ -748,6 +828,12 @@ static int imx290_start_streaming(struct imx290
> *imx290, return ret;
>  	}
> 
> +	ret = imx290_set_csi_config(imx290);
> +	if (ret < 0) {
> +		dev_err(imx290->dev, "Could not set csi cfg\n");
> +		return ret;
> +	}
> +
>  	/* Apply the register values related to current frame format */
>  	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
>  	ret = imx290_setup_format(imx290, format);





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

* Re: [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write
  2023-02-03  7:19   ` Alexander Stein
@ 2023-02-03  8:05     ` Dave Stevenson
  2023-02-03  8:39       ` Alexander Stein
  0 siblings, 1 reply; 49+ messages in thread
From: Dave Stevenson @ 2023-02-03  8:05 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media

Hi Alexander

On Fri, 3 Feb 2023 at 07:19, Alexander Stein
<alexander.stein@ew.tq-group.com> wrote:
>
> Hi Dave,
>
> thanks for the patch.
>
> Am Dienstag, 31. Januar 2023, 20:20:12 CET schrieb Dave Stevenson:
> > The driver exposed V4L2_CID_HBLANK as a read only control to allow
> > for exposure calculations and determination of the frame rate.
> >
> > Convert to a read/write control so that the frame rate can be
> > controlled.
> >
> > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > ---
> >  drivers/media/i2c/imx290.c | 33 +++++++++++++++++++--------------
> >  1 file changed, 19 insertions(+), 14 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 9ddd6382b127..9006be6e5e7c 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -47,6 +47,7 @@
> >  #define IMX290_GAIN
> IMX290_REG_8BIT(0x3014)
> >  #define IMX290_VMAX
> IMX290_REG_24BIT(0x3018)
> >  #define IMX290_HMAX
> IMX290_REG_16BIT(0x301c)
> > +#define IMX290_HMAX_MAX                                      0xffff
> >  #define IMX290_SHS1
> IMX290_REG_24BIT(0x3020)
> >  #define IMX290_WINWV_OB
> IMX290_REG_8BIT(0x303a)
> >  #define IMX290_WINPV
> IMX290_REG_16BIT(0x303c)
> > @@ -167,7 +168,7 @@ struct imx290_regval {
> >  struct imx290_mode {
> >       u32 width;
> >       u32 height;
> > -     u32 hmax;
> > +     u32 hmax_min;
> >       u8 link_freq_index;
> >
> >       const struct imx290_regval *data;
> > @@ -410,7 +411,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> > { {
> >               .width = 1920,
> >               .height = 1080,
> > -             .hmax = 2200,
> > +             .hmax_min = 2200,
> >               .link_freq_index = FREQ_INDEX_1080P,
> >               .data = imx290_1080p_settings,
> >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > @@ -418,7 +419,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> > { {
> >               .width = 1280,
> >               .height = 720,
> > -             .hmax = 3300,
> > +             .hmax_min = 3300,
> >               .link_freq_index = FREQ_INDEX_720P,
> >               .data = imx290_720p_settings,
> >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > @@ -429,7 +430,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> > { {
> >               .width = 1920,
> >               .height = 1080,
> > -             .hmax = 2200,
> > +             .hmax_min = 2200,
> >               .link_freq_index = FREQ_INDEX_1080P,
> >               .data = imx290_1080p_settings,
> >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > @@ -437,7 +438,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> > { {
> >               .width = 1280,
> >               .height = 720,
> > -             .hmax = 3300,
> > +             .hmax_min = 3300,
> >               .link_freq_index = FREQ_INDEX_720P,
> >               .data = imx290_720p_settings,
> >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > @@ -686,6 +687,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>
> You will need to remove V4L2_CID_HBLANK on the immediately return check at the
> beginning of the function. Otherwise this setting will never reach the device.

What tree are you adding these patches to? I'm basing it on Sakari's
tree at [1] - he's issued a pull for it, so that should be in 6.3.

The only immediate return check at the start of imx290_set_ctrl is
if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
   return 0;

The controls are no longer READ_ONLY, therefore they don't return early.
There is no case for V4L2_CID_HBLANK.

Does this also account for the difference you're reporting with V4L2_CID_VBLANK?

 Dave

[1] https://git.linuxtv.org/sailus/media_tree.git/tree/drivers/media/i2c/imx290.c#n567

> Best regards
> Alexander
>
> >               }
> >               break;
> >
> > +     case V4L2_CID_HBLANK:
> > +             ret = imx290_write(imx290, IMX290_HMAX,
> > +                                ctrl->val + imx290->current_mode-
> >width,
> > +                                NULL);
> > +             break;
> > +
> >       default:
> >               ret = -EINVAL;
> >               break;
> > @@ -716,12 +723,14 @@ static void imx290_ctrl_update(struct imx290 *imx290,
> >                              const struct v4l2_mbus_framefmt *format,
> >                              const struct imx290_mode *mode)
> >  {
> > -     unsigned int hblank = mode->hmax - mode->width;
> > +     unsigned int hblank_min = mode->hmax_min - mode->width;
> > +     unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
> >       unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> >
> >       __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> >
> > -     __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
> > +     __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
> > +                              hblank_min);
> >       __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> >  }
> >
> > @@ -778,10 +787,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >
> ARRAY_SIZE(imx290_test_pattern_menu) - 1,
> >                                    0, 0, imx290_test_pattern_menu);
> >
> > +     /*
> > +      * Actual range will be set from imx290_ctrl_update later in the
> probe.
> > +      */
> >       imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> >                                          V4L2_CID_HBLANK, 1, 1, 1,
> 1);
> > -     if (imx290->hblank)
> > -             imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> >
> >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> >                                          V4L2_CID_VBLANK, 1, 1, 1,
> 1);
> > @@ -850,11 +860,6 @@ static int imx290_start_streaming(struct imx290
> > *imx290, return ret;
> >       }
> >
> > -     ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax,
> > -                        NULL);
> > -     if (ret)
> > -             return ret;
> > -
> >       /* Apply customized values from user */
> >       ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> >       if (ret) {
>
>
>
>

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

* Re: [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips
  2023-02-03  7:57     ` Dave Stevenson
@ 2023-02-03  8:33       ` Alexander Stein
  2023-02-03  9:47         ` Dave Stevenson
  0 siblings, 1 reply; 49+ messages in thread
From: Alexander Stein @ 2023-02-03  8:33 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media

Am Freitag, 3. Februar 2023, 08:57:37 CET schrieb Dave Stevenson:
> Hi Alexander
> 
> On Fri, 3 Feb 2023 at 07:35, Alexander Stein
> 
> <alexander.stein@ew.tq-group.com> wrote:
> > Hi Dave,
> > 
> > thanks for the patch.
> > 
> > Am Dienstag, 31. Januar 2023, 20:20:16 CET schrieb Dave Stevenson:
> > > The sensor supports H & V flips, so add the relevant hooks for
> > > V4L2_CID_HFLIP and V4L2_CID_VFLIP to configure them.
> > > 
> > > Note that the Bayer order is maintained as the readout area
> > > shifts by 1 pixel in the appropriate direction (note the
> > > comment about the top margin being 8 pixels whilst the bottom
> > > margin is 9). The V4L2_SEL_TGT_CROP region is therefore
> > > adjusted appropriately.
> > > 
> > > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > > ---
> > > 
> > >  drivers/media/i2c/imx290.c | 37 ++++++++++++++++++++++++++++++++++---
> > >  1 file changed, 34 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index 7f6746f74040..d2b7534f2c51 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -227,6 +227,8 @@ struct imx290 {
> > > 
> > >       struct v4l2_ctrl *hblank;
> > >       struct v4l2_ctrl *vblank;
> > >       struct v4l2_ctrl *exposure;
> > > 
> > > +     struct v4l2_ctrl *hflip;
> > > +     struct v4l2_ctrl *vflip;
> > > 
> > >  };
> > >  
> > >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > > 
> > > @@ -801,6 +803,24 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > 
> > >                                  NULL);
> > >               
> > >               break;
> > > 
> > > +     case V4L2_CID_HFLIP:
> > > +     case V4L2_CID_VFLIP:
> > > +     {
> > > +             u32 reg;
> > > +
> > > +             /* WINMODE is in bits [6:4], so need to read-modify-write
> > 
> > */
> > 
> > > +             ret = imx290_read(imx290, IMX290_CTRL_07, &reg);
> > > +             if (ret)
> > > +                     break;
> > > +             reg &= ~(IMX290_HREVERSE | IMX290_VREVERSE);
> > > +             if (imx290->hflip->val)
> > > +                     reg |= IMX290_HREVERSE;
> > > +             if (imx290->vflip->val)
> > > +                     reg |= IMX290_VREVERSE;
> > > +             ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL);
> > > +             break;
> > > +     }
> > 
> > Given the grab while streaming is on, it can't be changed while streaming.
> > But then again the pm_runtime check above will prevent setting the
> > registers while streaming is off.
> 
> But __v4l2_ctrl_handler_setup is called from imx290_start_streaming
> after pm_runtime_resume_and_get, and so will programme the hardware.

That's right. I just noticed libcamera (libcamersrc in a gst-pipeline 
actrually) resets flipping to default when starting... so this behaviour is 
understandable, although somewhat unexpected.

> Writing the flips from the set_ctrl even if they have been grabbed due
> to changing the Bayer order is the normal approach with many other
> drivers. See imx319, imx219, imx335, imx415, and probably others.

What I meant is that it's not possible to change flipping from userspace while 
being grabbed.

> > > +
> > > 
> > >       default:
> > >               ret = -EINVAL;
> > >               break;
> > > 
> > > @@ -853,7 +873,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > 
> > >       if (ret < 0)
> > >       
> > >               return ret;
> > > 
> > > -     v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> > > +     v4l2_ctrl_handler_init(&imx290->ctrls, 11);
> > > 
> > >       /*
> > >       
> > >        * The sensor has an analog gain and a digital gain, both
> > >        controlled
> > > 
> > > @@ -909,6 +929,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > 
> > >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls,
> > >       &imx290_ctrl_ops,
> > >       
> > >                                          V4L2_CID_VBLANK, 1, 1, 1,
> > 
> > 1);
> > 
> > > +     imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls,
> > > &imx290_ctrl_ops,
> > > +                                       V4L2_CID_HFLIP, 0, 1, 1,
> > 
> > 0);
> > 
> > > +     imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls,
> > > &imx290_ctrl_ops,
> > > +                                       V4L2_CID_VFLIP, 0, 1, 1,
> > 
> > 0);
> > 
> > > +
> > > 
> > >       v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
> > >       
> > >                                       &props);
> > > 
> > > @@ -1030,6 +1055,9 @@ static int imx290_set_stream(struct v4l2_subdev
> > > *sd,
> > > int enable) pm_runtime_put_autosuspend(imx290->dev);
> > > 
> > >       }
> > > 
> > > +     /* vflip and hflip cannot change during streaming */
> > > +     __v4l2_ctrl_grab(imx290->vflip, enable);
> > > +     __v4l2_ctrl_grab(imx290->hflip, enable);
> > 
> > Why is this grab necessary? While trying to remove these lines, I can flip
> > the image while streaming.
> 
> IMX290 Datasheet section "Normal and inverted operation":
> "One invalid frame is generated when reading immediately after the
> readout direction is changed in order to switch the normal operation
> and inversion of frames."
> 
> There is no synchronisation between CSI2 receiver and the subdev, so
> no way to signal that corrupt frame.
> Is it permitted for sources to knowingly deliver corrupt frames? My
> understanding is not.

I see. I was not aware of this, maybe due to this there should be a comment 
why changing flips during stream should not be done. Current comment is 
totally misleading.

Best regards
Alexande

>   Dave
> 
> > Best regards,
> > Alexander
> > 
> > >  unlock:
> > >       v4l2_subdev_unlock_state(state);
> > >       return ret;
> > > 
> > > @@ -1115,6 +1143,7 @@ static int imx290_get_selection(struct v4l2_subdev
> > > *sd, struct v4l2_subdev_state *sd_state,
> > > 
> > >                               struct v4l2_subdev_selection *sel)
> > >  
> > >  {
> > > 
> > > +     struct imx290 *imx290 = to_imx290(sd);
> > > 
> > >       struct v4l2_mbus_framefmt *format;
> > >       
> > >       switch (sel->target) {
> > > 
> > > @@ -1122,9 +1151,11 @@ static int imx290_get_selection(struct
> > > v4l2_subdev
> > > *sd, format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> > > 
> > >               sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
> > > 
> > > -                        + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT -
> > 
> > format->height) / 2;
> > 
> > > +                        + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT -
> > 
> > format->height) / 2
> > 
> > > +                        + imx290->vflip->val;
> > > 
> > >               sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT
> > > 
> > > -                         + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH -
> > 
> > format->width) / 2;
> > 
> > > +                         + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH -
> > 
> > format->width) / 2
> > 
> > > +                         + imx290->hflip->val;
> > > 
> > >               sel->r.width = format->width;
> > >               sel->r.height = format->height;





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

* Re: [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write
  2023-02-03  8:05     ` Dave Stevenson
@ 2023-02-03  8:39       ` Alexander Stein
  0 siblings, 0 replies; 49+ messages in thread
From: Alexander Stein @ 2023-02-03  8:39 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media

Hi Dave,

Am Freitag, 3. Februar 2023, 09:05:44 CET schrieb Dave Stevenson:
> Hi Alexander
> 
> On Fri, 3 Feb 2023 at 07:19, Alexander Stein
> 
> <alexander.stein@ew.tq-group.com> wrote:
> > Hi Dave,
> > 
> > thanks for the patch.
> > 
> > Am Dienstag, 31. Januar 2023, 20:20:12 CET schrieb Dave Stevenson:
> > > The driver exposed V4L2_CID_HBLANK as a read only control to allow
> > > for exposure calculations and determination of the frame rate.
> > > 
> > > Convert to a read/write control so that the frame rate can be
> > > controlled.
> > > 
> > > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > > ---
> > > 
> > >  drivers/media/i2c/imx290.c | 33 +++++++++++++++++++--------------
> > >  1 file changed, 19 insertions(+), 14 deletions(-)
> > > 
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index 9ddd6382b127..9006be6e5e7c 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -47,6 +47,7 @@
> > > 
> > >  #define IMX290_GAIN
> > 
> > IMX290_REG_8BIT(0x3014)
> > 
> > >  #define IMX290_VMAX
> > 
> > IMX290_REG_24BIT(0x3018)
> > 
> > >  #define IMX290_HMAX
> > 
> > IMX290_REG_16BIT(0x301c)
> > 
> > > +#define IMX290_HMAX_MAX                                      0xffff
> > > 
> > >  #define IMX290_SHS1
> > 
> > IMX290_REG_24BIT(0x3020)
> > 
> > >  #define IMX290_WINWV_OB
> > 
> > IMX290_REG_8BIT(0x303a)
> > 
> > >  #define IMX290_WINPV
> > 
> > IMX290_REG_16BIT(0x303c)
> > 
> > > @@ -167,7 +168,7 @@ struct imx290_regval {
> > > 
> > >  struct imx290_mode {
> > >  
> > >       u32 width;
> > >       u32 height;
> > > 
> > > -     u32 hmax;
> > > +     u32 hmax_min;
> > > 
> > >       u8 link_freq_index;
> > >       
> > >       const struct imx290_regval *data;
> > > 
> > > @@ -410,7 +411,7 @@ static const struct imx290_mode
> > > imx290_modes_2lanes[] =
> > > { {
> > > 
> > >               .width = 1920,
> > >               .height = 1080,
> > > 
> > > -             .hmax = 2200,
> > > +             .hmax_min = 2200,
> > > 
> > >               .link_freq_index = FREQ_INDEX_1080P,
> > >               .data = imx290_1080p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > 
> > > @@ -418,7 +419,7 @@ static const struct imx290_mode
> > > imx290_modes_2lanes[] =
> > > { {
> > > 
> > >               .width = 1280,
> > >               .height = 720,
> > > 
> > > -             .hmax = 3300,
> > > +             .hmax_min = 3300,
> > > 
> > >               .link_freq_index = FREQ_INDEX_720P,
> > >               .data = imx290_720p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > 
> > > @@ -429,7 +430,7 @@ static const struct imx290_mode
> > > imx290_modes_4lanes[] =
> > > { {
> > > 
> > >               .width = 1920,
> > >               .height = 1080,
> > > 
> > > -             .hmax = 2200,
> > > +             .hmax_min = 2200,
> > > 
> > >               .link_freq_index = FREQ_INDEX_1080P,
> > >               .data = imx290_1080p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > 
> > > @@ -437,7 +438,7 @@ static const struct imx290_mode
> > > imx290_modes_4lanes[] =
> > > { {
> > > 
> > >               .width = 1280,
> > >               .height = 720,
> > > 
> > > -             .hmax = 3300,
> > > +             .hmax_min = 3300,
> > > 
> > >               .link_freq_index = FREQ_INDEX_720P,
> > >               .data = imx290_720p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > 
> > > @@ -686,6 +687,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > 
> > You will need to remove V4L2_CID_HBLANK on the immediately return check at
> > the beginning of the function. Otherwise this setting will never reach
> > the device.
> What tree are you adding these patches to? I'm basing it on Sakari's
> tree at [1] - he's issued a pull for it, so that should be in 6.3.

Thanks for pointing this out. I'm based on linux-next as I need other patches 
for platform support. I've had Laurent's patches included, but only v2 :( my 
bad. With v3 included instead this seems to work.

> The only immediate return check at the start of imx290_set_ctrl is
> if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
>    return 0;
> 
> The controls are no longer READ_ONLY, therefore they don't return early.
> There is no case for V4L2_CID_HBLANK.
> 
> Does this also account for the difference you're reporting with
> V4L2_CID_VBLANK?

Yes, things look good so far. Thanks

Best regards,
Alexander

> [1]
> https://git.linuxtv.org/sailus/media_tree.git/tree/drivers/media/i2c/imx290
> .c#n567
> > Best regards
> > Alexander
> > 
> > >               }
> > >               break;
> > > 
> > > +     case V4L2_CID_HBLANK:
> > > +             ret = imx290_write(imx290, IMX290_HMAX,
> > > +                                ctrl->val + imx290->current_mode-
> > >
> > >width,
> > >
> > > +                                NULL);
> > > +             break;
> > > +
> > > 
> > >       default:
> > >               ret = -EINVAL;
> > >               break;
> > > 
> > > @@ -716,12 +723,14 @@ static void imx290_ctrl_update(struct imx290
> > > *imx290,
> > > 
> > >                              const struct v4l2_mbus_framefmt *format,
> > >                              const struct imx290_mode *mode)
> > >  
> > >  {
> > > 
> > > -     unsigned int hblank = mode->hmax - mode->width;
> > > +     unsigned int hblank_min = mode->hmax_min - mode->width;
> > > +     unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
> > > 
> > >       unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> > >       
> > >       __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> > > 
> > > -     __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1,
> > > hblank);
> > > +     __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max,
> > > 1,
> > > +                              hblank_min);
> > > 
> > >       __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1,
> > >       vblank);
> > >  
> > >  }
> > > 
> > > @@ -778,10 +787,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > 
> > ARRAY_SIZE(imx290_test_pattern_menu) - 1,
> > 
> > >                                    0, 0, imx290_test_pattern_menu);
> > > 
> > > +     /*
> > > +      * Actual range will be set from imx290_ctrl_update later in the
> > 
> > probe.
> > 
> > > +      */
> > > 
> > >       imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls,
> > >       &imx290_ctrl_ops,
> > >       
> > >                                          V4L2_CID_HBLANK, 1, 1, 1,
> > 
> > 1);
> > 
> > > -     if (imx290->hblank)
> > > -             imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > > 
> > >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls,
> > >       &imx290_ctrl_ops,
> > >       
> > >                                          V4L2_CID_VBLANK, 1, 1, 1,
> > 
> > 1);
> > 
> > > @@ -850,11 +860,6 @@ static int imx290_start_streaming(struct imx290
> > > *imx290, return ret;
> > > 
> > >       }
> > > 
> > > -     ret = imx290_write(imx290, IMX290_HMAX,
> > > imx290->current_mode->hmax,
> > > -                        NULL);
> > > -     if (ret)
> > > -             return ret;
> > > -
> > > 
> > >       /* Apply customized values from user */
> > >       ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> > >       if (ret) {





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

* Re: [PATCH 05/11] media: i2c: imx290: Support 60fps in 2 lane operation
  2023-01-31 19:20 ` [PATCH 05/11] media: i2c: imx290: Support 60fps in 2 lane operation Dave Stevenson
  2023-02-02 10:50   ` Laurent Pinchart
@ 2023-02-03  8:50   ` Alexander Stein
  1 sibling, 0 replies; 49+ messages in thread
From: Alexander Stein @ 2023-02-03  8:50 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Dave Stevenson
  Cc: Dave Stevenson

Hi Dave,

thanks for the patch.

Am Dienstag, 31. Januar 2023, 20:20:10 CET schrieb Dave Stevenson:
> Commit "97589ad61c73 media: i2c: imx290: Add support for 2 data lanes"
> added support for running in two lane mode (instead of 4), but
> without changing the link frequency that resulted in a max of 30fps.
> 
> Commit "98e0500eadb7 media: i2c: imx290: Add configurable link frequency
> and pixel rate" then doubled the link frequency when in 2 lane mode,
> but didn't undo the correction for running at only 30fps, just extending
> horizontal blanking instead.
> 
> Remove the 30fps limit on 2 lane by correcting the register config
> in accordance with the datasheet for 60fps operation over 2 lanes.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> ---
>  drivers/media/i2c/imx290.c | 17 +++--------------
>  1 file changed, 3 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index bd8729aed43c..6bcfa535872f 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -366,7 +366,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> { {
>  		.width = 1920,
>  		.height = 1080,
> -		.hmax = 4400,
> +		.hmax = 2200,
>  		.link_freq_index = FREQ_INDEX_1080P,
>  		.data = imx290_1080p_settings,
>  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> @@ -374,7 +374,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> { {
>  		.width = 1280,
>  		.height = 720,
> -		.hmax = 6600,
> +		.hmax = 3300,
>  		.link_freq_index = FREQ_INDEX_720P,
>  		.data = imx290_720p_settings,
>  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> @@ -518,21 +518,10 @@ static int imx290_set_register_array(struct imx290
> *imx290, static int imx290_set_data_lanes(struct imx290 *imx290)
>  {
>  	int ret = 0;
> -	u32 frsel;
> -
> -	switch (imx290->nlanes) {
> -	case 2:
> -	default:
> -		frsel = 0x02;
> -		break;
> -	case 4:
> -		frsel = 0x01;
> -		break;
> -	}
> 
>  	imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret);
>  	imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, 
&ret);
> -	imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
> +	imx290_write(imx290, IMX290_FR_FDG_SEL, 0x01, &ret);
> 
>  	return ret;
>  }

Reviewed-by: Alexander Stein <alexander.stein@ew.tq-group.com>




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

* Re: [PATCH 03/11] media: i2c: imx290: Add V4L2_SUBDEV_FL_HAS_EVENTS and subscribe hooks
  2023-01-31 19:20 ` [PATCH 03/11] media: i2c: imx290: Add V4L2_SUBDEV_FL_HAS_EVENTS and subscribe hooks Dave Stevenson
  2023-02-02  0:29   ` Laurent Pinchart
@ 2023-02-03  8:54   ` Alexander Stein
  1 sibling, 0 replies; 49+ messages in thread
From: Alexander Stein @ 2023-02-03  8:54 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Dave Stevenson
  Cc: Dave Stevenson

Hi Dave,

thanks for the patch.

Am Dienstag, 31. Januar 2023, 20:20:08 CET schrieb Dave Stevenson:
> Any V4L2 subdevice that implements controls and declares
> V4L2_SUBDEV_FL_HAS_DEVNODE should also declare V4L2_SUBDEV_FL_HAS_EVENTS
> and implement subscribe_event and unsubscribe_event hooks.
> 
> This driver didn't and would therefore fail v4l2-compliance
> testing.
> 
> Add the relevant hooks.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

Reviewed-by: Alexander Stein <alexander.stein@ew.tq-group.com>

> ---
>  drivers/media/i2c/imx290.c | 10 +++++++++-
>  1 file changed, 9 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index bf96fd914303..12946ca9d8d2 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -20,6 +20,7 @@
>  #include <media/media-entity.h>
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-device.h>
> +#include <media/v4l2-event.h>
>  #include <media/v4l2-fwnode.h>
>  #include <media/v4l2-subdev.h>
> 
> @@ -977,6 +978,11 @@ static int imx290_entity_init_cfg(struct v4l2_subdev
> *subdev, return 0;
>  }
> 
> +static const struct v4l2_subdev_core_ops imx290_core_ops = {
> +	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
> +	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
> +};
> +
>  static const struct v4l2_subdev_video_ops imx290_video_ops = {
>  	.s_stream = imx290_set_stream,
>  };
> @@ -991,6 +997,7 @@ static const struct v4l2_subdev_pad_ops imx290_pad_ops =
> { };
> 
>  static const struct v4l2_subdev_ops imx290_subdev_ops = {
> +	.core = &imx290_core_ops,
>  	.video = &imx290_video_ops,
>  	.pad = &imx290_pad_ops,
>  };
> @@ -1009,7 +1016,8 @@ static int imx290_subdev_init(struct imx290 *imx290)
>  	imx290->current_mode = &imx290_modes_ptr(imx290)[0];
> 
>  	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> -	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
> +			    V4L2_SUBDEV_FL_HAS_EVENTS;
>  	imx290->sd.dev = imx290->dev;
>  	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
>  	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;





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

* Re: [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock
  2023-02-03  7:44   ` Alexander Stein
@ 2023-02-03  8:59     ` Laurent Pinchart
  2023-02-03  9:30       ` Dave Stevenson
  0 siblings, 1 reply; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-03  8:59 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media,
	Dave Stevenson

On Fri, Feb 03, 2023 at 08:44:08AM +0100, Alexander Stein wrote:
> Hi Dave,
> 
> thanks for the patch.
> 
> Am Dienstag, 31. Januar 2023, 20:20:15 CET schrieb Dave Stevenson:
> > The sensor supports either a 37.125 or 74.25MHz external, but
> > the driver only supported 37.125MHz.
> > 
> > Add the relevant register configuration for either clock
> > frequency option.
> > 
> > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > ---
> >  drivers/media/i2c/imx290.c | 120 +++++++++++++++++++++++++++++++------
> >  1 file changed, 103 insertions(+), 17 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 5202ef3cc3e6..7f6746f74040 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -102,6 +102,7 @@
> >  #define IMX290_TCLKPREPARE				IMX290_REG_16BIT(0x3452)
> >  #define IMX290_TLPX				IMX290_REG_16BIT(0x3454)
> >  #define IMX290_X_OUT_SIZE				IMX290_REG_16BIT(0x3472)
> > +#define IMX290_INCKSEL7				IMX290_REG_8BIT(0x3480)
> 
> Please add also defines for the clock setting, e.g.
> #define IMX290_INCKSEL7_INCK_37_125	0x49
> #define IMX290_INCKSEL7_INCK_74_25	0x92
> 
> >  #define IMX290_PGCTRL_REGEN				BIT(0)
> >  #define IMX290_PGCTRL_THRU				BIT(1)
> > @@ -159,11 +160,27 @@
> > 
> >  #define IMX290_NUM_SUPPLIES				3
> > 
> > +#define CLK_37_125	0
> > +#define CLK_74_25	1
> > +#define NUM_CLK		2
> > +
> 
> How about using an enum?
> 
> enum imx290_clk_freq {
> 	CLK_37_125 = 0,
> 	CLK_74_25 = 1,
> 	NUM_CLK
> };
> 
> >  struct imx290_regval {
> >  	u32 reg;
> >  	u32 val;
> >  };
> > 
> > +/*
> > + * Clock configuration for registers INCKSEL1 to INCKSEL6.
> > + */
> > +struct imx290_clk_cfg {
> > +	u8 incksel1;
> > +	u8 incksel2;
> > +	u8 incksel3;
> > +	u8 incksel4;
> > +	u8 incksel5;
> > +	u8 incksel6;
> > +};
> > +
> >  struct imx290_mode {
> >  	u32 width;
> >  	u32 height;
> > @@ -173,6 +190,8 @@ struct imx290_mode {
> > 
> >  	const struct imx290_regval *data;
> >  	u32 data_size;
> > +
> > +	const struct imx290_clk_cfg *clk_cfg;
> >  };
> > 
> >  struct imx290_csi_cfg {
> > @@ -191,6 +210,7 @@ struct imx290 {
> >  	struct device *dev;
> >  	struct clk *xclk;
> >  	struct regmap *regmap;
> > +	u32 xclk_freq;
> >  	u8 nlanes;
> >  	u8 mono;
> > 
> > @@ -219,7 +239,6 @@ static inline struct imx290 *to_imx290(struct
> > v4l2_subdev *_sd) */
> > 
> >  static const struct imx290_regval imx290_global_init_settings[] = {
> > -	{ IMX290_EXTCK_FREQ, 0x2520 },
> >  	{ IMX290_WINWV_OB, 12 },
> >  	{ IMX290_WINPH, 0 },
> >  	{ IMX290_WINPV, 0 },
> > @@ -269,7 +288,16 @@ static const struct imx290_regval
> > imx290_global_init_settings[] = { { IMX290_REG_8BIT(0x33b0), 0x50 },
> >  	{ IMX290_REG_8BIT(0x33b2), 0x1a },
> >  	{ IMX290_REG_8BIT(0x33b3), 0x04 },
> > -	{ IMX290_REG_8BIT(0x3480), 0x49 },
> > +};
> > +
> > +static const struct imx290_regval imx290_37_125mhz_clock[] = {
> > +	{ IMX290_EXTCK_FREQ, 0x2520 },
> 
> Please also add defines for these magic numbers.

While I really like macros that name register and bits, as they improve
readability, for values that are listed in the documentation without any
explanation I think it doesn't make the code more readable.

> > +	{ IMX290_INCKSEL7, 0x49 },
> > +};
> > +
> > +static const struct imx290_regval imx290_74_25mhz_clock[] = {
> > +	{ IMX290_EXTCK_FREQ, 0x4a40 },
> > +	{ IMX290_INCKSEL7, 0x92 },
> >  };
> > 
> >  static const struct imx290_regval imx290_1080p_settings[] = {
> > @@ -279,12 +307,6 @@ static const struct imx290_regval
> > imx290_1080p_settings[] = { { IMX290_OPB_SIZE_V, 10 },
> >  	{ IMX290_X_OUT_SIZE, 1920 },
> >  	{ IMX290_Y_OUT_SIZE, 1080 },
> > -	{ IMX290_INCKSEL1, 0x18 },
> > -	{ IMX290_INCKSEL2, 0x03 },
> > -	{ IMX290_INCKSEL3, 0x20 },
> > -	{ IMX290_INCKSEL4, 0x01 },
> > -	{ IMX290_INCKSEL5, 0x1a },
> > -	{ IMX290_INCKSEL6, 0x1a },
> >  };
> > 
> >  static const struct imx290_regval imx290_720p_settings[] = {
> > @@ -294,12 +316,6 @@ static const struct imx290_regval
> > imx290_720p_settings[] = { { IMX290_OPB_SIZE_V, 4 },
> >  	{ IMX290_X_OUT_SIZE, 1280 },
> >  	{ IMX290_Y_OUT_SIZE, 720 },
> > -	{ IMX290_INCKSEL1, 0x20 },
> > -	{ IMX290_INCKSEL2, 0x00 },
> > -	{ IMX290_INCKSEL3, 0x20 },
> > -	{ IMX290_INCKSEL4, 0x01 },
> > -	{ IMX290_INCKSEL5, 0x1a },
> > -	{ IMX290_INCKSEL6, 0x1a },
> >  };
> > 
> >  static const struct imx290_regval imx290_10bit_settings[] = {
> > @@ -405,6 +421,48 @@ static inline int imx290_link_freqs_num(const struct
> > imx290 *imx290) return ARRAY_SIZE(imx290_link_freq_4lanes);
> >  }
> > 
> > +static const struct imx290_clk_cfg imx290_1080p_clock_config[NUM_CLK] = {
> > +	[CLK_37_125] = {
> > +		/* 37.125MHz clock config */
> > +		.incksel1 = 0x18,
> > +		.incksel2 = 0x03,
> > +		.incksel3 = 0x20,
> > +		.incksel4 = 0x01,
> > +		.incksel5 = 0x1a,
> > +		.incksel6 = 0x1a,
> > +	},
> > +	[CLK_74_25] = {
> > +		/* 74.25MHz clock config */
> > +		.incksel1 = 0x0c,
> > +		.incksel2 = 0x03,
> > +		.incksel3 = 0x10,
> > +		.incksel4 = 0x01,
> > +		.incksel5 = 0x1b,
> > +		.incksel6 = 0x1b,
> > +	},
> > +};
> > +
> > +static const struct imx290_clk_cfg imx290_720p_clock_config[NUM_CLK] = {
> > +	[CLK_37_125] = {
> > +		/* 37.125MHz clock config */
> > +		.incksel1 = 0x20,
> > +		.incksel2 = 0x00,
> > +		.incksel3 = 0x20,
> > +		.incksel4 = 0x01,
> > +		.incksel5 = 0x1a,
> > +		.incksel6 = 0x1a,
> > +	},
> > +	[CLK_74_25] = {
> > +		/* 74.25MHz clock config */
> > +		.incksel1 = 0x10,
> > +		.incksel2 = 0x00,
> > +		.incksel3 = 0x10,
> > +		.incksel4 = 0x01,
> > +		.incksel5 = 0x1b,
> > +		.incksel6 = 0x1b,
> > +	},
> > +};
> > +
> >  /* Mode configs */
> >  static const struct imx290_mode imx290_modes_2lanes[] = {
> >  	{
> > @@ -415,6 +473,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> > { .link_freq_index = FREQ_INDEX_1080P,
> >  		.data = imx290_1080p_settings,
> >  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> > +		.clk_cfg = imx290_1080p_clock_config,
> >  	},
> >  	{
> >  		.width = 1280,
> > @@ -424,6 +483,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> > { .link_freq_index = FREQ_INDEX_720P,
> >  		.data = imx290_720p_settings,
> >  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> > +		.clk_cfg = imx290_720p_clock_config,
> >  	},
> >  };
> > 
> > @@ -436,6 +496,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> > { .link_freq_index = FREQ_INDEX_1080P,
> >  		.data = imx290_1080p_settings,
> >  		.data_size = ARRAY_SIZE(imx290_1080p_settings),
> > +		.clk_cfg = imx290_1080p_clock_config,
> >  	},
> >  	{
> >  		.width = 1280,
> > @@ -445,6 +506,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> > { .link_freq_index = FREQ_INDEX_720P,
> >  		.data = imx290_720p_settings,
> >  		.data_size = ARRAY_SIZE(imx290_720p_settings),
> > +		.clk_cfg = imx290_720p_clock_config,
> >  	},
> >  };
> > 
> > @@ -563,6 +625,23 @@ static int imx290_set_register_array(struct imx290
> > *imx290, return 0;
> >  }
> > 
> > +static int imx290_set_clock(struct imx290 *imx290)
> > +{
> > +	int clk_idx = (imx290->xclk_freq == 37125000) ? 0 : 1;
> > +	const struct imx290_mode *mode = imx290->current_mode;
> > +	const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[clk_idx];
> > +	int ret = 0;
> > +
> > +	imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret);
> > +	imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret);
> > +	imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret);
> > +	imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret);
> > +	imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret);
> > +	imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret);
> > +
> > +	return ret;
> > +}
> > +
> >  static int imx290_set_data_lanes(struct imx290 *imx290)
> >  {
> >  	int ret = 0;
> > @@ -863,6 +942,13 @@ static int imx290_start_streaming(struct imx290
> > *imx290, return ret;
> >  	}
> > 
> > +	/* Set clock parameters based on mode and xclk */
> > +	ret = imx290_set_clock(imx290);
> > +	if (ret < 0) {
> > +		dev_err(imx290->dev, "Could not set clocks\n");
> > +		return ret;
> > +	}
> > +
> >  	/* Set data lane count */
> >  	ret = imx290_set_data_lanes(imx290);
> >  	if (ret < 0) {
> > @@ -1259,14 +1345,14 @@ static int imx290_init_clk(struct imx290 *imx290)
> >  	int ret;
> > 
> >  	ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
> > -				       "clock-frequency", &xclk_freq);
> > +				       "clock-frequency", &imx290->xclk_freq);
> >  	if (ret) {
> >  		dev_err(imx290->dev, "Could not get xclk frequency\n");
> >  		return ret;
> >  	}
> > 
> > -	/* external clock must be 37.125 MHz */
> > -	if (xclk_freq != 37125000) {
> > +	/* external clock must be 37.125 MHz or 74.25MHz */
> > +	if (imx290->xclk_freq != 37125000 && imx290->xclk_freq != 74250000) 
> {
> >  		dev_err(imx290->dev, "External clock frequency %u is not supported\n",
> >  			xclk_freq);
> >  		return -EINVAL;

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 06/11] media: i2c: imx290: Use CSI timings as per datasheet
  2023-02-03  8:04   ` Alexander Stein
@ 2023-02-03  9:09     ` Dave Stevenson
  0 siblings, 0 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-02-03  9:09 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media

Hi Alexander

On Fri, 3 Feb 2023 at 08:04, Alexander Stein
<alexander.stein@ew.tq-group.com> wrote:
>
> Hi Dave,
>
> thanks for the patch.
>
> Am Dienstag, 31. Januar 2023, 20:20:11 CET schrieb Dave Stevenson:
> > Commit "98e0500eadb7 media: i2c: imx290: Add configurable link frequency
> > and pixel rate" added support for the increased link frequencies
> > on 2 data lanes, but didn't update the CSI timing registers in
> > accordance with the datasheet.
> >
> > Use the specified settings.
> >
> > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > ---
> >  drivers/media/i2c/imx290.c | 126 +++++++++++++++++++++++++++++++------
> >  1 file changed, 106 insertions(+), 20 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 6bcfa535872f..9ddd6382b127 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -174,6 +174,18 @@ struct imx290_mode {
> >       u32 data_size;
> >  };
> >
> > +struct imx290_csi_cfg {
> > +     u16 repitition;
> > +     u16 tclkpost;
> > +     u16 thszero;
> > +     u16 thsprepare;
> > +     u16 tclktrail;
> > +     u16 thstrail;
> > +     u16 tclkzero;
> > +     u16 tclkprepare;
> > +     u16 tlpx;
> > +};
> > +
> >  struct imx290 {
> >       struct device *dev;
> >       struct clk *xclk;
> > @@ -273,16 +285,6 @@ static const struct imx290_regval
> > imx290_1080p_settings[] = { { IMX290_INCKSEL4, 0x01 },
> >       { IMX290_INCKSEL5, 0x1a },
> >       { IMX290_INCKSEL6, 0x1a },
> > -     /* data rate settings */
> > -     { IMX290_REPETITION, 0x10 },
> > -     { IMX290_TCLKPOST, 87 },
> > -     { IMX290_THSZERO, 55 },
> > -     { IMX290_THSPREPARE, 31 },
> > -     { IMX290_TCLKTRAIL, 31 },
> > -     { IMX290_THSTRAIL, 31 },
> > -     { IMX290_TCLKZERO, 119 },
> > -     { IMX290_TCLKPREPARE, 31 },
> > -     { IMX290_TLPX, 23 },
> >  };
> >
> >  static const struct imx290_regval imx290_720p_settings[] = {
> > @@ -298,16 +300,6 @@ static const struct imx290_regval
> > imx290_720p_settings[] = { { IMX290_INCKSEL4, 0x01 },
> >       { IMX290_INCKSEL5, 0x1a },
> >       { IMX290_INCKSEL6, 0x1a },
> > -     /* data rate settings */
> > -     { IMX290_REPETITION, 0x10 },
> > -     { IMX290_TCLKPOST, 79 },
> > -     { IMX290_THSZERO, 47 },
> > -     { IMX290_THSPREPARE, 23 },
> > -     { IMX290_TCLKTRAIL, 23 },
> > -     { IMX290_THSTRAIL, 23 },
> > -     { IMX290_TCLKZERO, 87 },
> > -     { IMX290_TCLKPREPARE, 23 },
> > -     { IMX290_TLPX, 23 },
> >  };
> >
> >  static const struct imx290_regval imx290_10bit_settings[] = {
> > @@ -328,6 +320,58 @@ static const struct imx290_regval
> > imx290_12bit_settings[] = { { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
> >  };
> >
> > +static const struct imx290_csi_cfg imx290_csi_222_75mhz = {
> > +     /* 222.25MHz or 445.5Mbit/s per lane */
>
> 222.75MHz.

Ack s/222.25/222.75. Silly typo.

> This is also 891 MBit/s per lane, no? This link frequency is used
> for 4 lane setup only.

CSI2 is Double Data Rate (DDR), so 2 bits per clock cycle. If the
clock is 222.75MHz, that makes 445.5Mbit/s/lane.

We only use 222.75MHz (1080p) and 148.5MHz (720p) link frequencies on
4 lanes, and 445.5MHz (1080p) and 297MHz (720p) on 2 lanes. That
allows a max of 60fps in either configuration.

Whilst the datasheet says you can drop the link frequencies if you're
running at 30fps, there is no need to, so the driver doesn't.
Looking at it I don't believe there is an actual requirement to drop
the link frequency for 720p either. What's the difference in pixel
readout between their 720p mode and a windowed mode that configures
the same offset and size? There should be none, but from the datasheet
they would use different link frequencies / data rates.

> > +     .repitition = 0x10,
> > +     .tclkpost = 87,
> > +     .thszero = 55,
> > +     .thsprepare = 31,
> > +     .tclktrail = 31,
> > +     .thstrail = 31,
> > +     .tclkzero = 119,
> > +     .tclkprepare = 31,
> > +     .tlpx = 23,
> > +};
> > +
> > +static const struct imx290_csi_cfg imx290_csi_445_5mhz = {
> > +     /* 445.5MHz or 891Mbit/s per lane */
> > +     .repitition = 0x00,
> > +     .tclkpost = 119,
> > +     .thszero = 103,
> > +     .thsprepare = 71,
> > +     .tclktrail = 55,
> > +     .thstrail = 63,
> > +     .tclkzero = 255,
> > +     .tclkprepare = 63,
> > +     .tlpx = 55,
> > +};
> > +
> > +static const struct imx290_csi_cfg imx290_csi_148_5mhz = {
> > +     /* 148.5MHz or 297Mbit/s per lane */
>
> 594 MBit/s per lane, no? This link freq is only used for 4 lanes

As above.

> > +     .repitition = 0x10,
>
> Ah, this is so confusing. This structure is used depending on the link_freq,
> but this value is defined on the data rate (for both 2 & 4 lanes separately).

link_freq = data rate / 2
(Note that this is the data rate on the CSI-2 bus, not pixel rate)

Data rate in the datasheet mode tables is quoted as units of Mbps/lane
Looking at the table for all-pixel mode:
2 lane 30/25fps, data rate 445.5Mbps/lane, repetition 1
2 lane 60/50fps, data rate 891Mbps/lane, repetition 0
4 lane 30/25fps, data rate 222.75Mbps/lane, repetition 2
4 lane 60/50fps, data rate 445.5Mbps/lane, repetition 1
4 lane 120/100fps, data rate 891Mbps/lane, repetition 0

Repetition and all the other CSI2 timing parameters are based solely
on data rate and therefore link frequency. Number of lanes doesn't
change them.

  Dave

> Best regards,
> Alexander
>
> > +     .tclkpost = 79,
> > +     .thszero = 47,
> > +     .thsprepare = 23,
> > +     .tclktrail = 23,
> > +     .thstrail = 23,
> > +     .tclkzero = 87,
> > +     .tclkprepare = 23,
> > +     .tlpx = 23,
> > +};
> > +
> > +static const struct imx290_csi_cfg imx290_csi_297mhz = {
> > +     /* 297MHz or 594Mbit/s per lane */
> > +     .repitition = 0x00,
> > +     .tclkpost = 103,
> > +     .thszero = 87,
> > +     .thsprepare = 47,
> > +     .tclktrail = 39,
> > +     .thstrail = 47,
> > +     .tclkzero = 191,
> > +     .tclkprepare = 47,
> > +     .tlpx = 39,
> > +};
> > +
> >  /* supported link frequencies */
> >  #define FREQ_INDEX_1080P     0
> >  #define FREQ_INDEX_720P              1
> > @@ -536,6 +580,42 @@ static int imx290_set_black_level(struct imx290
> > *imx290, black_level >> (16 - bpp), err);
> >  }
> >
> > +static int imx290_set_csi_config(struct imx290 *imx290)
> > +{
> > +     const s64 *link_freqs = imx290_link_freqs_ptr(imx290);
> > +     const struct imx290_csi_cfg *csi_cfg;
> > +     int ret = 0;
> > +
> > +     switch (link_freqs[imx290->current_mode->link_freq_index]) {
> > +     case 445500000:
> > +             csi_cfg = &imx290_csi_445_5mhz;
> > +             break;
> > +     case 297000000:
> > +             csi_cfg = &imx290_csi_297mhz;
> > +             break;
> > +     case 222750000:
> > +             csi_cfg = &imx290_csi_222_75mhz;
> > +             break;
> > +     case 148500000:
> > +             csi_cfg = &imx290_csi_148_5mhz;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     imx290_write(imx290, IMX290_REPETITION, csi_cfg->repitition, &ret);
> > +     imx290_write(imx290, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret);
> > +     imx290_write(imx290, IMX290_THSZERO, csi_cfg->thszero, &ret);
> > +     imx290_write(imx290, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret);
> > +     imx290_write(imx290, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret);
> > +     imx290_write(imx290, IMX290_THSTRAIL, csi_cfg->thstrail, &ret);
> > +     imx290_write(imx290, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret);
> > +     imx290_write(imx290, IMX290_TCLKPREPARE, csi_cfg->tclkprepare,
> &ret);
> > +     imx290_write(imx290, IMX290_TLPX, csi_cfg->tlpx, &ret);
> > +
> > +     return ret;
> > +}
> > +
> >  static int imx290_setup_format(struct imx290 *imx290,
> >                              const struct v4l2_mbus_framefmt *format)
> >  {
> > @@ -748,6 +828,12 @@ static int imx290_start_streaming(struct imx290
> > *imx290, return ret;
> >       }
> >
> > +     ret = imx290_set_csi_config(imx290);
> > +     if (ret < 0) {
> > +             dev_err(imx290->dev, "Could not set csi cfg\n");
> > +             return ret;
> > +     }
> > +
> >       /* Apply the register values related to current frame format */
> >       format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> >       ret = imx290_setup_format(imx290, format);
>
>
>
>

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

* Re: [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock
  2023-02-03  8:59     ` Laurent Pinchart
@ 2023-02-03  9:30       ` Dave Stevenson
  0 siblings, 0 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-02-03  9:30 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Alexander Stein, Manivannan Sadhasivam, Mauro Carvalho Chehab,
	linux-media

Hi Laurent

On Fri, 3 Feb 2023 at 08:59, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> On Fri, Feb 03, 2023 at 08:44:08AM +0100, Alexander Stein wrote:
> > Hi Dave,
> >
> > thanks for the patch.
> >
> > Am Dienstag, 31. Januar 2023, 20:20:15 CET schrieb Dave Stevenson:
> > > The sensor supports either a 37.125 or 74.25MHz external, but
> > > the driver only supported 37.125MHz.
> > >
> > > Add the relevant register configuration for either clock
> > > frequency option.
> > >
> > > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > > ---
> > >  drivers/media/i2c/imx290.c | 120 +++++++++++++++++++++++++++++++------
> > >  1 file changed, 103 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index 5202ef3cc3e6..7f6746f74040 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -102,6 +102,7 @@
> > >  #define IMX290_TCLKPREPARE                         IMX290_REG_16BIT(0x3452)
> > >  #define IMX290_TLPX                                IMX290_REG_16BIT(0x3454)
> > >  #define IMX290_X_OUT_SIZE                          IMX290_REG_16BIT(0x3472)
> > > +#define IMX290_INCKSEL7                            IMX290_REG_8BIT(0x3480)
> >
> > Please add also defines for the clock setting, e.g.
> > #define IMX290_INCKSEL7_INCK_37_125   0x49
> > #define IMX290_INCKSEL7_INCK_74_25    0x92
> >
> > >  #define IMX290_PGCTRL_REGEN                                BIT(0)
> > >  #define IMX290_PGCTRL_THRU                         BIT(1)
> > > @@ -159,11 +160,27 @@
> > >
> > >  #define IMX290_NUM_SUPPLIES                                3
> > >
> > > +#define CLK_37_125 0
> > > +#define CLK_74_25  1
> > > +#define NUM_CLK            2
> > > +
> >
> > How about using an enum?
> >
> > enum imx290_clk_freq {
> >       CLK_37_125 = 0,
> >       CLK_74_25 = 1,
> >       NUM_CLK
> > };
> >
> > >  struct imx290_regval {
> > >     u32 reg;
> > >     u32 val;
> > >  };
> > >
> > > +/*
> > > + * Clock configuration for registers INCKSEL1 to INCKSEL6.
> > > + */
> > > +struct imx290_clk_cfg {
> > > +   u8 incksel1;
> > > +   u8 incksel2;
> > > +   u8 incksel3;
> > > +   u8 incksel4;
> > > +   u8 incksel5;
> > > +   u8 incksel6;
> > > +};
> > > +
> > >  struct imx290_mode {
> > >     u32 width;
> > >     u32 height;
> > > @@ -173,6 +190,8 @@ struct imx290_mode {
> > >
> > >     const struct imx290_regval *data;
> > >     u32 data_size;
> > > +
> > > +   const struct imx290_clk_cfg *clk_cfg;
> > >  };
> > >
> > >  struct imx290_csi_cfg {
> > > @@ -191,6 +210,7 @@ struct imx290 {
> > >     struct device *dev;
> > >     struct clk *xclk;
> > >     struct regmap *regmap;
> > > +   u32 xclk_freq;
> > >     u8 nlanes;
> > >     u8 mono;
> > >
> > > @@ -219,7 +239,6 @@ static inline struct imx290 *to_imx290(struct
> > > v4l2_subdev *_sd) */
> > >
> > >  static const struct imx290_regval imx290_global_init_settings[] = {
> > > -   { IMX290_EXTCK_FREQ, 0x2520 },
> > >     { IMX290_WINWV_OB, 12 },
> > >     { IMX290_WINPH, 0 },
> > >     { IMX290_WINPV, 0 },
> > > @@ -269,7 +288,16 @@ static const struct imx290_regval
> > > imx290_global_init_settings[] = { { IMX290_REG_8BIT(0x33b0), 0x50 },
> > >     { IMX290_REG_8BIT(0x33b2), 0x1a },
> > >     { IMX290_REG_8BIT(0x33b3), 0x04 },
> > > -   { IMX290_REG_8BIT(0x3480), 0x49 },
> > > +};
> > > +
> > > +static const struct imx290_regval imx290_37_125mhz_clock[] = {
> > > +   { IMX290_EXTCK_FREQ, 0x2520 },
> >
> > Please also add defines for these magic numbers.
>
> While I really like macros that name register and bits, as they improve
> readability, for values that are listed in the documentation without any
> explanation I think it doesn't make the code more readable.

Technically they're not totally magic - it's just the frequency in MHz
as 8q8 fixed point.
(0x2520 = 9504 decimal) * 1/256 = 37.125.
(0x4a40 = 19008 decimal) * 1/256 = 74.25

As there are only 2 acceptable clock rates computing it feels like
overkill, but whatever.
I'll put it in as ((37125 * 256) / 1000). Still some magic, but makes
it a little more obvious what it is and should give the right number
(I need to check for overflows. Rounding seems OK).

  Dave

> > > +   { IMX290_INCKSEL7, 0x49 },
> > > +};
> > > +
> > > +static const struct imx290_regval imx290_74_25mhz_clock[] = {
> > > +   { IMX290_EXTCK_FREQ, 0x4a40 },
> > > +   { IMX290_INCKSEL7, 0x92 },
> > >  };
> > >
> > >  static const struct imx290_regval imx290_1080p_settings[] = {
> > > @@ -279,12 +307,6 @@ static const struct imx290_regval
> > > imx290_1080p_settings[] = { { IMX290_OPB_SIZE_V, 10 },
> > >     { IMX290_X_OUT_SIZE, 1920 },
> > >     { IMX290_Y_OUT_SIZE, 1080 },
> > > -   { IMX290_INCKSEL1, 0x18 },
> > > -   { IMX290_INCKSEL2, 0x03 },
> > > -   { IMX290_INCKSEL3, 0x20 },
> > > -   { IMX290_INCKSEL4, 0x01 },
> > > -   { IMX290_INCKSEL5, 0x1a },
> > > -   { IMX290_INCKSEL6, 0x1a },
> > >  };
> > >
> > >  static const struct imx290_regval imx290_720p_settings[] = {
> > > @@ -294,12 +316,6 @@ static const struct imx290_regval
> > > imx290_720p_settings[] = { { IMX290_OPB_SIZE_V, 4 },
> > >     { IMX290_X_OUT_SIZE, 1280 },
> > >     { IMX290_Y_OUT_SIZE, 720 },
> > > -   { IMX290_INCKSEL1, 0x20 },
> > > -   { IMX290_INCKSEL2, 0x00 },
> > > -   { IMX290_INCKSEL3, 0x20 },
> > > -   { IMX290_INCKSEL4, 0x01 },
> > > -   { IMX290_INCKSEL5, 0x1a },
> > > -   { IMX290_INCKSEL6, 0x1a },
> > >  };
> > >
> > >  static const struct imx290_regval imx290_10bit_settings[] = {
> > > @@ -405,6 +421,48 @@ static inline int imx290_link_freqs_num(const struct
> > > imx290 *imx290) return ARRAY_SIZE(imx290_link_freq_4lanes);
> > >  }
> > >
> > > +static const struct imx290_clk_cfg imx290_1080p_clock_config[NUM_CLK] = {
> > > +   [CLK_37_125] = {
> > > +           /* 37.125MHz clock config */
> > > +           .incksel1 = 0x18,
> > > +           .incksel2 = 0x03,
> > > +           .incksel3 = 0x20,
> > > +           .incksel4 = 0x01,
> > > +           .incksel5 = 0x1a,
> > > +           .incksel6 = 0x1a,
> > > +   },
> > > +   [CLK_74_25] = {
> > > +           /* 74.25MHz clock config */
> > > +           .incksel1 = 0x0c,
> > > +           .incksel2 = 0x03,
> > > +           .incksel3 = 0x10,
> > > +           .incksel4 = 0x01,
> > > +           .incksel5 = 0x1b,
> > > +           .incksel6 = 0x1b,
> > > +   },
> > > +};
> > > +
> > > +static const struct imx290_clk_cfg imx290_720p_clock_config[NUM_CLK] = {
> > > +   [CLK_37_125] = {
> > > +           /* 37.125MHz clock config */
> > > +           .incksel1 = 0x20,
> > > +           .incksel2 = 0x00,
> > > +           .incksel3 = 0x20,
> > > +           .incksel4 = 0x01,
> > > +           .incksel5 = 0x1a,
> > > +           .incksel6 = 0x1a,
> > > +   },
> > > +   [CLK_74_25] = {
> > > +           /* 74.25MHz clock config */
> > > +           .incksel1 = 0x10,
> > > +           .incksel2 = 0x00,
> > > +           .incksel3 = 0x10,
> > > +           .incksel4 = 0x01,
> > > +           .incksel5 = 0x1b,
> > > +           .incksel6 = 0x1b,
> > > +   },
> > > +};
> > > +
> > >  /* Mode configs */
> > >  static const struct imx290_mode imx290_modes_2lanes[] = {
> > >     {
> > > @@ -415,6 +473,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> > > { .link_freq_index = FREQ_INDEX_1080P,
> > >             .data = imx290_1080p_settings,
> > >             .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > +           .clk_cfg = imx290_1080p_clock_config,
> > >     },
> > >     {
> > >             .width = 1280,
> > > @@ -424,6 +483,7 @@ static const struct imx290_mode imx290_modes_2lanes[] =
> > > { .link_freq_index = FREQ_INDEX_720P,
> > >             .data = imx290_720p_settings,
> > >             .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > +           .clk_cfg = imx290_720p_clock_config,
> > >     },
> > >  };
> > >
> > > @@ -436,6 +496,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> > > { .link_freq_index = FREQ_INDEX_1080P,
> > >             .data = imx290_1080p_settings,
> > >             .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > +           .clk_cfg = imx290_1080p_clock_config,
> > >     },
> > >     {
> > >             .width = 1280,
> > > @@ -445,6 +506,7 @@ static const struct imx290_mode imx290_modes_4lanes[] =
> > > { .link_freq_index = FREQ_INDEX_720P,
> > >             .data = imx290_720p_settings,
> > >             .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > +           .clk_cfg = imx290_720p_clock_config,
> > >     },
> > >  };
> > >
> > > @@ -563,6 +625,23 @@ static int imx290_set_register_array(struct imx290
> > > *imx290, return 0;
> > >  }
> > >
> > > +static int imx290_set_clock(struct imx290 *imx290)
> > > +{
> > > +   int clk_idx = (imx290->xclk_freq == 37125000) ? 0 : 1;
> > > +   const struct imx290_mode *mode = imx290->current_mode;
> > > +   const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[clk_idx];
> > > +   int ret = 0;
> > > +
> > > +   imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret);
> > > +   imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret);
> > > +   imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret);
> > > +   imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret);
> > > +   imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret);
> > > +   imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret);
> > > +
> > > +   return ret;
> > > +}
> > > +
> > >  static int imx290_set_data_lanes(struct imx290 *imx290)
> > >  {
> > >     int ret = 0;
> > > @@ -863,6 +942,13 @@ static int imx290_start_streaming(struct imx290
> > > *imx290, return ret;
> > >     }
> > >
> > > +   /* Set clock parameters based on mode and xclk */
> > > +   ret = imx290_set_clock(imx290);
> > > +   if (ret < 0) {
> > > +           dev_err(imx290->dev, "Could not set clocks\n");
> > > +           return ret;
> > > +   }
> > > +
> > >     /* Set data lane count */
> > >     ret = imx290_set_data_lanes(imx290);
> > >     if (ret < 0) {
> > > @@ -1259,14 +1345,14 @@ static int imx290_init_clk(struct imx290 *imx290)
> > >     int ret;
> > >
> > >     ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
> > > -                                  "clock-frequency", &xclk_freq);
> > > +                                  "clock-frequency", &imx290->xclk_freq);
> > >     if (ret) {
> > >             dev_err(imx290->dev, "Could not get xclk frequency\n");
> > >             return ret;
> > >     }
> > >
> > > -   /* external clock must be 37.125 MHz */
> > > -   if (xclk_freq != 37125000) {
> > > +   /* external clock must be 37.125 MHz or 74.25MHz */
> > > +   if (imx290->xclk_freq != 37125000 && imx290->xclk_freq != 74250000)
> > {
> > >             dev_err(imx290->dev, "External clock frequency %u is not supported\n",
> > >                     xclk_freq);
> > >             return -EINVAL;
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips
  2023-02-03  8:33       ` Alexander Stein
@ 2023-02-03  9:47         ` Dave Stevenson
  0 siblings, 0 replies; 49+ messages in thread
From: Dave Stevenson @ 2023-02-03  9:47 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media

On Fri, 3 Feb 2023 at 08:33, Alexander Stein
<alexander.stein@ew.tq-group.com> wrote:
>
> Am Freitag, 3. Februar 2023, 08:57:37 CET schrieb Dave Stevenson:
> > Hi Alexander
> >
> > On Fri, 3 Feb 2023 at 07:35, Alexander Stein
> >
> > <alexander.stein@ew.tq-group.com> wrote:
> > > Hi Dave,
> > >
> > > thanks for the patch.
> > >
> > > Am Dienstag, 31. Januar 2023, 20:20:16 CET schrieb Dave Stevenson:
> > > > The sensor supports H & V flips, so add the relevant hooks for
> > > > V4L2_CID_HFLIP and V4L2_CID_VFLIP to configure them.
> > > >
> > > > Note that the Bayer order is maintained as the readout area
> > > > shifts by 1 pixel in the appropriate direction (note the
> > > > comment about the top margin being 8 pixels whilst the bottom
> > > > margin is 9). The V4L2_SEL_TGT_CROP region is therefore
> > > > adjusted appropriately.
> > > >
> > > > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > > > ---
> > > >
> > > >  drivers/media/i2c/imx290.c | 37 ++++++++++++++++++++++++++++++++++---
> > > >  1 file changed, 34 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > > index 7f6746f74040..d2b7534f2c51 100644
> > > > --- a/drivers/media/i2c/imx290.c
> > > > +++ b/drivers/media/i2c/imx290.c
> > > > @@ -227,6 +227,8 @@ struct imx290 {
> > > >
> > > >       struct v4l2_ctrl *hblank;
> > > >       struct v4l2_ctrl *vblank;
> > > >       struct v4l2_ctrl *exposure;
> > > >
> > > > +     struct v4l2_ctrl *hflip;
> > > > +     struct v4l2_ctrl *vflip;
> > > >
> > > >  };
> > > >
> > > >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > > >
> > > > @@ -801,6 +803,24 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > >
> > > >                                  NULL);
> > > >
> > > >               break;
> > > >
> > > > +     case V4L2_CID_HFLIP:
> > > > +     case V4L2_CID_VFLIP:
> > > > +     {
> > > > +             u32 reg;
> > > > +
> > > > +             /* WINMODE is in bits [6:4], so need to read-modify-write
> > >
> > > */
> > >
> > > > +             ret = imx290_read(imx290, IMX290_CTRL_07, &reg);
> > > > +             if (ret)
> > > > +                     break;
> > > > +             reg &= ~(IMX290_HREVERSE | IMX290_VREVERSE);
> > > > +             if (imx290->hflip->val)
> > > > +                     reg |= IMX290_HREVERSE;
> > > > +             if (imx290->vflip->val)
> > > > +                     reg |= IMX290_VREVERSE;
> > > > +             ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL);
> > > > +             break;
> > > > +     }
> > >
> > > Given the grab while streaming is on, it can't be changed while streaming.
> > > But then again the pm_runtime check above will prevent setting the
> > > registers while streaming is off.
> >
> > But __v4l2_ctrl_handler_setup is called from imx290_start_streaming
> > after pm_runtime_resume_and_get, and so will programme the hardware.
>
> That's right. I just noticed libcamera (libcamersrc in a gst-pipeline
> actrually) resets flipping to default when starting... so this behaviour is
> understandable, although somewhat unexpected.

Libcamera has controls for flips, and those will always adopt either
defaults or the user requested flips. If libcamera updating VBLANK to
control the frame rate or EXPOSURE isn't unexpected, why would it
updating FLIPs be unexpected?

Either libcamera is in control of the sensor, or the user is through
v4l2-ctl. A half-and-half doesn't work. (Been there, done that, whilst
trying to get HBLANK implemented in some drivers)

> > Writing the flips from the set_ctrl even if they have been grabbed due
> > to changing the Bayer order is the normal approach with many other
> > drivers. See imx319, imx219, imx335, imx415, and probably others.
>
> What I meant is that it's not possible to change flipping from userspace while
> being grabbed.
>
> > > > +
> > > >
> > > >       default:
> > > >               ret = -EINVAL;
> > > >               break;
> > > >
> > > > @@ -853,7 +873,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > >
> > > >       if (ret < 0)
> > > >
> > > >               return ret;
> > > >
> > > > -     v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> > > > +     v4l2_ctrl_handler_init(&imx290->ctrls, 11);
> > > >
> > > >       /*
> > > >
> > > >        * The sensor has an analog gain and a digital gain, both
> > > >        controlled
> > > >
> > > > @@ -909,6 +929,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > >
> > > >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls,
> > > >       &imx290_ctrl_ops,
> > > >
> > > >                                          V4L2_CID_VBLANK, 1, 1, 1,
> > >
> > > 1);
> > >
> > > > +     imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls,
> > > > &imx290_ctrl_ops,
> > > > +                                       V4L2_CID_HFLIP, 0, 1, 1,
> > >
> > > 0);
> > >
> > > > +     imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls,
> > > > &imx290_ctrl_ops,
> > > > +                                       V4L2_CID_VFLIP, 0, 1, 1,
> > >
> > > 0);
> > >
> > > > +
> > > >
> > > >       v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
> > > >
> > > >                                       &props);
> > > >
> > > > @@ -1030,6 +1055,9 @@ static int imx290_set_stream(struct v4l2_subdev
> > > > *sd,
> > > > int enable) pm_runtime_put_autosuspend(imx290->dev);
> > > >
> > > >       }
> > > >
> > > > +     /* vflip and hflip cannot change during streaming */
> > > > +     __v4l2_ctrl_grab(imx290->vflip, enable);
> > > > +     __v4l2_ctrl_grab(imx290->hflip, enable);
> > >
> > > Why is this grab necessary? While trying to remove these lines, I can flip
> > > the image while streaming.
> >
> > IMX290 Datasheet section "Normal and inverted operation":
> > "One invalid frame is generated when reading immediately after the
> > readout direction is changed in order to switch the normal operation
> > and inversion of frames."
> >
> > There is no synchronisation between CSI2 receiver and the subdev, so
> > no way to signal that corrupt frame.
> > Is it permitted for sources to knowingly deliver corrupt frames? My
> > understanding is not.
>
> I see. I was not aware of this, maybe due to this there should be a comment
> why changing flips during stream should not be done. Current comment is
> totally misleading.

Largely the same text in all the other drivers, even when it would
change the Bayer order. It's describing the result of the grab rather
than the reasoning, but I'll update it.

  Dave

> Best regards
> Alexande
>
> >   Dave
> >
> > > Best regards,
> > > Alexander
> > >
> > > >  unlock:
> > > >       v4l2_subdev_unlock_state(state);
> > > >       return ret;
> > > >
> > > > @@ -1115,6 +1143,7 @@ static int imx290_get_selection(struct v4l2_subdev
> > > > *sd, struct v4l2_subdev_state *sd_state,
> > > >
> > > >                               struct v4l2_subdev_selection *sel)
> > > >
> > > >  {
> > > >
> > > > +     struct imx290 *imx290 = to_imx290(sd);
> > > >
> > > >       struct v4l2_mbus_framefmt *format;
> > > >
> > > >       switch (sel->target) {
> > > >
> > > > @@ -1122,9 +1151,11 @@ static int imx290_get_selection(struct
> > > > v4l2_subdev
> > > > *sd, format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> > > >
> > > >               sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
> > > >
> > > > -                        + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT -
> > >
> > > format->height) / 2;
> > >
> > > > +                        + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT -
> > >
> > > format->height) / 2
> > >
> > > > +                        + imx290->vflip->val;
> > > >
> > > >               sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT
> > > >
> > > > -                         + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH -
> > >
> > > format->width) / 2;
> > >
> > > > +                         + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH -
> > >
> > > format->width) / 2
> > >
> > > > +                         + imx290->hflip->val;
> > > >
> > > >               sel->r.width = format->width;
> > > >               sel->r.height = format->height;
>
>
>
>

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

* Re: [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips
  2023-02-02 22:10   ` Laurent Pinchart
@ 2023-02-03 10:21     ` Dave Stevenson
  2023-02-03 18:01       ` Laurent Pinchart
  0 siblings, 1 reply; 49+ messages in thread
From: Dave Stevenson @ 2023-02-03 10:21 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Laurent

On Thu, 2 Feb 2023 at 22:10, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Dave,
>
> Thank you for the patch.
>
> On Tue, Jan 31, 2023 at 07:20:16PM +0000, Dave Stevenson wrote:
> > The sensor supports H & V flips, so add the relevant hooks for
> > V4L2_CID_HFLIP and V4L2_CID_VFLIP to configure them.
> >
> > Note that the Bayer order is maintained as the readout area
> > shifts by 1 pixel in the appropriate direction (note the
> > comment about the top margin being 8 pixels whilst the bottom
> > margin is 9).
>
> That's why ! Now it makes sense to me.
>
> > The V4L2_SEL_TGT_CROP region is therefore
> > adjusted appropriately.
>
> I'm not sure I like when sensors try to be clever... Out of curiosity,
> do you know if this automatic shift of the crop rectangle can be
> disabled ?

Not as far as I'm aware.
Most of the OnSemi sensors I've looked at also preserve the Bayer
order on flips, and the datasheets say they're doing the same thing of
shifting by one pixel. I did query if it could be disabled, and they
said no.

I have a suspicion that the situation here is actually worse, but
haven't had a chance to test experimentally.
The datasheet settings for all-pixel mode gives [X|Y]_OUT_SIZE as
1948x1097, but the driver sets them to 1920x1080 (1308x729 for the
1280x720 mode). Which pixels get dropped due to that reduction. My
expectation is that it'll be the right side and bottom edge, not a
centre crop as is currently assumed by the driver in get_selection. If
you flip that, then it'll be the top and left edges that get cropped
off.
If this is the case, then we'll have to switch to using window
cropping mode to request the desired crop. Some people may be happy
with this as it'll give them the information required to configure
VGA@129fps and CIF@205fps modes as mentioned in the datasheet. For now
I'm burying my head in the sand.

> > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > ---
> >  drivers/media/i2c/imx290.c | 37 ++++++++++++++++++++++++++++++++++---
> >  1 file changed, 34 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 7f6746f74040..d2b7534f2c51 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -227,6 +227,8 @@ struct imx290 {
> >       struct v4l2_ctrl *hblank;
> >       struct v4l2_ctrl *vblank;
> >       struct v4l2_ctrl *exposure;
> > +     struct v4l2_ctrl *hflip;
> > +     struct v4l2_ctrl *vflip;
> >  };
> >
> >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > @@ -801,6 +803,24 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> >                                  NULL);
> >               break;
> >
> > +     case V4L2_CID_HFLIP:
> > +     case V4L2_CID_VFLIP:
> > +     {
> > +             u32 reg;
> > +
> > +             /* WINMODE is in bits [6:4], so need to read-modify-write */
>
> You could also cache the value of the register in struct imx290 to avoid
> the read.

We're already using regmap, so cache it there instead of locally?

Or alternatively move it out of the mode register array and into the
struct imx290_mode, then we can use imx290->current_mode->reg_ctrl_07
here. There's no need to set it from imx290_start_streaming as that
calls __v4l2_ctrl_handler_setup which will come through here. That
seems cleanest to me.

> > +             ret = imx290_read(imx290, IMX290_CTRL_07, &reg);
> > +             if (ret)
> > +                     break;
> > +             reg &= ~(IMX290_HREVERSE | IMX290_VREVERSE);
> > +             if (imx290->hflip->val)
> > +                     reg |= IMX290_HREVERSE;
> > +             if (imx290->vflip->val)
> > +                     reg |= IMX290_VREVERSE;
> > +             ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL);
> > +             break;
>
> As you always write those two controls together, they should be put in a
> cluster to have a single call to imx290_set_ctrl() when both are set in
> the same VIDIOC_S_EXT_CTRLS call.

Ah ctrl clusters - another can of worms :-)

 Dave

> > +     }
> > +
> >       default:
> >               ret = -EINVAL;
> >               break;
> > @@ -853,7 +873,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >       if (ret < 0)
> >               return ret;
> >
> > -     v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> > +     v4l2_ctrl_handler_init(&imx290->ctrls, 11);
> >
> >       /*
> >        * The sensor has an analog gain and a digital gain, both controlled
> > @@ -909,6 +929,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> >                                          V4L2_CID_VBLANK, 1, 1, 1, 1);
> >
> > +     imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > +                                       V4L2_CID_HFLIP, 0, 1, 1, 0);
> > +     imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > +                                       V4L2_CID_VFLIP, 0, 1, 1, 0);
> > +
> >       v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
> >                                       &props);
> >
> > @@ -1030,6 +1055,9 @@ static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
> >               pm_runtime_put_autosuspend(imx290->dev);
> >       }
> >
> > +     /* vflip and hflip cannot change during streaming */
> > +     __v4l2_ctrl_grab(imx290->vflip, enable);
> > +     __v4l2_ctrl_grab(imx290->hflip, enable);
>
> A blank line would be nice.
>
> >  unlock:
> >       v4l2_subdev_unlock_state(state);
> >       return ret;
> > @@ -1115,6 +1143,7 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
> >                               struct v4l2_subdev_state *sd_state,
> >                               struct v4l2_subdev_selection *sel)
> >  {
> > +     struct imx290 *imx290 = to_imx290(sd);
> >       struct v4l2_mbus_framefmt *format;
> >
> >       switch (sel->target) {
> > @@ -1122,9 +1151,11 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
> >               format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> >
>
> A comment to explain here why the crop rectangle is adjusted would be
> nice.
>
> >               sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
> > -                        + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2;
> > +                        + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2
> > +                        + imx290->vflip->val;
> >               sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT
> > -                         + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2;
> > +                         + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2
> > +                         + imx290->hflip->val;
> >               sel->r.width = format->width;
> >               sel->r.height = format->height;
> >
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock
  2023-02-02 22:03   ` Laurent Pinchart
@ 2023-02-03 17:04     ` Dave Stevenson
  2023-02-03 18:04       ` Laurent Pinchart
  0 siblings, 1 reply; 49+ messages in thread
From: Dave Stevenson @ 2023-02-03 17:04 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Laurent

On Thu, 2 Feb 2023 at 22:03, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Dave,
>
> Thank you for the patch.
>
> On Tue, Jan 31, 2023 at 07:20:15PM +0000, Dave Stevenson wrote:
> > The sensor supports either a 37.125 or 74.25MHz external, but
> > the driver only supported 37.125MHz.
> >
> > Add the relevant register configuration for either clock
> > frequency option.
> >
> > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > ---
> >  drivers/media/i2c/imx290.c | 120 +++++++++++++++++++++++++++++++------
> >  1 file changed, 103 insertions(+), 17 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 5202ef3cc3e6..7f6746f74040 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -102,6 +102,7 @@
> >  #define IMX290_TCLKPREPARE                           IMX290_REG_16BIT(0x3452)
> >  #define IMX290_TLPX                                  IMX290_REG_16BIT(0x3454)
> >  #define IMX290_X_OUT_SIZE                            IMX290_REG_16BIT(0x3472)
> > +#define IMX290_INCKSEL7                                      IMX290_REG_8BIT(0x3480)
> >
> >  #define IMX290_PGCTRL_REGEN                          BIT(0)
> >  #define IMX290_PGCTRL_THRU                           BIT(1)
> > @@ -159,11 +160,27 @@
> >
> >  #define IMX290_NUM_SUPPLIES                          3
> >
> > +#define CLK_37_125   0
> > +#define CLK_74_25    1
> > +#define NUM_CLK              2
>
> Please add an IMX290 prefer to avoid future namespace clashes.
>
> > +
> >  struct imx290_regval {
> >       u32 reg;
> >       u32 val;
> >  };
> >
> > +/*
> > + * Clock configuration for registers INCKSEL1 to INCKSEL6.
> > + */
> > +struct imx290_clk_cfg {
> > +     u8 incksel1;
> > +     u8 incksel2;
> > +     u8 incksel3;
> > +     u8 incksel4;
> > +     u8 incksel5;
> > +     u8 incksel6;
> > +};
> > +
> >  struct imx290_mode {
> >       u32 width;
> >       u32 height;
> > @@ -173,6 +190,8 @@ struct imx290_mode {
> >
> >       const struct imx290_regval *data;
> >       u32 data_size;
> > +
> > +     const struct imx290_clk_cfg *clk_cfg;
> >  };
> >
> >  struct imx290_csi_cfg {
> > @@ -191,6 +210,7 @@ struct imx290 {
> >       struct device *dev;
> >       struct clk *xclk;
> >       struct regmap *regmap;
> > +     u32 xclk_freq;
> >       u8 nlanes;
> >       u8 mono;
> >
> > @@ -219,7 +239,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> >   */
> >
> >  static const struct imx290_regval imx290_global_init_settings[] = {
> > -     { IMX290_EXTCK_FREQ, 0x2520 },
> >       { IMX290_WINWV_OB, 12 },
> >       { IMX290_WINPH, 0 },
> >       { IMX290_WINPV, 0 },
> > @@ -269,7 +288,16 @@ static const struct imx290_regval imx290_global_init_settings[] = {
> >       { IMX290_REG_8BIT(0x33b0), 0x50 },
> >       { IMX290_REG_8BIT(0x33b2), 0x1a },
> >       { IMX290_REG_8BIT(0x33b3), 0x04 },
> > -     { IMX290_REG_8BIT(0x3480), 0x49 },
>
> One less unnamed register, only 42 to go :-D
>
> > +};
> > +
> > +static const struct imx290_regval imx290_37_125mhz_clock[] = {
> > +     { IMX290_EXTCK_FREQ, 0x2520 },
> > +     { IMX290_INCKSEL7, 0x49 },
> > +};
> > +
> > +static const struct imx290_regval imx290_74_25mhz_clock[] = {
> > +     { IMX290_EXTCK_FREQ, 0x4a40 },
> > +     { IMX290_INCKSEL7, 0x92 },
> >  };
>
> Those two arrays are not used, which I assume is not normal :-) A rebase
> problem maybe ?

Whoops! Seeing as I'd tested at both xclk_freqs it shows how little they do!

> How about moving the INCKSEL7 value to the imx290_clk_cfg structure for
> consistency ?

These two only vary based on xclk_freq, whilst imx290_clk_cfg varies
with mode and xclk. I kept them separate to make that distinction
obvious, rather than duplicating the values between 1080p and 720p
tables.

> >
> >  static const struct imx290_regval imx290_1080p_settings[] = {
> > @@ -279,12 +307,6 @@ static const struct imx290_regval imx290_1080p_settings[] = {
> >       { IMX290_OPB_SIZE_V, 10 },
> >       { IMX290_X_OUT_SIZE, 1920 },
> >       { IMX290_Y_OUT_SIZE, 1080 },
> > -     { IMX290_INCKSEL1, 0x18 },
> > -     { IMX290_INCKSEL2, 0x03 },
> > -     { IMX290_INCKSEL3, 0x20 },
> > -     { IMX290_INCKSEL4, 0x01 },
> > -     { IMX290_INCKSEL5, 0x1a },
> > -     { IMX290_INCKSEL6, 0x1a },
> >  };
> >
> >  static const struct imx290_regval imx290_720p_settings[] = {
> > @@ -294,12 +316,6 @@ static const struct imx290_regval imx290_720p_settings[] = {
> >       { IMX290_OPB_SIZE_V, 4 },
> >       { IMX290_X_OUT_SIZE, 1280 },
> >       { IMX290_Y_OUT_SIZE, 720 },
> > -     { IMX290_INCKSEL1, 0x20 },
> > -     { IMX290_INCKSEL2, 0x00 },
> > -     { IMX290_INCKSEL3, 0x20 },
> > -     { IMX290_INCKSEL4, 0x01 },
> > -     { IMX290_INCKSEL5, 0x1a },
> > -     { IMX290_INCKSEL6, 0x1a },
> >  };
> >
> >  static const struct imx290_regval imx290_10bit_settings[] = {
> > @@ -405,6 +421,48 @@ static inline int imx290_link_freqs_num(const struct imx290 *imx290)
> >               return ARRAY_SIZE(imx290_link_freq_4lanes);
> >  }
> >
> > +static const struct imx290_clk_cfg imx290_1080p_clock_config[NUM_CLK] = {
> > +     [CLK_37_125] = {
> > +             /* 37.125MHz clock config */
> > +             .incksel1 = 0x18,
> > +             .incksel2 = 0x03,
> > +             .incksel3 = 0x20,
> > +             .incksel4 = 0x01,
> > +             .incksel5 = 0x1a,
> > +             .incksel6 = 0x1a,
> > +     },
>
> As the incksel[0-6] fields are only used in one place, to write all 6 of
> them to the device, you could also drop the imx290_clk_cfg structure and
> turn this into a imx290_regval array. Entirely up to you.

If you make them an array of imx290_regval then you also need to
manage the length of those arrays and either maintain that separately
for each one, or try and insert a compile time assert to pick up the
mismatch.
At least a dedicated struct avoids that pitfall.

> > +     [CLK_74_25] = {
> > +             /* 74.25MHz clock config */
> > +             .incksel1 = 0x0c,
> > +             .incksel2 = 0x03,
> > +             .incksel3 = 0x10,
> > +             .incksel4 = 0x01,
> > +             .incksel5 = 0x1b,
> > +             .incksel6 = 0x1b,
> > +     },
> > +};
> > +
> > +static const struct imx290_clk_cfg imx290_720p_clock_config[NUM_CLK] = {
> > +     [CLK_37_125] = {
> > +             /* 37.125MHz clock config */
> > +             .incksel1 = 0x20,
> > +             .incksel2 = 0x00,
> > +             .incksel3 = 0x20,
> > +             .incksel4 = 0x01,
> > +             .incksel5 = 0x1a,
> > +             .incksel6 = 0x1a,
> > +     },
> > +     [CLK_74_25] = {
> > +             /* 74.25MHz clock config */
> > +             .incksel1 = 0x10,
> > +             .incksel2 = 0x00,
> > +             .incksel3 = 0x10,
> > +             .incksel4 = 0x01,
> > +             .incksel5 = 0x1b,
> > +             .incksel6 = 0x1b,
> > +     },
> > +};
> > +
> >  /* Mode configs */
> >  static const struct imx290_mode imx290_modes_2lanes[] = {
> >       {
> > @@ -415,6 +473,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
> >               .link_freq_index = FREQ_INDEX_1080P,
> >               .data = imx290_1080p_settings,
> >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > +             .clk_cfg = imx290_1080p_clock_config,
> >       },
> >       {
> >               .width = 1280,
> > @@ -424,6 +483,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
> >               .link_freq_index = FREQ_INDEX_720P,
> >               .data = imx290_720p_settings,
> >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > +             .clk_cfg = imx290_720p_clock_config,
> >       },
> >  };
> >
> > @@ -436,6 +496,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
> >               .link_freq_index = FREQ_INDEX_1080P,
> >               .data = imx290_1080p_settings,
> >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > +             .clk_cfg = imx290_1080p_clock_config,
> >       },
> >       {
> >               .width = 1280,
> > @@ -445,6 +506,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
> >               .link_freq_index = FREQ_INDEX_720P,
> >               .data = imx290_720p_settings,
> >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > +             .clk_cfg = imx290_720p_clock_config,
> >       },
> >  };
> >
> > @@ -563,6 +625,23 @@ static int imx290_set_register_array(struct imx290 *imx290,
> >       return 0;
> >  }
> >
> > +static int imx290_set_clock(struct imx290 *imx290)
> > +{
> > +     int clk_idx = (imx290->xclk_freq == 37125000) ? 0 : 1;
> > +     const struct imx290_mode *mode = imx290->current_mode;
> > +     const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[clk_idx];
> > +     int ret = 0;
>
> How about turning the clock freq macros into an enum:
>
> enum imx290_clk_freq {
>         IMX290_CLK_37_125 = 0,
>         IMX290_CLK_74_25 = 1,
> };
>
> and replacing in struct imx290
>
> -       u32 xclk_freq;
> +       enum imx290_clk_freq xclk_freq;
>
> ? Then you could could simply write
>
>         const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[imx290->xclk_freq];
>
> (you could also name the field xclk_freq_idx if desired). Up to you, if
> you find that messier you can ignore the comment.

Done. Renamed to xclk_idx to make it clear that it's an index.

  Dave

> > +
> > +     imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret);
> > +     imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret);
> > +     imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret);
> > +     imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret);
> > +     imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret);
> > +     imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret);
> > +
> > +     return ret;
> > +}
> > +
> >  static int imx290_set_data_lanes(struct imx290 *imx290)
> >  {
> >       int ret = 0;
> > @@ -863,6 +942,13 @@ static int imx290_start_streaming(struct imx290 *imx290,
> >               return ret;
> >       }
> >
> > +     /* Set clock parameters based on mode and xclk */
> > +     ret = imx290_set_clock(imx290);
> > +     if (ret < 0) {
> > +             dev_err(imx290->dev, "Could not set clocks\n");
> > +             return ret;
> > +     }
> > +
> >       /* Set data lane count */
> >       ret = imx290_set_data_lanes(imx290);
> >       if (ret < 0) {
> > @@ -1259,14 +1345,14 @@ static int imx290_init_clk(struct imx290 *imx290)
> >       int ret;
> >
> >       ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
> > -                                    "clock-frequency", &xclk_freq);
> > +                                    "clock-frequency", &imx290->xclk_freq);
> >       if (ret) {
> >               dev_err(imx290->dev, "Could not get xclk frequency\n");
> >               return ret;
> >       }
> >
> > -     /* external clock must be 37.125 MHz */
> > -     if (xclk_freq != 37125000) {
> > +     /* external clock must be 37.125 MHz or 74.25MHz */
> > +     if (imx290->xclk_freq != 37125000 && imx290->xclk_freq != 74250000) {
> >               dev_err(imx290->dev, "External clock frequency %u is not supported\n",
> >                       xclk_freq);
> >               return -EINVAL;
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips
  2023-02-03 10:21     ` Dave Stevenson
@ 2023-02-03 18:01       ` Laurent Pinchart
  0 siblings, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-03 18:01 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

On Fri, Feb 03, 2023 at 10:21:28AM +0000, Dave Stevenson wrote:
> On Thu, 2 Feb 2023 at 22:10, Laurent Pinchart wrote:
> > On Tue, Jan 31, 2023 at 07:20:16PM +0000, Dave Stevenson wrote:
> > > The sensor supports H & V flips, so add the relevant hooks for
> > > V4L2_CID_HFLIP and V4L2_CID_VFLIP to configure them.
> > >
> > > Note that the Bayer order is maintained as the readout area
> > > shifts by 1 pixel in the appropriate direction (note the
> > > comment about the top margin being 8 pixels whilst the bottom
> > > margin is 9).
> >
> > That's why ! Now it makes sense to me.
> >
> > > The V4L2_SEL_TGT_CROP region is therefore
> > > adjusted appropriately.
> >
> > I'm not sure I like when sensors try to be clever... Out of curiosity,
> > do you know if this automatic shift of the crop rectangle can be
> > disabled ?
> 
> Not as far as I'm aware.
> Most of the OnSemi sensors I've looked at also preserve the Bayer
> order on flips, and the datasheets say they're doing the same thing of
> shifting by one pixel. I did query if it could be disabled, and they
> said no.
> 
> I have a suspicion that the situation here is actually worse, but
> haven't had a chance to test experimentally.
> The datasheet settings for all-pixel mode gives [X|Y]_OUT_SIZE as
> 1948x1097, but the driver sets them to 1920x1080 (1308x729 for the
> 1280x720 mode). Which pixels get dropped due to that reduction. My
> expectation is that it'll be the right side and bottom edge, not a
> centre crop as is currently assumed by the driver in get_selection. If
> you flip that, then it'll be the top and left edges that get cropped
> off.

I wonder if anybody would ever notice.

> If this is the case, then we'll have to switch to using window
> cropping mode to request the desired crop. Some people may be happy
> with this as it'll give them the information required to configure
> VGA@129fps and CIF@205fps modes as mentioned in the datasheet. For now
> I'm burying my head in the sand.

Thank you for the information. Is there any sand left to burry my head
as well ?

> > > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > > ---
> > >  drivers/media/i2c/imx290.c | 37 ++++++++++++++++++++++++++++++++++---
> > >  1 file changed, 34 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index 7f6746f74040..d2b7534f2c51 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -227,6 +227,8 @@ struct imx290 {
> > >       struct v4l2_ctrl *hblank;
> > >       struct v4l2_ctrl *vblank;
> > >       struct v4l2_ctrl *exposure;
> > > +     struct v4l2_ctrl *hflip;
> > > +     struct v4l2_ctrl *vflip;
> > >  };
> > >
> > >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > > @@ -801,6 +803,24 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > >                                  NULL);
> > >               break;
> > >
> > > +     case V4L2_CID_HFLIP:
> > > +     case V4L2_CID_VFLIP:
> > > +     {
> > > +             u32 reg;
> > > +
> > > +             /* WINMODE is in bits [6:4], so need to read-modify-write */
> >
> > You could also cache the value of the register in struct imx290 to avoid
> > the read.
> 
> We're already using regmap, so cache it there instead of locally?

Ah yes, it can be done in regmap.

> Or alternatively move it out of the mode register array and into the
> struct imx290_mode, then we can use imx290->current_mode->reg_ctrl_07
> here. There's no need to set it from imx290_start_streaming as that
> calls __v4l2_ctrl_handler_setup which will come through here. That
> seems cleanest to me.

Works for me too.

> > > +             ret = imx290_read(imx290, IMX290_CTRL_07, &reg);
> > > +             if (ret)
> > > +                     break;
> > > +             reg &= ~(IMX290_HREVERSE | IMX290_VREVERSE);
> > > +             if (imx290->hflip->val)
> > > +                     reg |= IMX290_HREVERSE;
> > > +             if (imx290->vflip->val)
> > > +                     reg |= IMX290_VREVERSE;
> > > +             ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL);
> > > +             break;
> >
> > As you always write those two controls together, they should be put in a
> > cluster to have a single call to imx290_set_ctrl() when both are set in
> > the same VIDIOC_S_EXT_CTRLS call.
> 
> Ah ctrl clusters - another can of worms :-)

With a bit of luck it will only be one extra function call at init time
and all will be fine :-)

> > > +     }
> > > +
> > >       default:
> > >               ret = -EINVAL;
> > >               break;
> > > @@ -853,7 +873,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > >       if (ret < 0)
> > >               return ret;
> > >
> > > -     v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> > > +     v4l2_ctrl_handler_init(&imx290->ctrls, 11);
> > >
> > >       /*
> > >        * The sensor has an analog gain and a digital gain, both controlled
> > > @@ -909,6 +929,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > >       imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > >                                          V4L2_CID_VBLANK, 1, 1, 1, 1);
> > >
> > > +     imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > > +                                       V4L2_CID_HFLIP, 0, 1, 1, 0);
> > > +     imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> > > +                                       V4L2_CID_VFLIP, 0, 1, 1, 0);
> > > +
> > >       v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
> > >                                       &props);
> > >
> > > @@ -1030,6 +1055,9 @@ static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
> > >               pm_runtime_put_autosuspend(imx290->dev);
> > >       }
> > >
> > > +     /* vflip and hflip cannot change during streaming */
> > > +     __v4l2_ctrl_grab(imx290->vflip, enable);
> > > +     __v4l2_ctrl_grab(imx290->hflip, enable);
> >
> > A blank line would be nice.
> >
> > >  unlock:
> > >       v4l2_subdev_unlock_state(state);
> > >       return ret;
> > > @@ -1115,6 +1143,7 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
> > >                               struct v4l2_subdev_state *sd_state,
> > >                               struct v4l2_subdev_selection *sel)
> > >  {
> > > +     struct imx290 *imx290 = to_imx290(sd);
> > >       struct v4l2_mbus_framefmt *format;
> > >
> > >       switch (sel->target) {
> > > @@ -1122,9 +1151,11 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
> > >               format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> > >
> >
> > A comment to explain here why the crop rectangle is adjusted would be
> > nice.
> >
> > >               sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP
> > > -                        + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2;
> > > +                        + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2
> > > +                        + imx290->vflip->val;
> > >               sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT
> > > -                         + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2;
> > > +                         + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2
> > > +                         + imx290->hflip->val;
> > >               sel->r.width = format->width;
> > >               sel->r.height = format->height;
> > >

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock
  2023-02-03 17:04     ` Dave Stevenson
@ 2023-02-03 18:04       ` Laurent Pinchart
  0 siblings, 0 replies; 49+ messages in thread
From: Laurent Pinchart @ 2023-02-03 18:04 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: Manivannan Sadhasivam, Mauro Carvalho Chehab, linux-media

Hi Dave,

On Fri, Feb 03, 2023 at 05:04:13PM +0000, Dave Stevenson wrote:
> On Thu, 2 Feb 2023 at 22:03, Laurent Pinchart wrote:
> > On Tue, Jan 31, 2023 at 07:20:15PM +0000, Dave Stevenson wrote:
> > > The sensor supports either a 37.125 or 74.25MHz external, but
> > > the driver only supported 37.125MHz.
> > >
> > > Add the relevant register configuration for either clock
> > > frequency option.
> > >
> > > Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > > ---
> > >  drivers/media/i2c/imx290.c | 120 +++++++++++++++++++++++++++++++------
> > >  1 file changed, 103 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index 5202ef3cc3e6..7f6746f74040 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -102,6 +102,7 @@
> > >  #define IMX290_TCLKPREPARE                           IMX290_REG_16BIT(0x3452)
> > >  #define IMX290_TLPX                                  IMX290_REG_16BIT(0x3454)
> > >  #define IMX290_X_OUT_SIZE                            IMX290_REG_16BIT(0x3472)
> > > +#define IMX290_INCKSEL7                                      IMX290_REG_8BIT(0x3480)
> > >
> > >  #define IMX290_PGCTRL_REGEN                          BIT(0)
> > >  #define IMX290_PGCTRL_THRU                           BIT(1)
> > > @@ -159,11 +160,27 @@
> > >
> > >  #define IMX290_NUM_SUPPLIES                          3
> > >
> > > +#define CLK_37_125   0
> > > +#define CLK_74_25    1
> > > +#define NUM_CLK              2
> >
> > Please add an IMX290 prefer to avoid future namespace clashes.
> >
> > > +
> > >  struct imx290_regval {
> > >       u32 reg;
> > >       u32 val;
> > >  };
> > >
> > > +/*
> > > + * Clock configuration for registers INCKSEL1 to INCKSEL6.
> > > + */
> > > +struct imx290_clk_cfg {
> > > +     u8 incksel1;
> > > +     u8 incksel2;
> > > +     u8 incksel3;
> > > +     u8 incksel4;
> > > +     u8 incksel5;
> > > +     u8 incksel6;
> > > +};
> > > +
> > >  struct imx290_mode {
> > >       u32 width;
> > >       u32 height;
> > > @@ -173,6 +190,8 @@ struct imx290_mode {
> > >
> > >       const struct imx290_regval *data;
> > >       u32 data_size;
> > > +
> > > +     const struct imx290_clk_cfg *clk_cfg;
> > >  };
> > >
> > >  struct imx290_csi_cfg {
> > > @@ -191,6 +210,7 @@ struct imx290 {
> > >       struct device *dev;
> > >       struct clk *xclk;
> > >       struct regmap *regmap;
> > > +     u32 xclk_freq;
> > >       u8 nlanes;
> > >       u8 mono;
> > >
> > > @@ -219,7 +239,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > >   */
> > >
> > >  static const struct imx290_regval imx290_global_init_settings[] = {
> > > -     { IMX290_EXTCK_FREQ, 0x2520 },
> > >       { IMX290_WINWV_OB, 12 },
> > >       { IMX290_WINPH, 0 },
> > >       { IMX290_WINPV, 0 },
> > > @@ -269,7 +288,16 @@ static const struct imx290_regval imx290_global_init_settings[] = {
> > >       { IMX290_REG_8BIT(0x33b0), 0x50 },
> > >       { IMX290_REG_8BIT(0x33b2), 0x1a },
> > >       { IMX290_REG_8BIT(0x33b3), 0x04 },
> > > -     { IMX290_REG_8BIT(0x3480), 0x49 },
> >
> > One less unnamed register, only 42 to go :-D
> >
> > > +};
> > > +
> > > +static const struct imx290_regval imx290_37_125mhz_clock[] = {
> > > +     { IMX290_EXTCK_FREQ, 0x2520 },
> > > +     { IMX290_INCKSEL7, 0x49 },
> > > +};
> > > +
> > > +static const struct imx290_regval imx290_74_25mhz_clock[] = {
> > > +     { IMX290_EXTCK_FREQ, 0x4a40 },
> > > +     { IMX290_INCKSEL7, 0x92 },
> > >  };
> >
> > Those two arrays are not used, which I assume is not normal :-) A rebase
> > problem maybe ?
> 
> Whoops! Seeing as I'd tested at both xclk_freqs it shows how little they do!

I was wondering about that :-)

> > How about moving the INCKSEL7 value to the imx290_clk_cfg structure for
> > consistency ?
> 
> These two only vary based on xclk_freq, whilst imx290_clk_cfg varies
> with mode and xclk. I kept them separate to make that distinction
> obvious, rather than duplicating the values between 1080p and 720p
> tables.

incksel3, incksel5 and incksel6 also vary based on the frequency. Up to
you.

> > >  static const struct imx290_regval imx290_1080p_settings[] = {
> > > @@ -279,12 +307,6 @@ static const struct imx290_regval imx290_1080p_settings[] = {
> > >       { IMX290_OPB_SIZE_V, 10 },
> > >       { IMX290_X_OUT_SIZE, 1920 },
> > >       { IMX290_Y_OUT_SIZE, 1080 },
> > > -     { IMX290_INCKSEL1, 0x18 },
> > > -     { IMX290_INCKSEL2, 0x03 },
> > > -     { IMX290_INCKSEL3, 0x20 },
> > > -     { IMX290_INCKSEL4, 0x01 },
> > > -     { IMX290_INCKSEL5, 0x1a },
> > > -     { IMX290_INCKSEL6, 0x1a },
> > >  };
> > >
> > >  static const struct imx290_regval imx290_720p_settings[] = {
> > > @@ -294,12 +316,6 @@ static const struct imx290_regval imx290_720p_settings[] = {
> > >       { IMX290_OPB_SIZE_V, 4 },
> > >       { IMX290_X_OUT_SIZE, 1280 },
> > >       { IMX290_Y_OUT_SIZE, 720 },
> > > -     { IMX290_INCKSEL1, 0x20 },
> > > -     { IMX290_INCKSEL2, 0x00 },
> > > -     { IMX290_INCKSEL3, 0x20 },
> > > -     { IMX290_INCKSEL4, 0x01 },
> > > -     { IMX290_INCKSEL5, 0x1a },
> > > -     { IMX290_INCKSEL6, 0x1a },
> > >  };
> > >
> > >  static const struct imx290_regval imx290_10bit_settings[] = {
> > > @@ -405,6 +421,48 @@ static inline int imx290_link_freqs_num(const struct imx290 *imx290)
> > >               return ARRAY_SIZE(imx290_link_freq_4lanes);
> > >  }
> > >
> > > +static const struct imx290_clk_cfg imx290_1080p_clock_config[NUM_CLK] = {
> > > +     [CLK_37_125] = {
> > > +             /* 37.125MHz clock config */
> > > +             .incksel1 = 0x18,
> > > +             .incksel2 = 0x03,
> > > +             .incksel3 = 0x20,
> > > +             .incksel4 = 0x01,
> > > +             .incksel5 = 0x1a,
> > > +             .incksel6 = 0x1a,
> > > +     },
> >
> > As the incksel[0-6] fields are only used in one place, to write all 6 of
> > them to the device, you could also drop the imx290_clk_cfg structure and
> > turn this into a imx290_regval array. Entirely up to you.
> 
> If you make them an array of imx290_regval then you also need to
> manage the length of those arrays and either maintain that separately
> for each one, or try and insert a compile time assert to pick up the
> mismatch.
> At least a dedicated struct avoids that pitfall.

The array is the same for all of them, so we could define it in a macro,
but I agree a structure is a bit more future-proof. Up to you.

> > > +     [CLK_74_25] = {
> > > +             /* 74.25MHz clock config */
> > > +             .incksel1 = 0x0c,
> > > +             .incksel2 = 0x03,
> > > +             .incksel3 = 0x10,
> > > +             .incksel4 = 0x01,
> > > +             .incksel5 = 0x1b,
> > > +             .incksel6 = 0x1b,
> > > +     },
> > > +};
> > > +
> > > +static const struct imx290_clk_cfg imx290_720p_clock_config[NUM_CLK] = {
> > > +     [CLK_37_125] = {
> > > +             /* 37.125MHz clock config */
> > > +             .incksel1 = 0x20,
> > > +             .incksel2 = 0x00,
> > > +             .incksel3 = 0x20,
> > > +             .incksel4 = 0x01,
> > > +             .incksel5 = 0x1a,
> > > +             .incksel6 = 0x1a,
> > > +     },
> > > +     [CLK_74_25] = {
> > > +             /* 74.25MHz clock config */
> > > +             .incksel1 = 0x10,
> > > +             .incksel2 = 0x00,
> > > +             .incksel3 = 0x10,
> > > +             .incksel4 = 0x01,
> > > +             .incksel5 = 0x1b,
> > > +             .incksel6 = 0x1b,
> > > +     },
> > > +};
> > > +
> > >  /* Mode configs */
> > >  static const struct imx290_mode imx290_modes_2lanes[] = {
> > >       {
> > > @@ -415,6 +473,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
> > >               .link_freq_index = FREQ_INDEX_1080P,
> > >               .data = imx290_1080p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > +             .clk_cfg = imx290_1080p_clock_config,
> > >       },
> > >       {
> > >               .width = 1280,
> > > @@ -424,6 +483,7 @@ static const struct imx290_mode imx290_modes_2lanes[] = {
> > >               .link_freq_index = FREQ_INDEX_720P,
> > >               .data = imx290_720p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > +             .clk_cfg = imx290_720p_clock_config,
> > >       },
> > >  };
> > >
> > > @@ -436,6 +496,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
> > >               .link_freq_index = FREQ_INDEX_1080P,
> > >               .data = imx290_1080p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_1080p_settings),
> > > +             .clk_cfg = imx290_1080p_clock_config,
> > >       },
> > >       {
> > >               .width = 1280,
> > > @@ -445,6 +506,7 @@ static const struct imx290_mode imx290_modes_4lanes[] = {
> > >               .link_freq_index = FREQ_INDEX_720P,
> > >               .data = imx290_720p_settings,
> > >               .data_size = ARRAY_SIZE(imx290_720p_settings),
> > > +             .clk_cfg = imx290_720p_clock_config,
> > >       },
> > >  };
> > >
> > > @@ -563,6 +625,23 @@ static int imx290_set_register_array(struct imx290 *imx290,
> > >       return 0;
> > >  }
> > >
> > > +static int imx290_set_clock(struct imx290 *imx290)
> > > +{
> > > +     int clk_idx = (imx290->xclk_freq == 37125000) ? 0 : 1;
> > > +     const struct imx290_mode *mode = imx290->current_mode;
> > > +     const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[clk_idx];
> > > +     int ret = 0;
> >
> > How about turning the clock freq macros into an enum:
> >
> > enum imx290_clk_freq {
> >         IMX290_CLK_37_125 = 0,
> >         IMX290_CLK_74_25 = 1,
> > };
> >
> > and replacing in struct imx290
> >
> > -       u32 xclk_freq;
> > +       enum imx290_clk_freq xclk_freq;
> >
> > ? Then you could could simply write
> >
> >         const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[imx290->xclk_freq];
> >
> > (you could also name the field xclk_freq_idx if desired). Up to you, if
> > you find that messier you can ignore the comment.
> 
> Done. Renamed to xclk_idx to make it clear that it's an index.
> 
> > > +
> > > +     imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret);
> > > +     imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret);
> > > +     imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret);
> > > +     imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret);
> > > +     imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret);
> > > +     imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret);
> > > +
> > > +     return ret;
> > > +}
> > > +
> > >  static int imx290_set_data_lanes(struct imx290 *imx290)
> > >  {
> > >       int ret = 0;
> > > @@ -863,6 +942,13 @@ static int imx290_start_streaming(struct imx290 *imx290,
> > >               return ret;
> > >       }
> > >
> > > +     /* Set clock parameters based on mode and xclk */
> > > +     ret = imx290_set_clock(imx290);
> > > +     if (ret < 0) {
> > > +             dev_err(imx290->dev, "Could not set clocks\n");
> > > +             return ret;
> > > +     }
> > > +
> > >       /* Set data lane count */
> > >       ret = imx290_set_data_lanes(imx290);
> > >       if (ret < 0) {
> > > @@ -1259,14 +1345,14 @@ static int imx290_init_clk(struct imx290 *imx290)
> > >       int ret;
> > >
> > >       ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
> > > -                                    "clock-frequency", &xclk_freq);
> > > +                                    "clock-frequency", &imx290->xclk_freq);
> > >       if (ret) {
> > >               dev_err(imx290->dev, "Could not get xclk frequency\n");
> > >               return ret;
> > >       }
> > >
> > > -     /* external clock must be 37.125 MHz */
> > > -     if (xclk_freq != 37125000) {
> > > +     /* external clock must be 37.125 MHz or 74.25MHz */
> > > +     if (imx290->xclk_freq != 37125000 && imx290->xclk_freq != 74250000) {
> > >               dev_err(imx290->dev, "External clock frequency %u is not supported\n",
> > >                       xclk_freq);
> > >               return -EINVAL;

-- 
Regards,

Laurent Pinchart

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

end of thread, other threads:[~2023-02-03 18:05 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-31 19:20 [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Dave Stevenson
2023-01-31 19:20 ` [PATCH 01/11] media: i2c: imx290: Match kernel coding style on whitespace Dave Stevenson
2023-02-02  0:21   ` Laurent Pinchart
2023-01-31 19:20 ` [PATCH 02/11] media: i2c: imx290: Set the colorspace fields in the format Dave Stevenson
2023-02-02  0:29   ` Laurent Pinchart
2023-01-31 19:20 ` [PATCH 03/11] media: i2c: imx290: Add V4L2_SUBDEV_FL_HAS_EVENTS and subscribe hooks Dave Stevenson
2023-02-02  0:29   ` Laurent Pinchart
2023-02-03  8:54   ` Alexander Stein
2023-01-31 19:20 ` [PATCH 04/11] media: i2c: imx290: Fix the pixel rate at 148.5Mpix/s Dave Stevenson
2023-02-02  8:13   ` Laurent Pinchart
2023-01-31 19:20 ` [PATCH 05/11] media: i2c: imx290: Support 60fps in 2 lane operation Dave Stevenson
2023-02-02 10:50   ` Laurent Pinchart
2023-02-03  8:50   ` Alexander Stein
2023-01-31 19:20 ` [PATCH 06/11] media: i2c: imx290: Use CSI timings as per datasheet Dave Stevenson
2023-02-02 14:36   ` Laurent Pinchart
2023-02-03  8:04   ` Alexander Stein
2023-02-03  9:09     ` Dave Stevenson
2023-01-31 19:20 ` [PATCH 07/11] media: i2c: imx290: Convert V4L2_CID_HBLANK to read/write Dave Stevenson
2023-02-02 14:58   ` Laurent Pinchart
2023-02-02 15:48     ` Dave Stevenson
2023-02-02 22:14       ` Laurent Pinchart
2023-02-03  7:19   ` Alexander Stein
2023-02-03  8:05     ` Dave Stevenson
2023-02-03  8:39       ` Alexander Stein
2023-01-31 19:20 ` [PATCH 08/11] media: i2c: imx290: Convert V4L2_CID_VBLANK " Dave Stevenson
2023-02-02 15:40   ` Alexander Stein
2023-02-02 16:04     ` Dave Stevenson
2023-02-02 17:42       ` Dave Stevenson
2023-02-03  7:16         ` Alexander Stein
2023-02-02 16:01   ` Laurent Pinchart
2023-01-31 19:20 ` [PATCH 09/11] media: i2c: imx290: Remove duplicated write to IMX290_CTRL_07 Dave Stevenson
2023-02-02 16:04   ` Laurent Pinchart
2023-02-03  7:45   ` Alexander Stein
2023-01-31 19:20 ` [PATCH 10/11] media: i2c: imx290: Add support for 74.25MHz external clock Dave Stevenson
2023-02-02 22:03   ` Laurent Pinchart
2023-02-03 17:04     ` Dave Stevenson
2023-02-03 18:04       ` Laurent Pinchart
2023-02-03  7:44   ` Alexander Stein
2023-02-03  8:59     ` Laurent Pinchart
2023-02-03  9:30       ` Dave Stevenson
2023-01-31 19:20 ` [PATCH 11/11] media: i2c: imx290: Add support for H & V Flips Dave Stevenson
2023-02-02 22:10   ` Laurent Pinchart
2023-02-03 10:21     ` Dave Stevenson
2023-02-03 18:01       ` Laurent Pinchart
2023-02-03  7:35   ` Alexander Stein
2023-02-03  7:57     ` Dave Stevenson
2023-02-03  8:33       ` Alexander Stein
2023-02-03  9:47         ` Dave Stevenson
2023-02-02 22:16 ` [PATCH 00/11] imx290: Minor fixes, support for alternate INCK, and more ctrls Laurent Pinchart

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.