All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements
@ 2022-11-22 22:32 Laurent Pinchart
  2022-11-22 22:32 ` [PATCH v1 01/15] media: i2c: imx290: Group functions in sections Laurent Pinchart
                   ` (14 more replies)
  0 siblings, 15 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

Hello,

This patch series contains miscellaneous cleanups and improvements for
the imx290 driver. The most notable changes are usage of the V4L2 subdev
active state (11/15) and runtime PM autosuspend (13/15). Please see
invididual patches for details.

Laurent Pinchart (15):
  media: i2c: imx290: Group functions in sections
  media: i2c: imx290: Factor out subdev init and cleanup to functions
  media: i2c: imx290: Factor out control update code to a function
  media: i2c: imx290: Access link_freq_index directly
  media: i2c: imx290: Pass format and mode to imx290_calc_pixel_rate()
  media: i2c: imx290: Compute pixel rate and blanking in one place
  media: i2c: imx290: Factor out black level setting to a function
  media: i2c: imx290: Factor out DT parsing to separate function
  media: i2c: imx290: Use dev_err_probe()
  media: i2c: imx290: Factor out clock initialization to separate
    function
  media: i2c: imx290: Use V4L2 subdev active state
  media: i2c: imx290: Rename, extend and expand usage of imx290_pixfmt
  media: i2c: imx290: Use runtime PM autosuspend
  media: i2c: imx290: Configure data lanes at start time
  media: i2c: imx290: Simplify imx290_set_data_lanes()

 drivers/media/i2c/imx290.c | 1007 +++++++++++++++++++-----------------
 1 file changed, 523 insertions(+), 484 deletions(-)

-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 01/15] media: i2c: imx290: Group functions in sections
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  7:46   ` Alexander Stein
  2022-11-22 22:32 ` [PATCH v1 02/15] media: i2c: imx290: Factor out subdev init and cleanup to functions Laurent Pinchart
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

Move functions around to group them in logical sections, in order to
improve readability. As a result, the IMX290_NUM_SUPPLIES macro has to
be changed. No other code change is included, only moves.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 626 +++++++++++++++++++------------------
 1 file changed, 325 insertions(+), 301 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 9954ad7376f7..aae4f51941a1 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -152,13 +152,7 @@
 #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH		1920
 #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT		1080
 
-static const char * const imx290_supply_name[] = {
-	"vdda",
-	"vddd",
-	"vdddo",
-};
-
-#define IMX290_NUM_SUPPLIES ARRAY_SIZE(imx290_supply_name)
+#define IMX290_NUM_SUPPLIES				3
 
 struct imx290_regval {
 	u32 reg;
@@ -199,31 +193,14 @@ struct imx290 {
 	struct mutex lock;
 };
 
-struct imx290_pixfmt {
-	u32 code;
-	u8 bpp;
-};
+static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
+{
+	return container_of(_sd, struct imx290, sd);
+}
 
-static const struct imx290_pixfmt imx290_formats[] = {
-	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
-	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
-};
-
-static const struct regmap_config imx290_regmap_config = {
-	.reg_bits = 16,
-	.val_bits = 8,
-};
-
-static const char * const imx290_test_pattern_menu[] = {
-	"Disabled",
-	"Sequence Pattern 1",
-	"Horizontal Color-bar Chart",
-	"Vertical Color-bar Chart",
-	"Sequence Pattern 2",
-	"Gradation Pattern 1",
-	"Gradation Pattern 2",
-	"000/555h Toggle Pattern",
-};
+/* -----------------------------------------------------------------------------
+ * Modes and formats
+ */
 
 static const struct imx290_regval imx290_global_init_settings[] = {
 	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
@@ -438,10 +415,19 @@ static inline int imx290_modes_num(const struct imx290 *imx290)
 		return ARRAY_SIZE(imx290_modes_4lanes);
 }
 
-static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
-{
-	return container_of(_sd, struct imx290, sd);
-}
+struct imx290_pixfmt {
+	u32 code;
+	u8 bpp;
+};
+
+static const struct imx290_pixfmt imx290_formats[] = {
+	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
+	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
+};
+
+/* -----------------------------------------------------------------------------
+ * Register access
+ */
 
 static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *value)
 {
@@ -501,18 +487,94 @@ static int imx290_set_register_array(struct imx290 *imx290,
 	return 0;
 }
 
-/* Stop streaming */
-static int imx290_stop_streaming(struct imx290 *imx290)
+static int imx290_set_data_lanes(struct imx290 *imx290)
 {
-	int ret = 0;
+	int ret = 0, laneval, frsel;
 
-	imx290_write(imx290, IMX290_STANDBY, 0x01, &ret);
+	switch (imx290->nlanes) {
+	case 2:
+		laneval = 0x01;
+		frsel = 0x02;
+		break;
+	case 4:
+		laneval = 0x03;
+		frsel = 0x01;
+		break;
+	default:
+		/*
+		 * We should never hit this since the data lane count is
+		 * validated in probe itself
+		 */
+		dev_err(imx290->dev, "Lane configuration not supported\n");
+		return -EINVAL;
+	}
 
-	msleep(30);
+	imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
+	imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
+	imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
 
-	return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret);
+	return ret;
 }
 
+static int imx290_write_current_format(struct imx290 *imx290)
+{
+	int ret;
+
+	switch (imx290->current_format.code) {
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+		ret = imx290_set_register_array(imx290, imx290_10bit_settings,
+						ARRAY_SIZE(
+							imx290_10bit_settings));
+		if (ret < 0) {
+			dev_err(imx290->dev, "Could not set format registers\n");
+			return ret;
+		}
+		break;
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+		ret = imx290_set_register_array(imx290, imx290_12bit_settings,
+						ARRAY_SIZE(
+							imx290_12bit_settings));
+		if (ret < 0) {
+			dev_err(imx290->dev, "Could not set format registers\n");
+			return ret;
+		}
+		break;
+	default:
+		dev_err(imx290->dev, "Unknown pixel format\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline u8 imx290_get_link_freq_index(struct imx290 *imx290)
+{
+	return imx290->current_mode->link_freq_index;
+}
+
+static s64 imx290_get_link_freq(struct imx290 *imx290)
+{
+	u8 index = imx290_get_link_freq_index(imx290);
+
+	return *(imx290_link_freqs_ptr(imx290) + index);
+}
+
+static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
+{
+	s64 link_freq = imx290_get_link_freq(imx290);
+	u8 nlanes = imx290->nlanes;
+	u64 pixel_rate;
+
+	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
+	pixel_rate = link_freq * 2 * nlanes;
+	do_div(pixel_rate, imx290->bpp);
+	return pixel_rate;
+}
+
+/* ----------------------------------------------------------------------------
+ * Controls
+ */
+
 static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct imx290 *imx290 = container_of(ctrl->handler,
@@ -566,6 +628,187 @@ static const struct v4l2_ctrl_ops imx290_ctrl_ops = {
 	.s_ctrl = imx290_set_ctrl,
 };
 
+static const char * const imx290_test_pattern_menu[] = {
+	"Disabled",
+	"Sequence Pattern 1",
+	"Horizontal Color-bar Chart",
+	"Vertical Color-bar Chart",
+	"Sequence Pattern 2",
+	"Gradation Pattern 1",
+	"Gradation Pattern 2",
+	"000/555h Toggle Pattern",
+};
+
+static int imx290_ctrl_init(struct imx290 *imx290)
+{
+	struct v4l2_fwnode_device_properties props;
+	unsigned int blank;
+	int ret;
+
+	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
+	if (ret < 0)
+		return ret;
+
+	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
+	imx290->ctrls.lock = &imx290->lock;
+
+	/*
+	 * The sensor has an analog gain and a digital gain, both controlled
+	 * through a single gain value, expressed in 0.3dB increments. Values
+	 * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
+	 * up to 72.0dB (240) add further digital gain. Limit the range to
+	 * analog gain only, support for digital gain can be added separately
+	 * if needed.
+	 *
+	 * The IMX327 and IMX462 are largely compatible with the IMX290, but
+	 * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital
+	 * gain. When support for those sensors gets added to the driver, the
+	 * gain control should be adjusted accordingly.
+	 */
+	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);
+
+	imx290->link_freq =
+		v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
+				       V4L2_CID_LINK_FREQ,
+				       imx290_link_freqs_num(imx290) - 1, 0,
+				       imx290_link_freqs_ptr(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,
+					       imx290_calc_pixel_rate(imx290));
+
+	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
+				     V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(imx290_test_pattern_menu) - 1,
+				     0, 0, imx290_test_pattern_menu);
+
+	blank = imx290->current_mode->hmax - imx290->current_mode->width;
+	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					   V4L2_CID_HBLANK, blank, blank, 1,
+					   blank);
+	if (imx290->hblank)
+		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
+	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					   V4L2_CID_VBLANK, blank, blank, 1,
+					   blank);
+	if (imx290->vblank)
+		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
+					&props);
+
+	imx290->sd.ctrl_handler = &imx290->ctrls;
+
+	if (imx290->ctrls.error) {
+		ret = imx290->ctrls.error;
+		v4l2_ctrl_handler_free(&imx290->ctrls);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* ----------------------------------------------------------------------------
+ * Subdev operations
+ */
+
+/* Start streaming */
+static int imx290_start_streaming(struct imx290 *imx290)
+{
+	int ret;
+
+	/* Set init register settings */
+	ret = imx290_set_register_array(imx290, 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;
+	}
+
+	/* Apply the register values related to current frame format */
+	ret = imx290_write_current_format(imx290);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Could not set frame format\n");
+		return ret;
+	}
+
+	/* Apply default values of current mode */
+	ret = imx290_set_register_array(imx290, imx290->current_mode->data,
+					imx290->current_mode->data_size);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Could not set current mode\n");
+		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) {
+		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
+		return ret;
+	}
+
+	imx290_write(imx290, IMX290_STANDBY, 0x00, &ret);
+
+	msleep(30);
+
+	/* Start streaming */
+	return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret);
+}
+
+/* Stop streaming */
+static int imx290_stop_streaming(struct imx290 *imx290)
+{
+	int ret = 0;
+
+	imx290_write(imx290, IMX290_STANDBY, 0x01, &ret);
+
+	msleep(30);
+
+	return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret);
+}
+
+static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct imx290 *imx290 = to_imx290(sd);
+	int ret = 0;
+
+	if (enable) {
+		ret = pm_runtime_resume_and_get(imx290->dev);
+		if (ret < 0)
+			goto unlock_and_return;
+
+		ret = imx290_start_streaming(imx290);
+		if (ret) {
+			dev_err(imx290->dev, "Start stream failed\n");
+			pm_runtime_put(imx290->dev);
+			goto unlock_and_return;
+		}
+	} else {
+		imx290_stop_streaming(imx290);
+		pm_runtime_put(imx290->dev);
+	}
+
+unlock_and_return:
+
+	return ret;
+}
+
 static struct v4l2_mbus_framefmt *
 imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state *state,
 		      u32 which)
@@ -627,30 +870,6 @@ static int imx290_get_fmt(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static inline u8 imx290_get_link_freq_index(struct imx290 *imx290)
-{
-	return imx290->current_mode->link_freq_index;
-}
-
-static s64 imx290_get_link_freq(struct imx290 *imx290)
-{
-	u8 index = imx290_get_link_freq_index(imx290);
-
-	return *(imx290_link_freqs_ptr(imx290) + index);
-}
-
-static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
-{
-	s64 link_freq = imx290_get_link_freq(imx290);
-	u8 nlanes = imx290->nlanes;
-	u64 pixel_rate;
-
-	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
-	pixel_rate = link_freq * 2 * nlanes;
-	do_div(pixel_rate, imx290->bpp);
-	return pixel_rate;
-}
-
 static int imx290_set_fmt(struct v4l2_subdev *sd,
 			  struct v4l2_subdev_state *sd_state,
 			  struct v4l2_subdev_format *fmt)
@@ -774,151 +993,31 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
 	return 0;
 }
 
-static int imx290_write_current_format(struct imx290 *imx290)
-{
-	int ret;
-
-	switch (imx290->current_format.code) {
-	case MEDIA_BUS_FMT_SRGGB10_1X10:
-		ret = imx290_set_register_array(imx290, imx290_10bit_settings,
-						ARRAY_SIZE(
-							imx290_10bit_settings));
-		if (ret < 0) {
-			dev_err(imx290->dev, "Could not set format registers\n");
-			return ret;
-		}
-		break;
-	case MEDIA_BUS_FMT_SRGGB12_1X12:
-		ret = imx290_set_register_array(imx290, imx290_12bit_settings,
-						ARRAY_SIZE(
-							imx290_12bit_settings));
-		if (ret < 0) {
-			dev_err(imx290->dev, "Could not set format registers\n");
-			return ret;
-		}
-		break;
-	default:
-		dev_err(imx290->dev, "Unknown pixel format\n");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-/* Start streaming */
-static int imx290_start_streaming(struct imx290 *imx290)
-{
-	int ret;
-
-	/* Set init register settings */
-	ret = imx290_set_register_array(imx290, 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;
-	}
-
-	/* Apply the register values related to current frame format */
-	ret = imx290_write_current_format(imx290);
-	if (ret < 0) {
-		dev_err(imx290->dev, "Could not set frame format\n");
-		return ret;
-	}
-
-	/* Apply default values of current mode */
-	ret = imx290_set_register_array(imx290, imx290->current_mode->data,
-					imx290->current_mode->data_size);
-	if (ret < 0) {
-		dev_err(imx290->dev, "Could not set current mode\n");
-		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) {
-		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
-		return ret;
-	}
-
-	imx290_write(imx290, IMX290_STANDBY, 0x00, &ret);
-
-	msleep(30);
-
-	/* Start streaming */
-	return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret);
-}
-
-static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
-{
-	struct imx290 *imx290 = to_imx290(sd);
-	int ret = 0;
-
-	if (enable) {
-		ret = pm_runtime_resume_and_get(imx290->dev);
-		if (ret < 0)
-			goto unlock_and_return;
-
-		ret = imx290_start_streaming(imx290);
-		if (ret) {
-			dev_err(imx290->dev, "Start stream failed\n");
-			pm_runtime_put(imx290->dev);
-			goto unlock_and_return;
-		}
-	} else {
-		imx290_stop_streaming(imx290);
-		pm_runtime_put(imx290->dev);
-	}
-
-unlock_and_return:
-
-	return ret;
-}
-
-static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++)
-		imx290->supplies[i].supply = imx290_supply_name[i];
-
-	return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies),
-				       imx290->supplies);
-}
-
-static int imx290_set_data_lanes(struct imx290 *imx290)
-{
-	int ret = 0, laneval, frsel;
-
-	switch (imx290->nlanes) {
-	case 2:
-		laneval = 0x01;
-		frsel = 0x02;
-		break;
-	case 4:
-		laneval = 0x03;
-		frsel = 0x01;
-		break;
-	default:
-		/*
-		 * We should never hit this since the data lane count is
-		 * validated in probe itself
-		 */
-		dev_err(imx290->dev, "Lane configuration not supported\n");
-		return -EINVAL;
-	}
-
-	imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
-	imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
-	imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
-
-	return ret;
-}
+static const struct v4l2_subdev_video_ops imx290_video_ops = {
+	.s_stream = imx290_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
+	.init_cfg = imx290_entity_init_cfg,
+	.enum_mbus_code = imx290_enum_mbus_code,
+	.enum_frame_size = imx290_enum_frame_size,
+	.get_fmt = imx290_get_fmt,
+	.set_fmt = imx290_set_fmt,
+	.get_selection = imx290_get_selection,
+};
+
+static const struct v4l2_subdev_ops imx290_subdev_ops = {
+	.video = &imx290_video_ops,
+	.pad = &imx290_pad_ops,
+};
+
+static const struct media_entity_operations imx290_subdev_entity_ops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+/* ----------------------------------------------------------------------------
+ * Power management
+ */
 
 static int imx290_power_on(struct device *dev)
 {
@@ -966,105 +1065,30 @@ static const struct dev_pm_ops imx290_pm_ops = {
 	SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL)
 };
 
-static const struct v4l2_subdev_video_ops imx290_video_ops = {
-	.s_stream = imx290_set_stream,
-};
-
-static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
-	.init_cfg = imx290_entity_init_cfg,
-	.enum_mbus_code = imx290_enum_mbus_code,
-	.enum_frame_size = imx290_enum_frame_size,
-	.get_fmt = imx290_get_fmt,
-	.set_fmt = imx290_set_fmt,
-	.get_selection = imx290_get_selection,
-};
+/* ----------------------------------------------------------------------------
+ * Probe & remove
+ */
 
-static const struct v4l2_subdev_ops imx290_subdev_ops = {
-	.video = &imx290_video_ops,
-	.pad = &imx290_pad_ops,
+static const struct regmap_config imx290_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
 };
 
-static const struct media_entity_operations imx290_subdev_entity_ops = {
-	.link_validate = v4l2_subdev_link_validate,
+static const char * const imx290_supply_name[IMX290_NUM_SUPPLIES] = {
+	"vdda",
+	"vddd",
+	"vdddo",
 };
 
-static int imx290_ctrl_init(struct imx290 *imx290)
+static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
 {
-	struct v4l2_fwnode_device_properties props;
-	unsigned int blank;
-	int ret;
+	unsigned int i;
 
-	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
-	if (ret < 0)
-		return ret;
+	for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++)
+		imx290->supplies[i].supply = imx290_supply_name[i];
 
-	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
-	imx290->ctrls.lock = &imx290->lock;
-
-	/*
-	 * The sensor has an analog gain and a digital gain, both controlled
-	 * through a single gain value, expressed in 0.3dB increments. Values
-	 * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
-	 * up to 72.0dB (240) add further digital gain. Limit the range to
-	 * analog gain only, support for digital gain can be added separately
-	 * if needed.
-	 *
-	 * The IMX327 and IMX462 are largely compatible with the IMX290, but
-	 * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital
-	 * gain. When support for those sensors gets added to the driver, the
-	 * gain control should be adjusted accordingly.
-	 */
-	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);
-
-	imx290->link_freq =
-		v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
-				       V4L2_CID_LINK_FREQ,
-				       imx290_link_freqs_num(imx290) - 1, 0,
-				       imx290_link_freqs_ptr(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,
-					       imx290_calc_pixel_rate(imx290));
-
-	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
-				     V4L2_CID_TEST_PATTERN,
-				     ARRAY_SIZE(imx290_test_pattern_menu) - 1,
-				     0, 0, imx290_test_pattern_menu);
-
-	blank = imx290->current_mode->hmax - imx290->current_mode->width;
-	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
-					   V4L2_CID_HBLANK, blank, blank, 1,
-					   blank);
-	if (imx290->hblank)
-		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-
-	blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
-	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
-					   V4L2_CID_VBLANK, blank, blank, 1,
-					   blank);
-	if (imx290->vblank)
-		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-
-	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
-					&props);
-
-	imx290->sd.ctrl_handler = &imx290->ctrls;
-
-	if (imx290->ctrls.error) {
-		ret = imx290->ctrls.error;
-		v4l2_ctrl_handler_free(&imx290->ctrls);
-		return ret;
-	}
-
-	return 0;
+	return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies),
+				       imx290->supplies);
 }
 
 /*
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 02/15] media: i2c: imx290: Factor out subdev init and cleanup to functions
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
  2022-11-22 22:32 ` [PATCH v1 01/15] media: i2c: imx290: Group functions in sections Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  7:44   ` Alexander Stein
  2022-11-24 18:31   ` Dave Stevenson
  2022-11-22 22:32 ` [PATCH v1 03/15] media: i2c: imx290: Factor out control update code to a function Laurent Pinchart
                   ` (12 subsequent siblings)
  14 siblings, 2 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

The probe() function is large. Make it more readable by factoring the
subdev initialization code out. While at it, rename the error labels as
the "free_" prefix isn't accurate.

No functional change intended.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 109 +++++++++++++++++++++----------------
 1 file changed, 62 insertions(+), 47 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index aae4f51941a1..4dbf218e7a63 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -1015,6 +1015,46 @@ static const struct media_entity_operations imx290_subdev_entity_ops = {
 	.link_validate = v4l2_subdev_link_validate,
 };
 
+static int imx290_subdev_init(struct imx290 *imx290)
+{
+	struct i2c_client *client = to_i2c_client(imx290->dev);
+	int ret;
+
+	/*
+	 * Initialize the frame format. In particular, imx290->current_mode
+	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
+	 * below relies on these fields.
+	 */
+	imx290_entity_init_cfg(&imx290->sd, NULL);
+
+	ret = imx290_ctrl_init(imx290);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Control initialization error %d\n", ret);
+		return ret;
+	}
+
+	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
+	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	imx290->sd.dev = imx290->dev;
+	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
+	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+	imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Could not register media entity\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void imx290_subdev_cleanup(struct imx290 *imx290)
+{
+	media_entity_cleanup(&imx290->sd.entity);
+	v4l2_ctrl_handler_free(&imx290->ctrls);
+}
+
 /* ----------------------------------------------------------------------------
  * Power management
  */
@@ -1148,10 +1188,10 @@ static int imx290_probe(struct i2c_client *client)
 	fwnode_handle_put(endpoint);
 	if (ret == -ENXIO) {
 		dev_err(dev, "Unsupported bus type, should be CSI2\n");
-		goto free_err;
+		goto err_endpoint;
 	} else if (ret) {
 		dev_err(dev, "Parsing endpoint node failed\n");
-		goto free_err;
+		goto err_endpoint;
 	}
 
 	/* Get number of data lanes */
@@ -1159,7 +1199,7 @@ static int imx290_probe(struct i2c_client *client)
 	if (imx290->nlanes != 2 && imx290->nlanes != 4) {
 		dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
 		ret = -EINVAL;
-		goto free_err;
+		goto err_endpoint;
 	}
 
 	dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
@@ -1167,7 +1207,7 @@ static int imx290_probe(struct i2c_client *client)
 	if (!ep.nr_of_link_frequencies) {
 		dev_err(dev, "link-frequency property not found in DT\n");
 		ret = -EINVAL;
-		goto free_err;
+		goto err_endpoint;
 	}
 
 	/* Check that link frequences for all the modes are in device tree */
@@ -1175,7 +1215,7 @@ static int imx290_probe(struct i2c_client *client)
 	if (fq) {
 		dev_err(dev, "Link frequency of %lld is not supported\n", fq);
 		ret = -EINVAL;
-		goto free_err;
+		goto err_endpoint;
 	}
 
 	/* get system clock (xclk) */
@@ -1183,14 +1223,14 @@ static int imx290_probe(struct i2c_client *client)
 	if (IS_ERR(imx290->xclk)) {
 		dev_err(dev, "Could not get xclk");
 		ret = PTR_ERR(imx290->xclk);
-		goto free_err;
+		goto err_endpoint;
 	}
 
 	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
 				       &xclk_freq);
 	if (ret) {
 		dev_err(dev, "Could not get xclk frequency\n");
-		goto free_err;
+		goto err_endpoint;
 	}
 
 	/* external clock must be 37.125 MHz */
@@ -1198,19 +1238,19 @@ static int imx290_probe(struct i2c_client *client)
 		dev_err(dev, "External clock frequency %u is not supported\n",
 			xclk_freq);
 		ret = -EINVAL;
-		goto free_err;
+		goto err_endpoint;
 	}
 
 	ret = clk_set_rate(imx290->xclk, xclk_freq);
 	if (ret) {
 		dev_err(dev, "Could not set xclk frequency\n");
-		goto free_err;
+		goto err_endpoint;
 	}
 
 	ret = imx290_get_regulators(dev, imx290);
 	if (ret < 0) {
 		dev_err(dev, "Cannot get regulators\n");
-		goto free_err;
+		goto err_endpoint;
 	}
 
 	imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
@@ -1218,55 +1258,33 @@ static int imx290_probe(struct i2c_client *client)
 	if (IS_ERR(imx290->rst_gpio)) {
 		dev_err(dev, "Cannot get reset gpio\n");
 		ret = PTR_ERR(imx290->rst_gpio);
-		goto free_err;
+		goto err_endpoint;
 	}
 
 	mutex_init(&imx290->lock);
 
-	/*
-	 * Initialize the frame format. In particular, imx290->current_mode
-	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
-	 * below relies on these fields.
-	 */
-	imx290_entity_init_cfg(&imx290->sd, NULL);
-
-	ret = imx290_ctrl_init(imx290);
-	if (ret < 0) {
-		dev_err(dev, "Control initialization error %d\n", ret);
-		goto free_mutex;
-	}
-
-	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
-	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-	imx290->sd.dev = &client->dev;
-	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
-	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
-
-	imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
-	ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
-	if (ret < 0) {
-		dev_err(dev, "Could not register media entity\n");
-		goto free_ctrl;
-	}
+	ret = imx290_subdev_init(imx290);
+	if (ret)
+		goto err_mutex;
 
 	ret = v4l2_async_register_subdev(&imx290->sd);
 	if (ret < 0) {
 		dev_err(dev, "Could not register v4l2 device\n");
-		goto free_entity;
+		goto err_subdev;
 	}
 
 	/* Power on the device to match runtime PM state below */
 	ret = imx290_power_on(dev);
 	if (ret < 0) {
 		dev_err(dev, "Could not power on the device\n");
-		goto free_entity;
+		goto err_subdev;
 	}
 
 	ret = imx290_read(imx290, IMX290_CHIP_ID, &chip_id);
 	if (ret) {
 		dev_err(dev, "Could not read chip ID: %d\n", ret);
 		imx290_power_off(dev);
-		goto free_entity;
+		goto err_subdev;
 	}
 
 	dev_info(dev, "chip ID 0x%04x\n", chip_id);
@@ -1279,13 +1297,11 @@ static int imx290_probe(struct i2c_client *client)
 
 	return 0;
 
-free_entity:
-	media_entity_cleanup(&imx290->sd.entity);
-free_ctrl:
-	v4l2_ctrl_handler_free(&imx290->ctrls);
-free_mutex:
+err_subdev:
+	imx290_subdev_cleanup(imx290);
+err_mutex:
 	mutex_destroy(&imx290->lock);
-free_err:
+err_endpoint:
 	v4l2_fwnode_endpoint_free(&ep);
 
 	return ret;
@@ -1297,8 +1313,7 @@ static void imx290_remove(struct i2c_client *client)
 	struct imx290 *imx290 = to_imx290(sd);
 
 	v4l2_async_unregister_subdev(sd);
-	media_entity_cleanup(&sd->entity);
-	v4l2_ctrl_handler_free(sd->ctrl_handler);
+	imx290_subdev_cleanup(imx290);
 
 	mutex_destroy(&imx290->lock);
 
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 03/15] media: i2c: imx290: Factor out control update code to a function
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
  2022-11-22 22:32 ` [PATCH v1 01/15] media: i2c: imx290: Group functions in sections Laurent Pinchart
  2022-11-22 22:32 ` [PATCH v1 02/15] media: i2c: imx290: Factor out subdev init and cleanup to functions Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  7:51   ` Alexander Stein
  2022-11-22 22:32 ` [PATCH v1 04/15] media: i2c: imx290: Access link_freq_index directly Laurent Pinchart
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

Move the control update code to a separate function to group it with all
the control-related code and make imx290_set_fmt() more readable.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 36 ++++++++++++++++--------------------
 1 file changed, 16 insertions(+), 20 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 4dbf218e7a63..eb295502d0c3 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -639,6 +639,21 @@ static const char * const imx290_test_pattern_menu[] = {
 	"000/555h Toggle Pattern",
 };
 
+static void imx290_ctrl_update(struct imx290 *imx290,
+			       const struct imx290_mode *mode)
+{
+	unsigned int hblank = mode->hmax - mode->width;
+	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
+
+	__v4l2_ctrl_s_ctrl(imx290->link_freq,
+			   imx290_get_link_freq_index(imx290));
+	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
+				 imx290_calc_pixel_rate(imx290));
+
+	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
+	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
+}
+
 static int imx290_ctrl_init(struct imx290 *imx290)
 {
 	struct v4l2_fwnode_device_properties props;
@@ -904,26 +919,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
 		imx290->current_mode = mode;
 		imx290->bpp = imx290_formats[i].bpp;
 
-		if (imx290->link_freq)
-			__v4l2_ctrl_s_ctrl(imx290->link_freq,
-					   imx290_get_link_freq_index(imx290));
-		if (imx290->pixel_rate)
-			__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
-						 imx290_calc_pixel_rate(imx290));
-
-		if (imx290->hblank) {
-			unsigned int hblank = mode->hmax - mode->width;
-
-			__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank,
-						 1, hblank);
-		}
-
-		if (imx290->vblank) {
-			unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
-
-			__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank,
-						 1, vblank);
-		}
+		imx290_ctrl_update(imx290, mode);
 	}
 
 	*format = fmt->format;
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 04/15] media: i2c: imx290: Access link_freq_index directly
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (2 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 03/15] media: i2c: imx290: Factor out control update code to a function Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  7:53   ` Alexander Stein
  2022-11-22 22:32 ` [PATCH v1 05/15] media: i2c: imx290: Pass format and mode to imx290_calc_pixel_rate() Laurent Pinchart
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

The imx290_get_link_freq_index() function hides the fact that it relies
on the imx290 current_mode field, which obfuscates the code instead of
making it more readable. Inline it in the callers, and use the mode
pointer we already have in imx290_ctrl_update() instead of using the
current_mode field.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index eb295502d0c3..2d198b167853 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -547,14 +547,9 @@ static int imx290_write_current_format(struct imx290 *imx290)
 	return 0;
 }
 
-static inline u8 imx290_get_link_freq_index(struct imx290 *imx290)
-{
-	return imx290->current_mode->link_freq_index;
-}
-
 static s64 imx290_get_link_freq(struct imx290 *imx290)
 {
-	u8 index = imx290_get_link_freq_index(imx290);
+	u8 index = imx290->current_mode->link_freq_index;
 
 	return *(imx290_link_freqs_ptr(imx290) + index);
 }
@@ -645,8 +640,7 @@ static void imx290_ctrl_update(struct imx290 *imx290,
 	unsigned int hblank = mode->hmax - mode->width;
 	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
 
-	__v4l2_ctrl_s_ctrl(imx290->link_freq,
-			   imx290_get_link_freq_index(imx290));
+	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
 	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
 				 imx290_calc_pixel_rate(imx290));
 
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 05/15] media: i2c: imx290: Pass format and mode to imx290_calc_pixel_rate()
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (3 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 04/15] media: i2c: imx290: Access link_freq_index directly Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  9:06   ` Alexander Stein
  2022-11-22 22:32 ` [PATCH v1 06/15] media: i2c: imx290: Compute pixel rate and blanking in one place Laurent Pinchart
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

Avoid accessing the imx290 current_format and current_mode fields in
imx290_calc_pixel_rate() to prepare for the removal of those fields.
Among the two callers of the function, imx290_ctrl_update() has an
explicit mode pointer already, and we can also give it a format pointer.
Use those explicitly.

While at it, inline the imx290_get_link_freq() function in
imx290_calc_pixel_rate() as it is only called there.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 27 ++++++++++++---------------
 1 file changed, 12 insertions(+), 15 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 2d198b167853..a676ce93f96e 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -547,21 +547,15 @@ static int imx290_write_current_format(struct imx290 *imx290)
 	return 0;
 }
 
-static s64 imx290_get_link_freq(struct imx290 *imx290)
+static u64 imx290_calc_pixel_rate(struct imx290 *imx290,
+				  const struct v4l2_mbus_framefmt *format,
+				  const struct imx290_mode *mode)
 {
-	u8 index = imx290->current_mode->link_freq_index;
-
-	return *(imx290_link_freqs_ptr(imx290) + index);
-}
-
-static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
-{
-	s64 link_freq = imx290_get_link_freq(imx290);
-	u8 nlanes = imx290->nlanes;
+	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 * nlanes;
+	pixel_rate = link_freq * 2 * imx290->nlanes;
 	do_div(pixel_rate, imx290->bpp);
 	return pixel_rate;
 }
@@ -635,6 +629,7 @@ static const char * const imx290_test_pattern_menu[] = {
 };
 
 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;
@@ -642,7 +637,7 @@ static void imx290_ctrl_update(struct imx290 *imx290,
 
 	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
 	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
-				 imx290_calc_pixel_rate(imx290));
+				 imx290_calc_pixel_rate(imx290, format, mode));
 
 	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
 	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
@@ -652,6 +647,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 {
 	struct v4l2_fwnode_device_properties props;
 	unsigned int blank;
+	u64 pixel_rate;
 	int ret;
 
 	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
@@ -689,10 +685,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 	if (imx290->link_freq)
 		imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
+	pixel_rate = imx290_calc_pixel_rate(imx290, &imx290->current_format,
+					    imx290->current_mode);
 	imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
 					       V4L2_CID_PIXEL_RATE,
-					       1, INT_MAX, 1,
-					       imx290_calc_pixel_rate(imx290));
+					       1, INT_MAX, 1, pixel_rate);
 
 	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
 				     V4L2_CID_TEST_PATTERN,
@@ -913,7 +910,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
 		imx290->current_mode = mode;
 		imx290->bpp = imx290_formats[i].bpp;
 
-		imx290_ctrl_update(imx290, mode);
+		imx290_ctrl_update(imx290, &fmt->format, mode);
 	}
 
 	*format = fmt->format;
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 06/15] media: i2c: imx290: Compute pixel rate and blanking in one place
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (4 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 05/15] media: i2c: imx290: Pass format and mode to imx290_calc_pixel_rate() Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  7:59   ` Alexander Stein
  2022-11-23  9:58   ` Dave Stevenson
  2022-11-22 22:32 ` [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function Laurent Pinchart
                   ` (8 subsequent siblings)
  14 siblings, 2 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

The hblank, vblank, pixel rate and link frequency values and limits are
currently computed when creating controls, in imx290_ctrl_init(), and
updated in imx290_ctrl_update(). This duplicates the logic in different
places. Simplify the code by setting the control values and limits to
hardcoded values when creating the controls, and call
imx290_ctrl_update() to then update them.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 46 +++++++++++++++++---------------------
 1 file changed, 20 insertions(+), 26 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index a676ce93f96e..135ed55592a1 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -547,19 +547,6 @@ static int imx290_write_current_format(struct imx290 *imx290)
 	return 0;
 }
 
-static u64 imx290_calc_pixel_rate(struct imx290 *imx290,
-				  const struct v4l2_mbus_framefmt *format,
-				  const struct imx290_mode *mode)
-{
-	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->bpp);
-	return pixel_rate;
-}
-
 /* ----------------------------------------------------------------------------
  * Controls
  */
@@ -634,10 +621,15 @@ 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->bpp);
 
 	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
-	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
-				 imx290_calc_pixel_rate(imx290, format, mode));
+	__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);
@@ -646,8 +638,6 @@ static void imx290_ctrl_update(struct imx290 *imx290,
 static int imx290_ctrl_init(struct imx290 *imx290)
 {
 	struct v4l2_fwnode_device_properties props;
-	unsigned int blank;
-	u64 pixel_rate;
 	int ret;
 
 	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
@@ -677,6 +667,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 			  V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
 			  IMX290_VMAX_DEFAULT - 2);
 
+	/*
+	 * Set the link frequency, pixel rate, horizontal blanking and vertical
+	 * blanking it hardcoded values, they will be updated by
+	 * imx290_ctrl_update().
+	 */
 	imx290->link_freq =
 		v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
 				       V4L2_CID_LINK_FREQ,
@@ -685,28 +680,22 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 	if (imx290->link_freq)
 		imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
-	pixel_rate = imx290_calc_pixel_rate(imx290, &imx290->current_format,
-					    imx290->current_mode);
 	imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
 					       V4L2_CID_PIXEL_RATE,
-					       1, INT_MAX, 1, pixel_rate);
+					       1, INT_MAX, 1, 1);
 
 	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
 				     V4L2_CID_TEST_PATTERN,
 				     ARRAY_SIZE(imx290_test_pattern_menu) - 1,
 				     0, 0, imx290_test_pattern_menu);
 
-	blank = imx290->current_mode->hmax - imx290->current_mode->width;
 	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
-					   V4L2_CID_HBLANK, blank, blank, 1,
-					   blank);
+					   V4L2_CID_HBLANK, 1, 1, 1, 1);
 	if (imx290->hblank)
 		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
-	blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
 	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
-					   V4L2_CID_VBLANK, blank, blank, 1,
-					   blank);
+					   V4L2_CID_VBLANK, 1, 1, 1, 1);
 	if (imx290->vblank)
 		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
@@ -721,6 +710,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 		return ret;
 	}
 
+	mutex_lock(imx290->ctrls.lock);
+	imx290_ctrl_update(imx290, &imx290->current_format,
+			   imx290->current_mode);
+	mutex_unlock(imx290->ctrls.lock);
+
 	return 0;
 }
 
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (5 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 06/15] media: i2c: imx290: Compute pixel rate and blanking in one place Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  8:03   ` Alexander Stein
  2022-11-23 10:08   ` Dave Stevenson
  2022-11-22 22:32 ` [PATCH v1 08/15] media: i2c: imx290: Factor out DT parsing to separate function Laurent Pinchart
                   ` (7 subsequent siblings)
  14 siblings, 2 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

The black level programmed in the BLKLEVEL register depends on the
output format. The black level value computation is currently performed
in imx290_set_ctrl(), in addition to having different black level values
in the output-specific register value tables. Move it to a separate
function to simplify the imx290_set_ctrl() code.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 53 +++++++++++++++++++++-----------------
 1 file changed, 29 insertions(+), 24 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 135ed55592a1..d9fc6c44b0f0 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -152,6 +152,9 @@
 #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH		1920
 #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT		1080
 
+/* Equivalent value for 16bpp */
+#define IMX290_BLACK_LEVEL_DEFAULT			3840
+
 #define IMX290_NUM_SUPPLIES				3
 
 struct imx290_regval {
@@ -315,7 +318,6 @@ static const struct imx290_regval imx290_10bit_settings[] = {
 	{ IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
 	{ IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
 	{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
-	{ IMX290_BLKLEVEL, 60 },
 };
 
 static const struct imx290_regval imx290_12bit_settings[] = {
@@ -325,7 +327,6 @@ static const struct imx290_regval imx290_12bit_settings[] = {
 	{ IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
 	{ IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
 	{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
-	{ IMX290_BLKLEVEL, 240 },
 };
 
 /* supported link frequencies */
@@ -516,35 +517,43 @@ static int imx290_set_data_lanes(struct imx290 *imx290)
 	return ret;
 }
 
+static int imx290_set_black_level(struct imx290 *imx290,
+				  unsigned int black_level, int *err)
+{
+	return imx290_write(imx290, IMX290_BLKLEVEL,
+			    black_level >> (16 - imx290->bpp), err);
+}
+
 static int imx290_write_current_format(struct imx290 *imx290)
 {
+	const struct imx290_regval *regs;
+	unsigned int num_regs;
+	unsigned int bpp;
 	int ret;
 
 	switch (imx290->current_format.code) {
 	case MEDIA_BUS_FMT_SRGGB10_1X10:
-		ret = imx290_set_register_array(imx290, imx290_10bit_settings,
-						ARRAY_SIZE(
-							imx290_10bit_settings));
-		if (ret < 0) {
-			dev_err(imx290->dev, "Could not set format registers\n");
-			return ret;
-		}
+		regs = imx290_10bit_settings;
+		num_regs = ARRAY_SIZE(imx290_10bit_settings);
+		bpp = 10;
 		break;
 	case MEDIA_BUS_FMT_SRGGB12_1X12:
-		ret = imx290_set_register_array(imx290, imx290_12bit_settings,
-						ARRAY_SIZE(
-							imx290_12bit_settings));
-		if (ret < 0) {
-			dev_err(imx290->dev, "Could not set format registers\n");
-			return ret;
-		}
+		regs = imx290_12bit_settings;
+		num_regs = ARRAY_SIZE(imx290_12bit_settings);
+		bpp = 12;
 		break;
 	default:
 		dev_err(imx290->dev, "Unknown pixel format\n");
 		return -EINVAL;
 	}
 
-	return 0;
+	ret = imx290_set_register_array(imx290, regs, num_regs);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Could not set format registers\n");
+		return ret;
+	}
+
+	return imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT, &ret);
 }
 
 /* ----------------------------------------------------------------------------
@@ -573,7 +582,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 
 	case V4L2_CID_TEST_PATTERN:
 		if (ctrl->val) {
-			imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
+			imx290_set_black_level(imx290, 0, &ret);
 			usleep_range(10000, 11000);
 			imx290_write(imx290, IMX290_PGCTRL,
 				     (u8)(IMX290_PGCTRL_REGEN |
@@ -582,12 +591,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 		} else {
 			imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
 			usleep_range(10000, 11000);
-			if (imx290->bpp == 10)
-				imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
-					     &ret);
-			else /* 12 bits per pixel */
-				imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
-					     &ret);
+			imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT,
+					       &ret);
 		}
 		break;
 	default:
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 08/15] media: i2c: imx290: Factor out DT parsing to separate function
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (6 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  8:14   ` Alexander Stein
  2022-11-22 22:32 ` [PATCH v1 09/15] media: i2c: imx290: Use dev_err_probe() Laurent Pinchart
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

Make the probe() function more readable by factoring out the DT parsing
code to a separate function. No functional change intended.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 97 +++++++++++++++++++++-----------------
 1 file changed, 53 insertions(+), 44 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index d9fc6c44b0f0..655f676df3d2 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -1139,112 +1139,125 @@ static s64 imx290_check_link_freqs(const struct imx290 *imx290,
 	return 0;
 }
 
-static int imx290_probe(struct i2c_client *client)
+static int imx290_parse_dt(struct imx290 *imx290)
 {
-	struct device *dev = &client->dev;
-	struct fwnode_handle *endpoint;
 	/* Only CSI2 is supported for now: */
 	struct v4l2_fwnode_endpoint ep = {
 		.bus_type = V4L2_MBUS_CSI2_DPHY
 	};
-	struct imx290 *imx290;
-	u32 xclk_freq;
-	u32 chip_id;
+	struct fwnode_handle *endpoint;
+	int ret;
 	s64 fq;
-	int ret;
 
-	imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
-	if (!imx290)
-		return -ENOMEM;
-
-	imx290->dev = dev;
-	imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config);
-	if (IS_ERR(imx290->regmap)) {
-		dev_err(dev, "Unable to initialize I2C\n");
-		return -ENODEV;
-	}
-
-	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), NULL);
 	if (!endpoint) {
-		dev_err(dev, "Endpoint node not found\n");
+		dev_err(imx290->dev, "Endpoint node not found\n");
 		return -EINVAL;
 	}
 
 	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
 	fwnode_handle_put(endpoint);
 	if (ret == -ENXIO) {
-		dev_err(dev, "Unsupported bus type, should be CSI2\n");
-		goto err_endpoint;
+		dev_err(imx290->dev, "Unsupported bus type, should be CSI2\n");
+		goto done;
 	} else if (ret) {
-		dev_err(dev, "Parsing endpoint node failed\n");
-		goto err_endpoint;
+		dev_err(imx290->dev, "Parsing endpoint node failed\n");
+		goto done;
 	}
 
 	/* Get number of data lanes */
 	imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes;
 	if (imx290->nlanes != 2 && imx290->nlanes != 4) {
-		dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
+		dev_err(imx290->dev, "Invalid data lanes: %d\n", imx290->nlanes);
 		ret = -EINVAL;
-		goto err_endpoint;
+		goto done;
 	}
 
-	dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
+	dev_dbg(imx290->dev, "Using %u data lanes\n", imx290->nlanes);
 
 	if (!ep.nr_of_link_frequencies) {
-		dev_err(dev, "link-frequency property not found in DT\n");
+		dev_err(imx290->dev, "link-frequency property not found in DT\n");
 		ret = -EINVAL;
-		goto err_endpoint;
+		goto done;
 	}
 
 	/* Check that link frequences for all the modes are in device tree */
 	fq = imx290_check_link_freqs(imx290, &ep);
 	if (fq) {
-		dev_err(dev, "Link frequency of %lld is not supported\n", fq);
+		dev_err(imx290->dev, "Link frequency of %lld is not supported\n",
+			fq);
 		ret = -EINVAL;
-		goto err_endpoint;
+		goto done;
 	}
 
+	ret = 0;
+
+done:
+	v4l2_fwnode_endpoint_free(&ep);
+	return ret;
+}
+
+static int imx290_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct imx290 *imx290;
+	u32 xclk_freq;
+	u32 chip_id;
+	int ret;
+
+	imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
+	if (!imx290)
+		return -ENOMEM;
+
+	imx290->dev = dev;
+	imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config);
+	if (IS_ERR(imx290->regmap)) {
+		dev_err(dev, "Unable to initialize I2C\n");
+		return -ENODEV;
+	}
+
+	ret = imx290_parse_dt(imx290);
+	if (ret)
+		return ret;
+
 	/* get system clock (xclk) */
 	imx290->xclk = devm_clk_get(dev, "xclk");
 	if (IS_ERR(imx290->xclk)) {
 		dev_err(dev, "Could not get xclk");
-		ret = PTR_ERR(imx290->xclk);
-		goto err_endpoint;
+		return PTR_ERR(imx290->xclk);
 	}
 
 	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
 				       &xclk_freq);
 	if (ret) {
 		dev_err(dev, "Could not get xclk frequency\n");
-		goto err_endpoint;
+		return ret;
 	}
 
 	/* external clock must be 37.125 MHz */
 	if (xclk_freq != 37125000) {
 		dev_err(dev, "External clock frequency %u is not supported\n",
 			xclk_freq);
-		ret = -EINVAL;
-		goto err_endpoint;
+		return -EINVAL;
 	}
 
 	ret = clk_set_rate(imx290->xclk, xclk_freq);
 	if (ret) {
 		dev_err(dev, "Could not set xclk frequency\n");
-		goto err_endpoint;
+		return ret;
 	}
 
 	ret = imx290_get_regulators(dev, imx290);
 	if (ret < 0) {
 		dev_err(dev, "Cannot get regulators\n");
-		goto err_endpoint;
+		return ret;
 	}
 
 	imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
 						   GPIOD_OUT_HIGH);
 	if (IS_ERR(imx290->rst_gpio)) {
 		dev_err(dev, "Cannot get reset gpio\n");
-		ret = PTR_ERR(imx290->rst_gpio);
-		goto err_endpoint;
+		return PTR_ERR(imx290->rst_gpio);
 	}
 
 	mutex_init(&imx290->lock);
@@ -1279,16 +1292,12 @@ static int imx290_probe(struct i2c_client *client)
 	pm_runtime_enable(dev);
 	pm_runtime_idle(dev);
 
-	v4l2_fwnode_endpoint_free(&ep);
-
 	return 0;
 
 err_subdev:
 	imx290_subdev_cleanup(imx290);
 err_mutex:
 	mutex_destroy(&imx290->lock);
-err_endpoint:
-	v4l2_fwnode_endpoint_free(&ep);
 
 	return ret;
 }
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 09/15] media: i2c: imx290: Use dev_err_probe()
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (7 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 08/15] media: i2c: imx290: Factor out DT parsing to separate function Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  8:16   ` Alexander Stein
  2022-11-22 22:32 ` [PATCH v1 10/15] media: i2c: imx290: Factor out clock initialization to separate function Laurent Pinchart
                   ` (5 subsequent siblings)
  14 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

Improve error handling in the probe() function with dev_err_probe().

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 20 ++++++++------------
 1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 655f676df3d2..d423860402fd 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -1222,10 +1222,9 @@ static int imx290_probe(struct i2c_client *client)
 
 	/* get system clock (xclk) */
 	imx290->xclk = devm_clk_get(dev, "xclk");
-	if (IS_ERR(imx290->xclk)) {
-		dev_err(dev, "Could not get xclk");
-		return PTR_ERR(imx290->xclk);
-	}
+	if (IS_ERR(imx290->xclk))
+		return dev_err_probe(dev, PTR_ERR(imx290->xclk),
+				     "Could not get xclk");
 
 	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
 				       &xclk_freq);
@@ -1248,17 +1247,14 @@ static int imx290_probe(struct i2c_client *client)
 	}
 
 	ret = imx290_get_regulators(dev, imx290);
-	if (ret < 0) {
-		dev_err(dev, "Cannot get regulators\n");
-		return ret;
-	}
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Cannot get regulators\n");
 
 	imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
 						   GPIOD_OUT_HIGH);
-	if (IS_ERR(imx290->rst_gpio)) {
-		dev_err(dev, "Cannot get reset gpio\n");
-		return PTR_ERR(imx290->rst_gpio);
-	}
+	if (IS_ERR(imx290->rst_gpio))
+		return dev_err_probe(dev, PTR_ERR(imx290->rst_gpio),
+				     "Cannot get reset gpio\n");
 
 	mutex_init(&imx290->lock);
 
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 10/15] media: i2c: imx290: Factor out clock initialization to separate function
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (8 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 09/15] media: i2c: imx290: Use dev_err_probe() Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  8:18   ` Alexander Stein
  2022-11-22 22:32 ` [PATCH v1 11/15] media: i2c: imx290: Use V4L2 subdev active state Laurent Pinchart
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

Move the external clock initialization code from probe() to a separate
function to improve readability. No functional change intended.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 57 +++++++++++++++++++++++---------------
 1 file changed, 35 insertions(+), 22 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index d423860402fd..848de4c90d3b 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -1117,6 +1117,34 @@ static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
 				       imx290->supplies);
 }
 
+static int imx290_init_clk(struct imx290 *imx290)
+{
+	u32 xclk_freq;
+	int ret;
+
+	ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
+				       "clock-frequency", &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) {
+		dev_err(imx290->dev, "External clock frequency %u is not supported\n",
+			xclk_freq);
+		return -EINVAL;
+	}
+
+	ret = clk_set_rate(imx290->xclk, xclk_freq);
+	if (ret) {
+		dev_err(imx290->dev, "Could not set xclk frequency\n");
+		return ret;
+	}
+
+	return 0;
+}
+
 /*
  * Returns 0 if all link frequencies used by the driver for the given number
  * of MIPI data lanes are mentioned in the device tree, or the value of the
@@ -1201,7 +1229,6 @@ static int imx290_probe(struct i2c_client *client)
 {
 	struct device *dev = &client->dev;
 	struct imx290 *imx290;
-	u32 xclk_freq;
 	u32 chip_id;
 	int ret;
 
@@ -1220,32 +1247,12 @@ static int imx290_probe(struct i2c_client *client)
 	if (ret)
 		return ret;
 
-	/* get system clock (xclk) */
+	/* Acquire resources. */
 	imx290->xclk = devm_clk_get(dev, "xclk");
 	if (IS_ERR(imx290->xclk))
 		return dev_err_probe(dev, PTR_ERR(imx290->xclk),
 				     "Could not get xclk");
 
-	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
-				       &xclk_freq);
-	if (ret) {
-		dev_err(dev, "Could not get xclk frequency\n");
-		return ret;
-	}
-
-	/* external clock must be 37.125 MHz */
-	if (xclk_freq != 37125000) {
-		dev_err(dev, "External clock frequency %u is not supported\n",
-			xclk_freq);
-		return -EINVAL;
-	}
-
-	ret = clk_set_rate(imx290->xclk, xclk_freq);
-	if (ret) {
-		dev_err(dev, "Could not set xclk frequency\n");
-		return ret;
-	}
-
 	ret = imx290_get_regulators(dev, imx290);
 	if (ret < 0)
 		return dev_err_probe(dev, ret, "Cannot get regulators\n");
@@ -1256,8 +1263,14 @@ static int imx290_probe(struct i2c_client *client)
 		return dev_err_probe(dev, PTR_ERR(imx290->rst_gpio),
 				     "Cannot get reset gpio\n");
 
+	/* Initialize external clock frequency. */
+	ret = imx290_init_clk(imx290);
+	if (ret)
+		return ret;
+
 	mutex_init(&imx290->lock);
 
+	/* Initialize and register subdev. */
 	ret = imx290_subdev_init(imx290);
 	if (ret)
 		goto err_mutex;
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 11/15] media: i2c: imx290: Use V4L2 subdev active state
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (9 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 10/15] media: i2c: imx290: Factor out clock initialization to separate function Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  8:42   ` Alexander Stein
  2022-11-22 22:32 ` [PATCH v1 12/15] media: i2c: imx290: Rename, extend and expand usage of imx290_pixfmt Laurent Pinchart
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

Use the V4L2 subdev active state API to store the active format. This
simplifies the driver not only by dropping the imx290 current_format
field, but it also allows dropping the imx290 lock, replaced with the
state lock.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 139 +++++++++++++++----------------------
 1 file changed, 56 insertions(+), 83 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 848de4c90d3b..cd2154983341 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -177,12 +177,12 @@ struct imx290 {
 	struct clk *xclk;
 	struct regmap *regmap;
 	u8 nlanes;
-	u8 bpp;
 
 	struct v4l2_subdev sd;
 	struct media_pad pad;
-	struct v4l2_mbus_framefmt current_format;
+
 	const struct imx290_mode *current_mode;
+	u8 bpp;
 
 	struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
 	struct gpio_desc *rst_gpio;
@@ -192,8 +192,6 @@ struct imx290 {
 	struct v4l2_ctrl *pixel_rate;
 	struct v4l2_ctrl *hblank;
 	struct v4l2_ctrl *vblank;
-
-	struct mutex lock;
 };
 
 static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
@@ -524,14 +522,15 @@ static int imx290_set_black_level(struct imx290 *imx290,
 			    black_level >> (16 - imx290->bpp), err);
 }
 
-static int imx290_write_current_format(struct imx290 *imx290)
+static int imx290_setup_format(struct imx290 *imx290,
+			       const struct v4l2_mbus_framefmt *format)
 {
 	const struct imx290_regval *regs;
 	unsigned int num_regs;
 	unsigned int bpp;
 	int ret;
 
-	switch (imx290->current_format.code) {
+	switch (format->code) {
 	case MEDIA_BUS_FMT_SRGGB10_1X10:
 		regs = imx290_10bit_settings;
 		num_regs = ARRAY_SIZE(imx290_10bit_settings);
@@ -564,12 +563,17 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct imx290 *imx290 = container_of(ctrl->handler,
 					     struct imx290, ctrls);
+	const struct v4l2_mbus_framefmt *format;
+	struct v4l2_subdev_state *state;
 	int ret = 0;
 
 	/* V4L2 controls values will be applied only when power is already up */
 	if (!pm_runtime_get_if_in_use(imx290->dev))
 		return 0;
 
+	state = v4l2_subdev_get_locked_active_state(&imx290->sd);
+	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
+
 	switch (ctrl->id) {
 	case V4L2_CID_ANALOGUE_GAIN:
 		ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
@@ -646,11 +650,10 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 	int ret;
 
 	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
-	if (ret < 0)
+	if (ret)
 		return ret;
 
 	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
-	imx290->ctrls.lock = &imx290->lock;
 
 	/*
 	 * The sensor has an analog gain and a digital gain, both controlled
@@ -715,11 +718,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
 		return ret;
 	}
 
-	mutex_lock(imx290->ctrls.lock);
-	imx290_ctrl_update(imx290, &imx290->current_format,
-			   imx290->current_mode);
-	mutex_unlock(imx290->ctrls.lock);
-
 	return 0;
 }
 
@@ -728,8 +726,10 @@ static int imx290_ctrl_init(struct imx290 *imx290)
  */
 
 /* Start streaming */
-static int imx290_start_streaming(struct imx290 *imx290)
+static int imx290_start_streaming(struct imx290 *imx290,
+				  struct v4l2_subdev_state *state)
 {
+	const struct v4l2_mbus_framefmt *format;
 	int ret;
 
 	/* Set init register settings */
@@ -742,7 +742,8 @@ static int imx290_start_streaming(struct imx290 *imx290)
 	}
 
 	/* Apply the register values related to current frame format */
-	ret = imx290_write_current_format(imx290);
+	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
+	ret = imx290_setup_format(imx290, format);
 	if (ret < 0) {
 		dev_err(imx290->dev, "Could not set frame format\n");
 		return ret;
@@ -762,7 +763,7 @@ static int imx290_start_streaming(struct imx290 *imx290)
 		return ret;
 
 	/* Apply customized values from user */
-	ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
+	ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
 	if (ret) {
 		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
 		return ret;
@@ -791,39 +792,32 @@ static int imx290_stop_streaming(struct imx290 *imx290)
 static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
 {
 	struct imx290 *imx290 = to_imx290(sd);
+	struct v4l2_subdev_state *state;
 	int ret = 0;
 
+	state = v4l2_subdev_lock_and_get_active_state(sd);
+
 	if (enable) {
 		ret = pm_runtime_resume_and_get(imx290->dev);
 		if (ret < 0)
-			goto unlock_and_return;
+			goto unlock;
 
-		ret = imx290_start_streaming(imx290);
+		ret = imx290_start_streaming(imx290, state);
 		if (ret) {
 			dev_err(imx290->dev, "Start stream failed\n");
 			pm_runtime_put(imx290->dev);
-			goto unlock_and_return;
+			goto unlock;
 		}
 	} else {
 		imx290_stop_streaming(imx290);
 		pm_runtime_put(imx290->dev);
 	}
 
-unlock_and_return:
-
+unlock:
+	v4l2_subdev_unlock_state(state);
 	return ret;
 }
 
-static struct v4l2_mbus_framefmt *
-imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state *state,
-		      u32 which)
-{
-	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
-		return &imx290->current_format;
-	else
-		return v4l2_subdev_get_try_format(&imx290->sd, state, 0);
-}
-
 static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
 				 struct v4l2_subdev_state *sd_state,
 				 struct v4l2_subdev_mbus_code_enum *code)
@@ -858,23 +852,6 @@ static int imx290_enum_frame_size(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int imx290_get_fmt(struct v4l2_subdev *sd,
-			  struct v4l2_subdev_state *sd_state,
-			  struct v4l2_subdev_format *fmt)
-{
-	struct imx290 *imx290 = to_imx290(sd);
-	struct v4l2_mbus_framefmt *framefmt;
-
-	mutex_lock(&imx290->lock);
-
-	framefmt = imx290_get_pad_format(imx290, sd_state, fmt->which);
-	fmt->format = *framefmt;
-
-	mutex_unlock(&imx290->lock);
-
-	return 0;
-}
-
 static int imx290_set_fmt(struct v4l2_subdev *sd,
 			  struct v4l2_subdev_state *sd_state,
 			  struct v4l2_subdev_format *fmt)
@@ -884,8 +861,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
 	struct v4l2_mbus_framefmt *format;
 	unsigned int i;
 
-	mutex_lock(&imx290->lock);
-
 	mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
 				      imx290_modes_num(imx290), width, height,
 				      fmt->format.width, fmt->format.height);
@@ -903,7 +878,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
 	fmt->format.code = imx290_formats[i].code;
 	fmt->format.field = V4L2_FIELD_NONE;
 
-	format = imx290_get_pad_format(imx290, sd_state, fmt->which);
+	format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
 		imx290->current_mode = mode;
@@ -914,8 +889,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
 
 	*format = fmt->format;
 
-	mutex_unlock(&imx290->lock);
-
 	return 0;
 }
 
@@ -923,14 +896,11 @@ 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) {
 	case V4L2_SEL_TGT_CROP: {
-		format = imx290_get_pad_format(imx290, sd_state, sel->which);
-
-		mutex_lock(&imx290->lock);
+		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;
@@ -939,7 +909,6 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
 		sel->r.width = format->width;
 		sel->r.height = format->height;
 
-		mutex_unlock(&imx290->lock);
 		return 0;
 	}
 
@@ -968,11 +937,13 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
 static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
 				  struct v4l2_subdev_state *sd_state)
 {
-	struct v4l2_subdev_format fmt = { 0 };
-
-	fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
-	fmt.format.width = 1920;
-	fmt.format.height = 1080;
+	struct v4l2_subdev_format fmt = {
+		.which = V4L2_SUBDEV_FORMAT_TRY,
+		.format = {
+			.width = 1920,
+			.height = 1080,
+		},
+	};
 
 	imx290_set_fmt(subdev, sd_state, &fmt);
 
@@ -987,7 +958,7 @@ static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
 	.init_cfg = imx290_entity_init_cfg,
 	.enum_mbus_code = imx290_enum_mbus_code,
 	.enum_frame_size = imx290_enum_frame_size,
-	.get_fmt = imx290_get_fmt,
+	.get_fmt = v4l2_subdev_get_fmt,
 	.set_fmt = imx290_set_fmt,
 	.get_selection = imx290_get_selection,
 };
@@ -1004,20 +975,12 @@ static const struct media_entity_operations imx290_subdev_entity_ops = {
 static int imx290_subdev_init(struct imx290 *imx290)
 {
 	struct i2c_client *client = to_i2c_client(imx290->dev);
+	const struct v4l2_mbus_framefmt *format;
+	struct v4l2_subdev_state *state;
 	int ret;
 
-	/*
-	 * Initialize the frame format. In particular, imx290->current_mode
-	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
-	 * below relies on these fields.
-	 */
-	imx290_entity_init_cfg(&imx290->sd, NULL);
-
-	ret = imx290_ctrl_init(imx290);
-	if (ret < 0) {
-		dev_err(imx290->dev, "Control initialization error %d\n", ret);
-		return ret;
-	}
+	imx290->current_mode = &imx290_modes_ptr(imx290)[0];
+	imx290->bpp = imx290_formats[0].bpp;
 
 	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
 	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -1032,6 +995,22 @@ static int imx290_subdev_init(struct imx290 *imx290)
 		return ret;
 	}
 
+	ret = imx290_ctrl_init(imx290);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Control initialization error %d\n", ret);
+		media_entity_cleanup(&imx290->sd.entity);
+		return ret;
+	}
+
+	imx290->sd.state_lock = imx290->ctrls.lock;
+
+	v4l2_subdev_init_finalize(&imx290->sd);
+
+	state = v4l2_subdev_lock_and_get_active_state(&imx290->sd);
+	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
+	imx290_ctrl_update(imx290, format, imx290->current_mode);
+	v4l2_subdev_unlock_state(state);
+
 	return 0;
 }
 
@@ -1268,12 +1247,10 @@ static int imx290_probe(struct i2c_client *client)
 	if (ret)
 		return ret;
 
-	mutex_init(&imx290->lock);
-
 	/* Initialize and register subdev. */
 	ret = imx290_subdev_init(imx290);
 	if (ret)
-		goto err_mutex;
+		return ret;
 
 	ret = v4l2_async_register_subdev(&imx290->sd);
 	if (ret < 0) {
@@ -1305,8 +1282,6 @@ static int imx290_probe(struct i2c_client *client)
 
 err_subdev:
 	imx290_subdev_cleanup(imx290);
-err_mutex:
-	mutex_destroy(&imx290->lock);
 
 	return ret;
 }
@@ -1319,8 +1294,6 @@ static void imx290_remove(struct i2c_client *client)
 	v4l2_async_unregister_subdev(sd);
 	imx290_subdev_cleanup(imx290);
 
-	mutex_destroy(&imx290->lock);
-
 	pm_runtime_disable(imx290->dev);
 	if (!pm_runtime_status_suspended(imx290->dev))
 		imx290_power_off(imx290->dev);
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 12/15] media: i2c: imx290: Rename, extend and expand usage of imx290_pixfmt
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (10 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 11/15] media: i2c: imx290: Use V4L2 subdev active state Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  8:53   ` Alexander Stein
  2022-11-22 22:32 ` [PATCH v1 13/15] media: i2c: imx290: Use runtime PM autosuspend Laurent Pinchart
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

The imx290_pixfmt structure contains information about formats,
currently limited to the bpp value. Extend it with the register settings
for each format, and rename it to imx290_format_info to make its purpose
clearer. Add a function named imx290_format_info() to look up format
info for a media bus code, and use it through the code. This allows
dropping the imx290 bpp field as the value is now looked up dynamically.

The error handling in imx290_setup_format() can also be dropped, as the
format is guaranteed by imx290_set_fmt() to be valid.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 87 ++++++++++++++++++++------------------
 1 file changed, 45 insertions(+), 42 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index cd2154983341..d279d6dd0cb4 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -182,7 +182,6 @@ struct imx290 {
 	struct media_pad pad;
 
 	const struct imx290_mode *current_mode;
-	u8 bpp;
 
 	struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
 	struct gpio_desc *rst_gpio;
@@ -414,16 +413,41 @@ static inline int imx290_modes_num(const struct imx290 *imx290)
 		return ARRAY_SIZE(imx290_modes_4lanes);
 }
 
-struct imx290_pixfmt {
+struct imx290_format_info {
 	u32 code;
 	u8 bpp;
+	const struct imx290_regval *regs;
+	unsigned int num_regs;
 };
 
-static const struct imx290_pixfmt imx290_formats[] = {
-	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
-	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
+static const struct imx290_format_info imx290_formats[] = {
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
+		.bpp = 10,
+		.regs = imx290_10bit_settings,
+		.num_regs = ARRAY_SIZE(imx290_10bit_settings),
+	}, {
+		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
+		.bpp = 12,
+		.regs = imx290_12bit_settings,
+		.num_regs = ARRAY_SIZE(imx290_12bit_settings),
+	}
 };
 
+static const struct imx290_format_info *imx290_format_info(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) {
+		const struct imx290_format_info *info = &imx290_formats[i];
+
+		if (info->code == code)
+			return info;
+	}
+
+	return NULL;
+}
+
 /* -----------------------------------------------------------------------------
  * Register access
  */
@@ -516,43 +540,31 @@ static int imx290_set_data_lanes(struct imx290 *imx290)
 }
 
 static int imx290_set_black_level(struct imx290 *imx290,
+				  const struct v4l2_mbus_framefmt *format,
 				  unsigned int black_level, int *err)
 {
+	unsigned int bpp = imx290_format_info(format->code)->bpp;
+
 	return imx290_write(imx290, IMX290_BLKLEVEL,
-			    black_level >> (16 - imx290->bpp), err);
+			    black_level >> (16 - bpp), err);
 }
 
 static int imx290_setup_format(struct imx290 *imx290,
 			       const struct v4l2_mbus_framefmt *format)
 {
-	const struct imx290_regval *regs;
-	unsigned int num_regs;
-	unsigned int bpp;
+	const struct imx290_format_info *info;
 	int ret;
 
-	switch (format->code) {
-	case MEDIA_BUS_FMT_SRGGB10_1X10:
-		regs = imx290_10bit_settings;
-		num_regs = ARRAY_SIZE(imx290_10bit_settings);
-		bpp = 10;
-		break;
-	case MEDIA_BUS_FMT_SRGGB12_1X12:
-		regs = imx290_12bit_settings;
-		num_regs = ARRAY_SIZE(imx290_12bit_settings);
-		bpp = 12;
-		break;
-	default:
-		dev_err(imx290->dev, "Unknown pixel format\n");
-		return -EINVAL;
-	}
+	info = imx290_format_info(format->code);
 
-	ret = imx290_set_register_array(imx290, regs, num_regs);
+	ret = imx290_set_register_array(imx290, info->regs, info->num_regs);
 	if (ret < 0) {
 		dev_err(imx290->dev, "Could not set format registers\n");
 		return ret;
 	}
 
-	return imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT, &ret);
+	return imx290_set_black_level(imx290, format,
+				      IMX290_BLACK_LEVEL_DEFAULT, &ret);
 }
 
 /* ----------------------------------------------------------------------------
@@ -586,7 +598,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 
 	case V4L2_CID_TEST_PATTERN:
 		if (ctrl->val) {
-			imx290_set_black_level(imx290, 0, &ret);
+			imx290_set_black_level(imx290, format, 0, &ret);
 			usleep_range(10000, 11000);
 			imx290_write(imx290, IMX290_PGCTRL,
 				     (u8)(IMX290_PGCTRL_REGEN |
@@ -595,8 +607,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
 		} else {
 			imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
 			usleep_range(10000, 11000);
-			imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT,
-					       &ret);
+			imx290_set_black_level(imx290, format,
+					       IMX290_BLACK_LEVEL_DEFAULT, &ret);
 		}
 		break;
 	default:
@@ -635,7 +647,7 @@ static void imx290_ctrl_update(struct imx290 *imx290,
 
 	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
 	pixel_rate = link_freq * 2 * imx290->nlanes;
-	do_div(pixel_rate, imx290->bpp);
+	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);
@@ -837,8 +849,7 @@ static int imx290_enum_frame_size(struct v4l2_subdev *sd,
 	const struct imx290 *imx290 = to_imx290(sd);
 	const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290);
 
-	if ((fse->code != imx290_formats[0].code) &&
-	    (fse->code != imx290_formats[1].code))
+	if (!imx290_format_info(fse->code))
 		return -EINVAL;
 
 	if (fse->index >= imx290_modes_num(imx290))
@@ -859,7 +870,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
 	struct imx290 *imx290 = to_imx290(sd);
 	const struct imx290_mode *mode;
 	struct v4l2_mbus_framefmt *format;
-	unsigned int i;
 
 	mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
 				      imx290_modes_num(imx290), width, height,
@@ -868,21 +878,15 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
 	fmt->format.width = mode->width;
 	fmt->format.height = mode->height;
 
-	for (i = 0; i < ARRAY_SIZE(imx290_formats); i++)
-		if (imx290_formats[i].code == fmt->format.code)
-			break;
+	if (!imx290_format_info(fmt->format.code))
+		fmt->format.code = imx290_formats[0].code;
 
-	if (i >= ARRAY_SIZE(imx290_formats))
-		i = 0;
-
-	fmt->format.code = imx290_formats[i].code;
 	fmt->format.field = V4L2_FIELD_NONE;
 
 	format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
 		imx290->current_mode = mode;
-		imx290->bpp = imx290_formats[i].bpp;
 
 		imx290_ctrl_update(imx290, &fmt->format, mode);
 	}
@@ -980,7 +984,6 @@ static int imx290_subdev_init(struct imx290 *imx290)
 	int ret;
 
 	imx290->current_mode = &imx290_modes_ptr(imx290)[0];
-	imx290->bpp = imx290_formats[0].bpp;
 
 	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
 	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 13/15] media: i2c: imx290: Use runtime PM autosuspend
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (11 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 12/15] media: i2c: imx290: Rename, extend and expand usage of imx290_pixfmt Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-22 22:32 ` [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time Laurent Pinchart
  2022-11-22 22:32 ` [PATCH v1 15/15] media: i2c: imx290: Simplify imx290_set_data_lanes() Laurent Pinchart
  14 siblings, 0 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

Use runtime PM autosuspend to avoid powering off the sensor during fast
stop-reconfigure-restart cycles. This also fixes runtime PM handling in
the probe function that didn't suspend the device, effectively leaving
it resumed forever.

While at it, improve documentation of power management in probe() and
remove().

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 55 ++++++++++++++++++++++++++++----------
 1 file changed, 41 insertions(+), 14 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index d279d6dd0cb4..dbed703fa199 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -817,12 +817,13 @@ static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
 		ret = imx290_start_streaming(imx290, state);
 		if (ret) {
 			dev_err(imx290->dev, "Start stream failed\n");
-			pm_runtime_put(imx290->dev);
+			pm_runtime_put_sync(imx290->dev);
 			goto unlock;
 		}
 	} else {
 		imx290_stop_streaming(imx290);
-		pm_runtime_put(imx290->dev);
+		pm_runtime_mark_last_busy(imx290->dev);
+		pm_runtime_put_autosuspend(imx290->dev);
 	}
 
 unlock:
@@ -1250,18 +1251,16 @@ static int imx290_probe(struct i2c_client *client)
 	if (ret)
 		return ret;
 
-	/* Initialize and register subdev. */
+	/* Initialize the V4L2 subdev. */
 	ret = imx290_subdev_init(imx290);
 	if (ret)
 		return ret;
 
-	ret = v4l2_async_register_subdev(&imx290->sd);
-	if (ret < 0) {
-		dev_err(dev, "Could not register v4l2 device\n");
-		goto err_subdev;
-	}
-
-	/* Power on the device to match runtime PM state below */
+	/*
+	 * Enable power management. The driver supports runtime PM, but needs to
+	 * work when runtime PM is disabled in the kernel. To that end, power
+	 * the sensor on manually here, identify it, and fully initialize it.
+	 */
 	ret = imx290_power_on(dev);
 	if (ret < 0) {
 		dev_err(dev, "Could not power on the device\n");
@@ -1271,21 +1270,45 @@ static int imx290_probe(struct i2c_client *client)
 	ret = imx290_read(imx290, IMX290_CHIP_ID, &chip_id);
 	if (ret) {
 		dev_err(dev, "Could not read chip ID: %d\n", ret);
-		imx290_power_off(dev);
-		goto err_subdev;
+		goto err_power;
 	}
 
 	dev_info(dev, "chip ID 0x%04x\n", chip_id);
 
+	/*
+	 * Enable runtime PM. As the device has been powered manually, mark it
+	 * as active, and increase the usage count without resuming the device.
+	 */
 	pm_runtime_set_active(dev);
+	pm_runtime_get_noresume(dev);
 	pm_runtime_enable(dev);
-	pm_runtime_idle(dev);
+
+	/* Register the V4L2 subdev. */
+	ret = v4l2_async_register_subdev(&imx290->sd);
+	if (ret < 0) {
+		dev_err(dev, "Could not register v4l2 device\n");
+		goto err_pm;
+	}
+
+	/*
+	 * Finally, enable autosuspend and decrease the usage count. The device
+	 * will get suspended after the autosuspend delay, turning the power
+	 * off.
+	 */
+	pm_runtime_set_autosuspend_delay(dev, 1000);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
 
 	return 0;
 
+err_pm:
+	pm_runtime_disable(dev);
+	pm_runtime_put_noidle(dev);
+err_power:
+	imx290_power_off(dev);
 err_subdev:
 	imx290_subdev_cleanup(imx290);
-
 	return ret;
 }
 
@@ -1297,6 +1320,10 @@ static void imx290_remove(struct i2c_client *client)
 	v4l2_async_unregister_subdev(sd);
 	imx290_subdev_cleanup(imx290);
 
+	/*
+	 * Disable runtime PM. In case runtime PM is disabled in the kernel,
+	 * make sure to turn power off manually.
+	 */
 	pm_runtime_disable(imx290->dev);
 	if (!pm_runtime_status_suspended(imx290->dev))
 		imx290_power_off(imx290->dev);
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (12 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 13/15] media: i2c: imx290: Use runtime PM autosuspend Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  9:01   ` Alexander Stein
  2022-11-23 10:16   ` Dave Stevenson
  2022-11-22 22:32 ` [PATCH v1 15/15] media: i2c: imx290: Simplify imx290_set_data_lanes() Laurent Pinchart
  14 siblings, 2 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

There's no need to configure the data lanes in the runtime PM resume
handler. Do so in imx290_start_streaming() instead.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index dbed703fa199..4dfa090f918d 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -753,6 +753,9 @@ static int imx290_start_streaming(struct imx290 *imx290,
 		return ret;
 	}
 
+	/* Set data lane count */
+	imx290_set_data_lanes(imx290);
+
 	/* 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);
@@ -1052,9 +1055,6 @@ static int imx290_power_on(struct device *dev)
 	gpiod_set_value_cansleep(imx290->rst_gpio, 0);
 	usleep_range(30000, 31000);
 
-	/* Set data lane count */
-	imx290_set_data_lanes(imx290);
-
 	return 0;
 }
 
-- 
Regards,

Laurent Pinchart


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

* [PATCH v1 15/15] media: i2c: imx290: Simplify imx290_set_data_lanes()
  2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
                   ` (13 preceding siblings ...)
  2022-11-22 22:32 ` [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time Laurent Pinchart
@ 2022-11-22 22:32 ` Laurent Pinchart
  2022-11-23  9:04   ` Alexander Stein
  14 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-22 22:32 UTC (permalink / raw)
  To: linux-media; +Cc: Sakari Ailus, Manivannan Sadhasivam

There's no need to check for an incorrect number of data lanes in
imx290_set_data_lanes() as the value is validated at probe() time. Drop
the check.

The PHY_LANE_NUM and CSI_LANE_MODE registers are programmed with a value
equal to the number of lanes minus one. Compute it instead of handling
it in the switch/case.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/i2c/imx290.c | 17 +++++------------
 1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 4dfa090f918d..369db35a7afd 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -512,28 +512,21 @@ static int imx290_set_register_array(struct imx290 *imx290,
 
 static int imx290_set_data_lanes(struct imx290 *imx290)
 {
-	int ret = 0, laneval, frsel;
+	int ret = 0;
+	u32 frsel;
 
 	switch (imx290->nlanes) {
 	case 2:
-		laneval = 0x01;
+	default:
 		frsel = 0x02;
 		break;
 	case 4:
-		laneval = 0x03;
 		frsel = 0x01;
 		break;
-	default:
-		/*
-		 * We should never hit this since the data lane count is
-		 * validated in probe itself
-		 */
-		dev_err(imx290->dev, "Lane configuration not supported\n");
-		return -EINVAL;
 	}
 
-	imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
-	imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
+	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);
 
 	return ret;
-- 
Regards,

Laurent Pinchart


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

* Re: [PATCH v1 02/15] media: i2c: imx290: Factor out subdev init and cleanup to functions
  2022-11-22 22:32 ` [PATCH v1 02/15] media: i2c: imx290: Factor out subdev init and cleanup to functions Laurent Pinchart
@ 2022-11-23  7:44   ` Alexander Stein
  2022-11-23 10:04     ` Laurent Pinchart
  2022-11-24 18:31   ` Dave Stevenson
  1 sibling, 1 reply; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  7:44 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:37 CET schrieb Laurent Pinchart:
> The probe() function is large. Make it more readable by factoring the
> subdev initialization code out. While at it, rename the error labels as
> the "free_" prefix isn't accurate.
> 
> No functional change intended.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 109 +++++++++++++++++++++----------------
>  1 file changed, 62 insertions(+), 47 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index aae4f51941a1..4dbf218e7a63 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -1015,6 +1015,46 @@ static const struct media_entity_operations
> imx290_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate,
>  };
> 
> +static int imx290_subdev_init(struct imx290 *imx290)
> +{
> +	struct i2c_client *client = to_i2c_client(imx290->dev);
> +	int ret;
> +
> +	/*
> +	 * Initialize the frame format. In particular, imx290->current_mode
> +	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() 
call
> +	 * below relies on these fields.
> +	 */
> +	imx290_entity_init_cfg(&imx290->sd, NULL);
> +
> +	ret = imx290_ctrl_init(imx290);
> +	if (ret < 0) {
> +		dev_err(imx290->dev, "Control initialization error %d\n", 
ret);
> +		return ret;
> +	}
> +
> +	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> +	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	imx290->sd.dev = imx290->dev;
> +	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
> +	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +
> +	imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
> +	if (ret < 0) {
> +		dev_err(imx290->dev, "Could not register media entity\n");
> +		return ret;

AFAICS in this error path v4l2_ctrl_handler_free is not called later on, 
because imx290_subdev_cleanup is skipped due to goto err_mutex.

Regards,
Alexander

> +	}
> +
> +	return 0;
> +}
> +
> +static void imx290_subdev_cleanup(struct imx290 *imx290)
> +{
> +	media_entity_cleanup(&imx290->sd.entity);
> +	v4l2_ctrl_handler_free(&imx290->ctrls);
> +}
> +
>  /*
> ---------------------------------------------------------------------------
> - * Power management
>   */
> @@ -1148,10 +1188,10 @@ static int imx290_probe(struct i2c_client *client)
>  	fwnode_handle_put(endpoint);
>  	if (ret == -ENXIO) {
>  		dev_err(dev, "Unsupported bus type, should be CSI2\n");
> -		goto free_err;
> +		goto err_endpoint;
>  	} else if (ret) {
>  		dev_err(dev, "Parsing endpoint node failed\n");
> -		goto free_err;
> +		goto err_endpoint;
>  	}
> 
>  	/* Get number of data lanes */
> @@ -1159,7 +1199,7 @@ static int imx290_probe(struct i2c_client *client)
>  	if (imx290->nlanes != 2 && imx290->nlanes != 4) {
>  		dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
>  		ret = -EINVAL;
> -		goto free_err;
> +		goto err_endpoint;
>  	}
> 
>  	dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
> @@ -1167,7 +1207,7 @@ static int imx290_probe(struct i2c_client *client)
>  	if (!ep.nr_of_link_frequencies) {
>  		dev_err(dev, "link-frequency property not found in DT\n");
>  		ret = -EINVAL;
> -		goto free_err;
> +		goto err_endpoint;
>  	}
> 
>  	/* Check that link frequences for all the modes are in device tree 
*/
> @@ -1175,7 +1215,7 @@ static int imx290_probe(struct i2c_client *client)
>  	if (fq) {
>  		dev_err(dev, "Link frequency of %lld is not supported\n", 
fq);
>  		ret = -EINVAL;
> -		goto free_err;
> +		goto err_endpoint;
>  	}
> 
>  	/* get system clock (xclk) */
> @@ -1183,14 +1223,14 @@ static int imx290_probe(struct i2c_client *client)
>  	if (IS_ERR(imx290->xclk)) {
>  		dev_err(dev, "Could not get xclk");
>  		ret = PTR_ERR(imx290->xclk);
> -		goto free_err;
> +		goto err_endpoint;
>  	}
> 
>  	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
>  				       &xclk_freq);
>  	if (ret) {
>  		dev_err(dev, "Could not get xclk frequency\n");
> -		goto free_err;
> +		goto err_endpoint;
>  	}
> 
>  	/* external clock must be 37.125 MHz */
> @@ -1198,19 +1238,19 @@ static int imx290_probe(struct i2c_client *client)
>  		dev_err(dev, "External clock frequency %u is not 
supported\n",
>  			xclk_freq);
>  		ret = -EINVAL;
> -		goto free_err;
> +		goto err_endpoint;
>  	}
> 
>  	ret = clk_set_rate(imx290->xclk, xclk_freq);
>  	if (ret) {
>  		dev_err(dev, "Could not set xclk frequency\n");
> -		goto free_err;
> +		goto err_endpoint;
>  	}
> 
>  	ret = imx290_get_regulators(dev, imx290);
>  	if (ret < 0) {
>  		dev_err(dev, "Cannot get regulators\n");
> -		goto free_err;
> +		goto err_endpoint;
>  	}
> 
>  	imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
> @@ -1218,55 +1258,33 @@ static int imx290_probe(struct i2c_client *client)
>  	if (IS_ERR(imx290->rst_gpio)) {
>  		dev_err(dev, "Cannot get reset gpio\n");
>  		ret = PTR_ERR(imx290->rst_gpio);
> -		goto free_err;
> +		goto err_endpoint;
>  	}
> 
>  	mutex_init(&imx290->lock);
> 
> -	/*
> -	 * Initialize the frame format. In particular, imx290->current_mode
> -	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() 
call
> -	 * below relies on these fields.
> -	 */
> -	imx290_entity_init_cfg(&imx290->sd, NULL);
> -
> -	ret = imx290_ctrl_init(imx290);
> -	if (ret < 0) {
> -		dev_err(dev, "Control initialization error %d\n", ret);
> -		goto free_mutex;
> -	}
> -
> -	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> -	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> -	imx290->sd.dev = &client->dev;
> -	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
> -	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> -
> -	imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
> -	ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
> -	if (ret < 0) {
> -		dev_err(dev, "Could not register media entity\n");
> -		goto free_ctrl;
> -	}
> +	ret = imx290_subdev_init(imx290);
> +	if (ret)
> +		goto err_mutex;
> 
>  	ret = v4l2_async_register_subdev(&imx290->sd);
>  	if (ret < 0) {
>  		dev_err(dev, "Could not register v4l2 device\n");
> -		goto free_entity;
> +		goto err_subdev;
>  	}
> 
>  	/* Power on the device to match runtime PM state below */
>  	ret = imx290_power_on(dev);
>  	if (ret < 0) {
>  		dev_err(dev, "Could not power on the device\n");
> -		goto free_entity;
> +		goto err_subdev;
>  	}
> 
>  	ret = imx290_read(imx290, IMX290_CHIP_ID, &chip_id);
>  	if (ret) {
>  		dev_err(dev, "Could not read chip ID: %d\n", ret);
>  		imx290_power_off(dev);
> -		goto free_entity;
> +		goto err_subdev;
>  	}
> 
>  	dev_info(dev, "chip ID 0x%04x\n", chip_id);
> @@ -1279,13 +1297,11 @@ static int imx290_probe(struct i2c_client *client)
> 
>  	return 0;
> 
> -free_entity:
> -	media_entity_cleanup(&imx290->sd.entity);
> -free_ctrl:
> -	v4l2_ctrl_handler_free(&imx290->ctrls);
> -free_mutex:
> +err_subdev:
> +	imx290_subdev_cleanup(imx290);
> +err_mutex:
>  	mutex_destroy(&imx290->lock);
> -free_err:
> +err_endpoint:
>  	v4l2_fwnode_endpoint_free(&ep);
> 
>  	return ret;
> @@ -1297,8 +1313,7 @@ static void imx290_remove(struct i2c_client *client)
>  	struct imx290 *imx290 = to_imx290(sd);
> 
>  	v4l2_async_unregister_subdev(sd);
> -	media_entity_cleanup(&sd->entity);
> -	v4l2_ctrl_handler_free(sd->ctrl_handler);
> +	imx290_subdev_cleanup(imx290);
> 
>  	mutex_destroy(&imx290->lock);





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

* Re: [PATCH v1 01/15] media: i2c: imx290: Group functions in sections
  2022-11-22 22:32 ` [PATCH v1 01/15] media: i2c: imx290: Group functions in sections Laurent Pinchart
@ 2022-11-23  7:46   ` Alexander Stein
  0 siblings, 0 replies; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  7:46 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:36 CET schrieb Laurent Pinchart:
> Move functions around to group them in logical sections, in order to
> improve readability. As a result, the IMX290_NUM_SUPPLIES macro has to
> be changed. No other code change is included, only moves.

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

> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 626 +++++++++++++++++++------------------
>  1 file changed, 325 insertions(+), 301 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 9954ad7376f7..aae4f51941a1 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -152,13 +152,7 @@
>  #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH		1920
>  #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT		1080
> 
> -static const char * const imx290_supply_name[] = {
> -	"vdda",
> -	"vddd",
> -	"vdddo",
> -};
> -
> -#define IMX290_NUM_SUPPLIES ARRAY_SIZE(imx290_supply_name)
> +#define IMX290_NUM_SUPPLIES				3
> 
>  struct imx290_regval {
>  	u32 reg;
> @@ -199,31 +193,14 @@ struct imx290 {
>  	struct mutex lock;
>  };
> 
> -struct imx290_pixfmt {
> -	u32 code;
> -	u8 bpp;
> -};
> +static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> +{
> +	return container_of(_sd, struct imx290, sd);
> +}
> 
> -static const struct imx290_pixfmt imx290_formats[] = {
> -	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
> -	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
> -};
> -
> -static const struct regmap_config imx290_regmap_config = {
> -	.reg_bits = 16,
> -	.val_bits = 8,
> -};
> -
> -static const char * const imx290_test_pattern_menu[] = {
> -	"Disabled",
> -	"Sequence Pattern 1",
> -	"Horizontal Color-bar Chart",
> -	"Vertical Color-bar Chart",
> -	"Sequence Pattern 2",
> -	"Gradation Pattern 1",
> -	"Gradation Pattern 2",
> -	"000/555h Toggle Pattern",
> -};
> +/*
> ---------------------------------------------------------------------------
> -- + * Modes and formats
> + */
> 
>  static const struct imx290_regval imx290_global_init_settings[] = {
>  	{ IMX290_CTRL_07, IMX290_WINMODE_1080P },
> @@ -438,10 +415,19 @@ static inline int imx290_modes_num(const struct imx290
> *imx290) return ARRAY_SIZE(imx290_modes_4lanes);
>  }
> 
> -static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> -{
> -	return container_of(_sd, struct imx290, sd);
> -}
> +struct imx290_pixfmt {
> +	u32 code;
> +	u8 bpp;
> +};
> +
> +static const struct imx290_pixfmt imx290_formats[] = {
> +	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
> +	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
> +};
> +
> +/*
> ---------------------------------------------------------------------------
> -- + * Register access
> + */
> 
>  static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32
> *value) {
> @@ -501,18 +487,94 @@ static int imx290_set_register_array(struct imx290
> *imx290, return 0;
>  }
> 
> -/* Stop streaming */
> -static int imx290_stop_streaming(struct imx290 *imx290)
> +static int imx290_set_data_lanes(struct imx290 *imx290)
>  {
> -	int ret = 0;
> +	int ret = 0, laneval, frsel;
> 
> -	imx290_write(imx290, IMX290_STANDBY, 0x01, &ret);
> +	switch (imx290->nlanes) {
> +	case 2:
> +		laneval = 0x01;
> +		frsel = 0x02;
> +		break;
> +	case 4:
> +		laneval = 0x03;
> +		frsel = 0x01;
> +		break;
> +	default:
> +		/*
> +		 * We should never hit this since the data lane count is
> +		 * validated in probe itself
> +		 */
> +		dev_err(imx290->dev, "Lane configuration not 
supported\n");
> +		return -EINVAL;
> +	}
> 
> -	msleep(30);
> +	imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
> +	imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
> +	imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
> 
> -	return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret);
> +	return ret;
>  }
> 
> +static int imx290_write_current_format(struct imx290 *imx290)
> +{
> +	int ret;
> +
> +	switch (imx290->current_format.code) {
> +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> +		ret = imx290_set_register_array(imx290, 
imx290_10bit_settings,
> +						ARRAY_SIZE(
> +							
imx290_10bit_settings));
> +		if (ret < 0) {
> +			dev_err(imx290->dev, "Could not set format 
registers\n");
> +			return ret;
> +		}
> +		break;
> +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> +		ret = imx290_set_register_array(imx290, 
imx290_12bit_settings,
> +						ARRAY_SIZE(
> +							
imx290_12bit_settings));
> +		if (ret < 0) {
> +			dev_err(imx290->dev, "Could not set format 
registers\n");
> +			return ret;
> +		}
> +		break;
> +	default:
> +		dev_err(imx290->dev, "Unknown pixel format\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static inline u8 imx290_get_link_freq_index(struct imx290 *imx290)
> +{
> +	return imx290->current_mode->link_freq_index;
> +}
> +
> +static s64 imx290_get_link_freq(struct imx290 *imx290)
> +{
> +	u8 index = imx290_get_link_freq_index(imx290);
> +
> +	return *(imx290_link_freqs_ptr(imx290) + index);
> +}
> +
> +static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
> +{
> +	s64 link_freq = imx290_get_link_freq(imx290);
> +	u8 nlanes = imx290->nlanes;
> +	u64 pixel_rate;
> +
> +	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
> +	pixel_rate = link_freq * 2 * nlanes;
> +	do_div(pixel_rate, imx290->bpp);
> +	return pixel_rate;
> +}
> +
> +/*
> ---------------------------------------------------------------------------
> - + * Controls
> + */
> +
>  static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  {
>  	struct imx290 *imx290 = container_of(ctrl->handler,
> @@ -566,6 +628,187 @@ static const struct v4l2_ctrl_ops imx290_ctrl_ops = {
>  	.s_ctrl = imx290_set_ctrl,
>  };
> 
> +static const char * const imx290_test_pattern_menu[] = {
> +	"Disabled",
> +	"Sequence Pattern 1",
> +	"Horizontal Color-bar Chart",
> +	"Vertical Color-bar Chart",
> +	"Sequence Pattern 2",
> +	"Gradation Pattern 1",
> +	"Gradation Pattern 2",
> +	"000/555h Toggle Pattern",
> +};
> +
> +static int imx290_ctrl_init(struct imx290 *imx290)
> +{
> +	struct v4l2_fwnode_device_properties props;
> +	unsigned int blank;
> +	int ret;
> +
> +	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
> +	if (ret < 0)
> +		return ret;
> +
> +	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> +	imx290->ctrls.lock = &imx290->lock;
> +
> +	/*
> +	 * The sensor has an analog gain and a digital gain, both controlled
> +	 * through a single gain value, expressed in 0.3dB increments. 
Values
> +	 * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher 
values
> +	 * up to 72.0dB (240) add further digital gain. Limit the range to
> +	 * analog gain only, support for digital gain can be added 
separately
> +	 * if needed.
> +	 *
> +	 * The IMX327 and IMX462 are largely compatible with the IMX290, but
> +	 * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital
> +	 * gain. When support for those sensors gets added to the driver, 
the
> +	 * gain control should be adjusted accordingly.
> +	 */
> +	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);
> +
> +	imx290->link_freq =
> +		v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
> +				       V4L2_CID_LINK_FREQ,
> +				       imx290_link_freqs_num(imx290) - 
1, 0,
> +				       imx290_link_freqs_ptr(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,
> +					       
imx290_calc_pixel_rate(imx290));
> +
> +	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
> +				     V4L2_CID_TEST_PATTERN,
> +				     
ARRAY_SIZE(imx290_test_pattern_menu) - 1,
> +				     0, 0, imx290_test_pattern_menu);
> +
> +	blank = imx290->current_mode->hmax - imx290->current_mode->width;
> +	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> +					   V4L2_CID_HBLANK, blank, 
blank, 1,
> +					   blank);
> +	if (imx290->hblank)
> +		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
> +	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> +					   V4L2_CID_VBLANK, blank, 
blank, 1,
> +					   blank);
> +	if (imx290->vblank)
> +		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
> +					&props);
> +
> +	imx290->sd.ctrl_handler = &imx290->ctrls;
> +
> +	if (imx290->ctrls.error) {
> +		ret = imx290->ctrls.error;
> +		v4l2_ctrl_handler_free(&imx290->ctrls);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> ---------------------------------------------------------------------------
> - + * Subdev operations
> + */
> +
> +/* Start streaming */
> +static int imx290_start_streaming(struct imx290 *imx290)
> +{
> +	int ret;
> +
> +	/* Set init register settings */
> +	ret = imx290_set_register_array(imx290, 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;
> +	}
> +
> +	/* Apply the register values related to current frame format */
> +	ret = imx290_write_current_format(imx290);
> +	if (ret < 0) {
> +		dev_err(imx290->dev, "Could not set frame format\n");
> +		return ret;
> +	}
> +
> +	/* Apply default values of current mode */
> +	ret = imx290_set_register_array(imx290, imx290->current_mode->data,
> +					imx290->current_mode-
>data_size);
> +	if (ret < 0) {
> +		dev_err(imx290->dev, "Could not set current mode\n");
> +		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) {
> +		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
> +		return ret;
> +	}
> +
> +	imx290_write(imx290, IMX290_STANDBY, 0x00, &ret);
> +
> +	msleep(30);
> +
> +	/* Start streaming */
> +	return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret);
> +}
> +
> +/* Stop streaming */
> +static int imx290_stop_streaming(struct imx290 *imx290)
> +{
> +	int ret = 0;
> +
> +	imx290_write(imx290, IMX290_STANDBY, 0x01, &ret);
> +
> +	msleep(30);
> +
> +	return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret);
> +}
> +
> +static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct imx290 *imx290 = to_imx290(sd);
> +	int ret = 0;
> +
> +	if (enable) {
> +		ret = pm_runtime_resume_and_get(imx290->dev);
> +		if (ret < 0)
> +			goto unlock_and_return;
> +
> +		ret = imx290_start_streaming(imx290);
> +		if (ret) {
> +			dev_err(imx290->dev, "Start stream failed\n");
> +			pm_runtime_put(imx290->dev);
> +			goto unlock_and_return;
> +		}
> +	} else {
> +		imx290_stop_streaming(imx290);
> +		pm_runtime_put(imx290->dev);
> +	}
> +
> +unlock_and_return:
> +
> +	return ret;
> +}
> +
>  static struct v4l2_mbus_framefmt *
>  imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state
> *state, u32 which)
> @@ -627,30 +870,6 @@ static int imx290_get_fmt(struct v4l2_subdev *sd,
>  	return 0;
>  }
> 
> -static inline u8 imx290_get_link_freq_index(struct imx290 *imx290)
> -{
> -	return imx290->current_mode->link_freq_index;
> -}
> -
> -static s64 imx290_get_link_freq(struct imx290 *imx290)
> -{
> -	u8 index = imx290_get_link_freq_index(imx290);
> -
> -	return *(imx290_link_freqs_ptr(imx290) + index);
> -}
> -
> -static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
> -{
> -	s64 link_freq = imx290_get_link_freq(imx290);
> -	u8 nlanes = imx290->nlanes;
> -	u64 pixel_rate;
> -
> -	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
> -	pixel_rate = link_freq * 2 * nlanes;
> -	do_div(pixel_rate, imx290->bpp);
> -	return pixel_rate;
> -}
> -
>  static int imx290_set_fmt(struct v4l2_subdev *sd,
>  			  struct v4l2_subdev_state *sd_state,
>  			  struct v4l2_subdev_format *fmt)
> @@ -774,151 +993,31 @@ static int imx290_entity_init_cfg(struct v4l2_subdev
> *subdev, return 0;
>  }
> 
> -static int imx290_write_current_format(struct imx290 *imx290)
> -{
> -	int ret;
> -
> -	switch (imx290->current_format.code) {
> -	case MEDIA_BUS_FMT_SRGGB10_1X10:
> -		ret = imx290_set_register_array(imx290, 
imx290_10bit_settings,
> -						ARRAY_SIZE(
> -							
imx290_10bit_settings));
> -		if (ret < 0) {
> -			dev_err(imx290->dev, "Could not set format 
registers\n");
> -			return ret;
> -		}
> -		break;
> -	case MEDIA_BUS_FMT_SRGGB12_1X12:
> -		ret = imx290_set_register_array(imx290, 
imx290_12bit_settings,
> -						ARRAY_SIZE(
> -							
imx290_12bit_settings));
> -		if (ret < 0) {
> -			dev_err(imx290->dev, "Could not set format 
registers\n");
> -			return ret;
> -		}
> -		break;
> -	default:
> -		dev_err(imx290->dev, "Unknown pixel format\n");
> -		return -EINVAL;
> -	}
> -
> -	return 0;
> -}
> -
> -/* Start streaming */
> -static int imx290_start_streaming(struct imx290 *imx290)
> -{
> -	int ret;
> -
> -	/* Set init register settings */
> -	ret = imx290_set_register_array(imx290, 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;
> -	}
> -
> -	/* Apply the register values related to current frame format */
> -	ret = imx290_write_current_format(imx290);
> -	if (ret < 0) {
> -		dev_err(imx290->dev, "Could not set frame format\n");
> -		return ret;
> -	}
> -
> -	/* Apply default values of current mode */
> -	ret = imx290_set_register_array(imx290, imx290->current_mode->data,
> -					imx290->current_mode-
>data_size);
> -	if (ret < 0) {
> -		dev_err(imx290->dev, "Could not set current mode\n");
> -		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) {
> -		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
> -		return ret;
> -	}
> -
> -	imx290_write(imx290, IMX290_STANDBY, 0x00, &ret);
> -
> -	msleep(30);
> -
> -	/* Start streaming */
> -	return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret);
> -}
> -
> -static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
> -{
> -	struct imx290 *imx290 = to_imx290(sd);
> -	int ret = 0;
> -
> -	if (enable) {
> -		ret = pm_runtime_resume_and_get(imx290->dev);
> -		if (ret < 0)
> -			goto unlock_and_return;
> -
> -		ret = imx290_start_streaming(imx290);
> -		if (ret) {
> -			dev_err(imx290->dev, "Start stream failed\n");
> -			pm_runtime_put(imx290->dev);
> -			goto unlock_and_return;
> -		}
> -	} else {
> -		imx290_stop_streaming(imx290);
> -		pm_runtime_put(imx290->dev);
> -	}
> -
> -unlock_and_return:
> -
> -	return ret;
> -}
> -
> -static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
> -{
> -	unsigned int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++)
> -		imx290->supplies[i].supply = imx290_supply_name[i];
> -
> -	return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies),
> -				       imx290->supplies);
> -}
> -
> -static int imx290_set_data_lanes(struct imx290 *imx290)
> -{
> -	int ret = 0, laneval, frsel;
> -
> -	switch (imx290->nlanes) {
> -	case 2:
> -		laneval = 0x01;
> -		frsel = 0x02;
> -		break;
> -	case 4:
> -		laneval = 0x03;
> -		frsel = 0x01;
> -		break;
> -	default:
> -		/*
> -		 * We should never hit this since the data lane count is
> -		 * validated in probe itself
> -		 */
> -		dev_err(imx290->dev, "Lane configuration not 
supported\n");
> -		return -EINVAL;
> -	}
> -
> -	imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
> -	imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
> -	imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret);
> -
> -	return ret;
> -}
> +static const struct v4l2_subdev_video_ops imx290_video_ops = {
> +	.s_stream = imx290_set_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
> +	.init_cfg = imx290_entity_init_cfg,
> +	.enum_mbus_code = imx290_enum_mbus_code,
> +	.enum_frame_size = imx290_enum_frame_size,
> +	.get_fmt = imx290_get_fmt,
> +	.set_fmt = imx290_set_fmt,
> +	.get_selection = imx290_get_selection,
> +};
> +
> +static const struct v4l2_subdev_ops imx290_subdev_ops = {
> +	.video = &imx290_video_ops,
> +	.pad = &imx290_pad_ops,
> +};
> +
> +static const struct media_entity_operations imx290_subdev_entity_ops = {
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +/*
> ---------------------------------------------------------------------------
> - + * Power management
> + */
> 
>  static int imx290_power_on(struct device *dev)
>  {
> @@ -966,105 +1065,30 @@ static const struct dev_pm_ops imx290_pm_ops = {
>  	SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL)
>  };
> 
> -static const struct v4l2_subdev_video_ops imx290_video_ops = {
> -	.s_stream = imx290_set_stream,
> -};
> -
> -static const struct v4l2_subdev_pad_ops imx290_pad_ops = {
> -	.init_cfg = imx290_entity_init_cfg,
> -	.enum_mbus_code = imx290_enum_mbus_code,
> -	.enum_frame_size = imx290_enum_frame_size,
> -	.get_fmt = imx290_get_fmt,
> -	.set_fmt = imx290_set_fmt,
> -	.get_selection = imx290_get_selection,
> -};
> +/*
> ---------------------------------------------------------------------------
> - + * Probe & remove
> + */
> 
> -static const struct v4l2_subdev_ops imx290_subdev_ops = {
> -	.video = &imx290_video_ops,
> -	.pad = &imx290_pad_ops,
> +static const struct regmap_config imx290_regmap_config = {
> +	.reg_bits = 16,
> +	.val_bits = 8,
>  };
> 
> -static const struct media_entity_operations imx290_subdev_entity_ops = {
> -	.link_validate = v4l2_subdev_link_validate,
> +static const char * const imx290_supply_name[IMX290_NUM_SUPPLIES] = {
> +	"vdda",
> +	"vddd",
> +	"vdddo",
>  };
> 
> -static int imx290_ctrl_init(struct imx290 *imx290)
> +static int imx290_get_regulators(struct device *dev, struct imx290 *imx290)
> {
> -	struct v4l2_fwnode_device_properties props;
> -	unsigned int blank;
> -	int ret;
> +	unsigned int i;
> 
> -	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
> -	if (ret < 0)
> -		return ret;
> +	for (i = 0; i < ARRAY_SIZE(imx290->supplies); i++)
> +		imx290->supplies[i].supply = imx290_supply_name[i];
> 
> -	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> -	imx290->ctrls.lock = &imx290->lock;
> -
> -	/*
> -	 * The sensor has an analog gain and a digital gain, both controlled
> -	 * through a single gain value, expressed in 0.3dB increments. 
Values
> -	 * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher 
values
> -	 * up to 72.0dB (240) add further digital gain. Limit the range to
> -	 * analog gain only, support for digital gain can be added 
separately
> -	 * if needed.
> -	 *
> -	 * The IMX327 and IMX462 are largely compatible with the IMX290, but
> -	 * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital
> -	 * gain. When support for those sensors gets added to the driver, 
the
> -	 * gain control should be adjusted accordingly.
> -	 */
> -	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);
> -
> -	imx290->link_freq =
> -		v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
> -				       V4L2_CID_LINK_FREQ,
> -				       imx290_link_freqs_num(imx290) - 
1, 0,
> -				       imx290_link_freqs_ptr(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,
> -					       
imx290_calc_pixel_rate(imx290));
> -
> -	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
> -				     V4L2_CID_TEST_PATTERN,
> -				     
ARRAY_SIZE(imx290_test_pattern_menu) - 1,
> -				     0, 0, imx290_test_pattern_menu);
> -
> -	blank = imx290->current_mode->hmax - imx290->current_mode->width;
> -	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> -					   V4L2_CID_HBLANK, blank, 
blank, 1,
> -					   blank);
> -	if (imx290->hblank)
> -		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> -
> -	blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
> -	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> -					   V4L2_CID_VBLANK, blank, 
blank, 1,
> -					   blank);
> -	if (imx290->vblank)
> -		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> -
> -	v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
> -					&props);
> -
> -	imx290->sd.ctrl_handler = &imx290->ctrls;
> -
> -	if (imx290->ctrls.error) {
> -		ret = imx290->ctrls.error;
> -		v4l2_ctrl_handler_free(&imx290->ctrls);
> -		return ret;
> -	}
> -
> -	return 0;
> +	return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx290->supplies),
> +				       imx290->supplies);
>  }
> 
>  /*





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

* Re: [PATCH v1 03/15] media: i2c: imx290: Factor out control update code to a function
  2022-11-22 22:32 ` [PATCH v1 03/15] media: i2c: imx290: Factor out control update code to a function Laurent Pinchart
@ 2022-11-23  7:51   ` Alexander Stein
  2022-11-23 10:08     ` Laurent Pinchart
  0 siblings, 1 reply; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  7:51 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:38 CET schrieb Laurent Pinchart:
> Move the control update code to a separate function to group it with all
> the control-related code and make imx290_set_fmt() more readable.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 36 ++++++++++++++++--------------------
>  1 file changed, 16 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 4dbf218e7a63..eb295502d0c3 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -639,6 +639,21 @@ static const char * const imx290_test_pattern_menu[] =
> { "000/555h Toggle Pattern",
>  };
> 
> +static void imx290_ctrl_update(struct imx290 *imx290,
> +			       const struct imx290_mode *mode)
> +{
> +	unsigned int hblank = mode->hmax - mode->width;
> +	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> +
> +	__v4l2_ctrl_s_ctrl(imx290->link_freq,
> +			   imx290_get_link_freq_index(imx290));
> +	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
> +				 imx290_calc_pixel_rate(imx290));

I do not know the details, but I assume that imx290_ctrl_init already detects, 
using imx290->ctrls.error, if one of those controls have _not_ been created.
If so:
Acked-by: Alexander Stein <alexander.stein@ew.tq-group.com>

> +
> +	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
> +	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> +}
> +
>  static int imx290_ctrl_init(struct imx290 *imx290)
>  {
>  	struct v4l2_fwnode_device_properties props;
> @@ -904,26 +919,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
>  		imx290->current_mode = mode;
>  		imx290->bpp = imx290_formats[i].bpp;
> 
> -		if (imx290->link_freq)
> -			__v4l2_ctrl_s_ctrl(imx290->link_freq,
> -					   
imx290_get_link_freq_index(imx290));
> -		if (imx290->pixel_rate)
> -			__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
> -						 
imx290_calc_pixel_rate(imx290));
> -
> -		if (imx290->hblank) {
> -			unsigned int hblank = mode->hmax - mode->width;
> -
> -			__v4l2_ctrl_modify_range(imx290->hblank, hblank, 
hblank,
> -						 1, hblank);
> -		}
> -
> -		if (imx290->vblank) {
> -			unsigned int vblank = IMX290_VMAX_DEFAULT - 
mode->height;
> -
> -			__v4l2_ctrl_modify_range(imx290->vblank, vblank, 
vblank,
> -						 1, vblank);
> -		}
> +		imx290_ctrl_update(imx290, mode);
>  	}
> 
>  	*format = fmt->format;





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

* Re: [PATCH v1 04/15] media: i2c: imx290: Access link_freq_index directly
  2022-11-22 22:32 ` [PATCH v1 04/15] media: i2c: imx290: Access link_freq_index directly Laurent Pinchart
@ 2022-11-23  7:53   ` Alexander Stein
  0 siblings, 0 replies; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  7:53 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:39 CET schrieb Laurent Pinchart:
> The imx290_get_link_freq_index() function hides the fact that it relies
> on the imx290 current_mode field, which obfuscates the code instead of
> making it more readable. Inline it in the callers, and use the mode
> pointer we already have in imx290_ctrl_update() instead of using the
> current_mode field.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 10 ++--------
>  1 file changed, 2 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index eb295502d0c3..2d198b167853 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -547,14 +547,9 @@ static int imx290_write_current_format(struct imx290
> *imx290) return 0;
>  }
> 
> -static inline u8 imx290_get_link_freq_index(struct imx290 *imx290)
> -{
> -	return imx290->current_mode->link_freq_index;
> -}
> -
>  static s64 imx290_get_link_freq(struct imx290 *imx290)
>  {
> -	u8 index = imx290_get_link_freq_index(imx290);
> +	u8 index = imx290->current_mode->link_freq_index;
> 
>  	return *(imx290_link_freqs_ptr(imx290) + index);
>  }
> @@ -645,8 +640,7 @@ static void imx290_ctrl_update(struct imx290 *imx290,
>  	unsigned int hblank = mode->hmax - mode->width;
>  	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> 
> -	__v4l2_ctrl_s_ctrl(imx290->link_freq,
> -			   imx290_get_link_freq_index(imx290));
> +	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
>  	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
>  				 imx290_calc_pixel_rate(imx290));

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




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

* Re: [PATCH v1 06/15] media: i2c: imx290: Compute pixel rate and blanking in one place
  2022-11-22 22:32 ` [PATCH v1 06/15] media: i2c: imx290: Compute pixel rate and blanking in one place Laurent Pinchart
@ 2022-11-23  7:59   ` Alexander Stein
  2022-11-23  9:58   ` Dave Stevenson
  1 sibling, 0 replies; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  7:59 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:41 CET schrieb Laurent Pinchart:
> The hblank, vblank, pixel rate and link frequency values and limits are
> currently computed when creating controls, in imx290_ctrl_init(), and
> updated in imx290_ctrl_update(). This duplicates the logic in different
> places. Simplify the code by setting the control values and limits to
> hardcoded values when creating the controls, and call
> imx290_ctrl_update() to then update them.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 46 +++++++++++++++++---------------------
>  1 file changed, 20 insertions(+), 26 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index a676ce93f96e..135ed55592a1 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -547,19 +547,6 @@ static int imx290_write_current_format(struct imx290
> *imx290) return 0;
>  }
> 
> -static u64 imx290_calc_pixel_rate(struct imx290 *imx290,
> -				  const struct v4l2_mbus_framefmt 
*format,
> -				  const struct imx290_mode *mode)
> -{
> -	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->bpp);
> -	return pixel_rate;
> -}
> -
>  /*
> ---------------------------------------------------------------------------
> - * Controls
>   */
> @@ -634,10 +621,15 @@ 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->bpp);
> 
>  	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> -	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
> -				 imx290_calc_pixel_rate(imx290, 
format, mode));
> +	__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);
> @@ -646,8 +638,6 @@ static void imx290_ctrl_update(struct imx290 *imx290,
>  static int imx290_ctrl_init(struct imx290 *imx290)
>  {
>  	struct v4l2_fwnode_device_properties props;
> -	unsigned int blank;
> -	u64 pixel_rate;
>  	int ret;
> 
>  	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
> @@ -677,6 +667,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  			  V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 
1,
>  			  IMX290_VMAX_DEFAULT - 2);
> 
> +	/*
> +	 * Set the link frequency, pixel rate, horizontal blanking and 
vertical
> +	 * blanking it hardcoded values, they will be updated by
> +	 * imx290_ctrl_update().
> +	 */
>  	imx290->link_freq =
>  		v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
>  				       V4L2_CID_LINK_FREQ,
> @@ -685,28 +680,22 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  	if (imx290->link_freq)
>  		imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> 
> -	pixel_rate = imx290_calc_pixel_rate(imx290, &imx290->current_format,
> -					    imx290->current_mode);
>  	imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, 
&imx290_ctrl_ops,
>  					       V4L2_CID_PIXEL_RATE,
> -					       1, INT_MAX, 1, 
pixel_rate);
> +					       1, INT_MAX, 1, 1);
> 
>  	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
>  				     V4L2_CID_TEST_PATTERN,
>  				     
ARRAY_SIZE(imx290_test_pattern_menu) - 1,
>  				     0, 0, imx290_test_pattern_menu);
> 
> -	blank = imx290->current_mode->hmax - imx290->current_mode->width;
>  	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> -					   V4L2_CID_HBLANK, blank, 
blank, 1,
> -					   blank);
> +					   V4L2_CID_HBLANK, 1, 1, 1, 
1);
>  	if (imx290->hblank)
>  		imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> 
> -	blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
>  	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> -					   V4L2_CID_VBLANK, blank, 
blank, 1,
> -					   blank);
> +					   V4L2_CID_VBLANK, 1, 1, 1, 
1);
>  	if (imx290->vblank)
>  		imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> 
> @@ -721,6 +710,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  		return ret;
>  	}
> 
> +	mutex_lock(imx290->ctrls.lock);
> +	imx290_ctrl_update(imx290, &imx290->current_format,
> +			   imx290->current_mode);
> +	mutex_unlock(imx290->ctrls.lock);
> +
>  	return 0;
>  }

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




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

* Re: [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function
  2022-11-22 22:32 ` [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function Laurent Pinchart
@ 2022-11-23  8:03   ` Alexander Stein
  2022-11-23 10:08   ` Dave Stevenson
  1 sibling, 0 replies; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  8:03 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:42 CET schrieb Laurent Pinchart:
> The black level programmed in the BLKLEVEL register depends on the
> output format. The black level value computation is currently performed
> in imx290_set_ctrl(), in addition to having different black level values
> in the output-specific register value tables. Move it to a separate
> function to simplify the imx290_set_ctrl() code.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 53 +++++++++++++++++++++-----------------
>  1 file changed, 29 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 135ed55592a1..d9fc6c44b0f0 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -152,6 +152,9 @@
>  #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH		1920
>  #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT		1080
> 
> +/* Equivalent value for 16bpp */
> +#define IMX290_BLACK_LEVEL_DEFAULT			3840
> +
>  #define IMX290_NUM_SUPPLIES				3
> 
>  struct imx290_regval {
> @@ -315,7 +318,6 @@ static const struct imx290_regval
> imx290_10bit_settings[] = { { IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
>  	{ IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
>  	{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
> -	{ IMX290_BLKLEVEL, 60 },
>  };
> 
>  static const struct imx290_regval imx290_12bit_settings[] = {
> @@ -325,7 +327,6 @@ static const struct imx290_regval
> imx290_12bit_settings[] = { { IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
>  	{ IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
>  	{ IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
> -	{ IMX290_BLKLEVEL, 240 },
>  };
> 
>  /* supported link frequencies */
> @@ -516,35 +517,43 @@ static int imx290_set_data_lanes(struct imx290
> *imx290) return ret;
>  }
> 
> +static int imx290_set_black_level(struct imx290 *imx290,
> +				  unsigned int black_level, int *err)
> +{
> +	return imx290_write(imx290, IMX290_BLKLEVEL,
> +			    black_level >> (16 - imx290->bpp), err);
> +}
> +
>  static int imx290_write_current_format(struct imx290 *imx290)
>  {
> +	const struct imx290_regval *regs;
> +	unsigned int num_regs;
> +	unsigned int bpp;
>  	int ret;
> 
>  	switch (imx290->current_format.code) {
>  	case MEDIA_BUS_FMT_SRGGB10_1X10:
> -		ret = imx290_set_register_array(imx290, 
imx290_10bit_settings,
> -						ARRAY_SIZE(
> -							
imx290_10bit_settings));
> -		if (ret < 0) {
> -			dev_err(imx290->dev, "Could not set format 
registers\n");
> -			return ret;
> -		}
> +		regs = imx290_10bit_settings;
> +		num_regs = ARRAY_SIZE(imx290_10bit_settings);
> +		bpp = 10;
>  		break;
>  	case MEDIA_BUS_FMT_SRGGB12_1X12:
> -		ret = imx290_set_register_array(imx290, 
imx290_12bit_settings,
> -						ARRAY_SIZE(
> -							
imx290_12bit_settings));
> -		if (ret < 0) {
> -			dev_err(imx290->dev, "Could not set format 
registers\n");
> -			return ret;
> -		}
> +		regs = imx290_12bit_settings;
> +		num_regs = ARRAY_SIZE(imx290_12bit_settings);
> +		bpp = 12;
>  		break;
>  	default:
>  		dev_err(imx290->dev, "Unknown pixel format\n");
>  		return -EINVAL;
>  	}
> 
> -	return 0;
> +	ret = imx290_set_register_array(imx290, regs, num_regs);
> +	if (ret < 0) {
> +		dev_err(imx290->dev, "Could not set format registers\n");
> +		return ret;
> +	}
> +
> +	return imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT, 
&ret);
>  }
> 
>  /*
> ---------------------------------------------------------------------------
> - @@ -573,7 +582,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> 
>  	case V4L2_CID_TEST_PATTERN:
>  		if (ctrl->val) {
> -			imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
> +			imx290_set_black_level(imx290, 0, &ret);
>  			usleep_range(10000, 11000);
>  			imx290_write(imx290, IMX290_PGCTRL,
>  				     (u8)(IMX290_PGCTRL_REGEN |
> @@ -582,12 +591,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  		} else {
>  			imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
>  			usleep_range(10000, 11000);
> -			if (imx290->bpp == 10)
> -				imx290_write(imx290, IMX290_BLKLEVEL, 
0x3c,
> -					     &ret);
> -			else /* 12 bits per pixel */
> -				imx290_write(imx290, IMX290_BLKLEVEL, 
0xf0,
> -					     &ret);
> +			imx290_set_black_level(imx290, 
IMX290_BLACK_LEVEL_DEFAULT,
> +					       &ret);
>  		}
>  		break;
>  	default:

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




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

* Re: [PATCH v1 08/15] media: i2c: imx290: Factor out DT parsing to separate function
  2022-11-22 22:32 ` [PATCH v1 08/15] media: i2c: imx290: Factor out DT parsing to separate function Laurent Pinchart
@ 2022-11-23  8:14   ` Alexander Stein
  2022-11-23 10:16     ` Laurent Pinchart
  0 siblings, 1 reply; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  8:14 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:43 CET schrieb Laurent Pinchart:
> Make the probe() function more readable by factoring out the DT parsing
> code to a separate function. No functional change intended.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 97 +++++++++++++++++++++-----------------
>  1 file changed, 53 insertions(+), 44 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index d9fc6c44b0f0..655f676df3d2 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -1139,112 +1139,125 @@ static s64 imx290_check_link_freqs(const struct
> imx290 *imx290, return 0;
>  }
> 
> -static int imx290_probe(struct i2c_client *client)
> +static int imx290_parse_dt(struct imx290 *imx290)
>  {
> -	struct device *dev = &client->dev;
> -	struct fwnode_handle *endpoint;
>  	/* Only CSI2 is supported for now: */
>  	struct v4l2_fwnode_endpoint ep = {
>  		.bus_type = V4L2_MBUS_CSI2_DPHY
>  	};
> -	struct imx290 *imx290;
> -	u32 xclk_freq;
> -	u32 chip_id;

I assume your patch for reading the chip ID slipped into this series.
Either add it to the series or rearrange your patches.

> +	struct fwnode_handle *endpoint;
> +	int ret;
>  	s64 fq;
> -	int ret;
> 
> -	imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
> -	if (!imx290)
> -		return -ENOMEM;
> -
> -	imx290->dev = dev;
> -	imx290->regmap = devm_regmap_init_i2c(client, 
&imx290_regmap_config);
> -	if (IS_ERR(imx290->regmap)) {
> -		dev_err(dev, "Unable to initialize I2C\n");
> -		return -ENODEV;
> -	}
> -
> -	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
> +	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), 
NULL);
>  	if (!endpoint) {
> -		dev_err(dev, "Endpoint node not found\n");
> +		dev_err(imx290->dev, "Endpoint node not found\n");
>  		return -EINVAL;
>  	}
> 
>  	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
>  	fwnode_handle_put(endpoint);
>  	if (ret == -ENXIO) {
> -		dev_err(dev, "Unsupported bus type, should be CSI2\n");
> -		goto err_endpoint;
> +		dev_err(imx290->dev, "Unsupported bus type, should be 
CSI2\n");
> +		goto done;
>  	} else if (ret) {
> -		dev_err(dev, "Parsing endpoint node failed\n");
> -		goto err_endpoint;
> +		dev_err(imx290->dev, "Parsing endpoint node failed\n");
> +		goto done;
>  	}
> 
>  	/* Get number of data lanes */
>  	imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes;
>  	if (imx290->nlanes != 2 && imx290->nlanes != 4) {
> -		dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
> +		dev_err(imx290->dev, "Invalid data lanes: %d\n", imx290-
>nlanes);
>  		ret = -EINVAL;
> -		goto err_endpoint;
> +		goto done;
>  	}
> 
> -	dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
> +	dev_dbg(imx290->dev, "Using %u data lanes\n", imx290->nlanes);
> 
>  	if (!ep.nr_of_link_frequencies) {
> -		dev_err(dev, "link-frequency property not found in DT\n");
> +		dev_err(imx290->dev, "link-frequency property not found in 
DT\n");
>  		ret = -EINVAL;
> -		goto err_endpoint;
> +		goto done;
>  	}
> 
>  	/* Check that link frequences for all the modes are in device tree 
*/
>  	fq = imx290_check_link_freqs(imx290, &ep);
>  	if (fq) {
> -		dev_err(dev, "Link frequency of %lld is not supported\n", 
fq);
> +		dev_err(imx290->dev, "Link frequency of %lld is not 
supported\n",
> +			fq);
>  		ret = -EINVAL;
> -		goto err_endpoint;
> +		goto done;
>  	}
> 
> +	ret = 0;
> +
> +done:
> +	v4l2_fwnode_endpoint_free(&ep);
> +	return ret;
> +}
> +
> +static int imx290_probe(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct imx290 *imx290;
> +	u32 xclk_freq;
> +	u32 chip_id;
> +	int ret;
> +
> +	imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
> +	if (!imx290)
> +		return -ENOMEM;
> +
> +	imx290->dev = dev;
> +	imx290->regmap = devm_regmap_init_i2c(client, 
&imx290_regmap_config);
> +	if (IS_ERR(imx290->regmap)) {
> +		dev_err(dev, "Unable to initialize I2C\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = imx290_parse_dt(imx290);
> +	if (ret)
> +		return ret;
> +
>  	/* get system clock (xclk) */
>  	imx290->xclk = devm_clk_get(dev, "xclk");
>  	if (IS_ERR(imx290->xclk)) {
>  		dev_err(dev, "Could not get xclk");
> -		ret = PTR_ERR(imx290->xclk);
> -		goto err_endpoint;
> +		return PTR_ERR(imx290->xclk);

Mh, using dev_err_probe this can be a one-liner.

>  	}
> 
>  	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
>  				       &xclk_freq);
>  	if (ret) {
>  		dev_err(dev, "Could not get xclk frequency\n");
> -		goto err_endpoint;
> +		return ret;
>  	}
> 
>  	/* external clock must be 37.125 MHz */
>  	if (xclk_freq != 37125000) {
>  		dev_err(dev, "External clock frequency %u is not 
supported\n",
>  			xclk_freq);
> -		ret = -EINVAL;
> -		goto err_endpoint;
> +		return -EINVAL;
>  	}
> 
>  	ret = clk_set_rate(imx290->xclk, xclk_freq);
>  	if (ret) {
>  		dev_err(dev, "Could not set xclk frequency\n");
> -		goto err_endpoint;
> +		return ret;
>  	}
> 
>  	ret = imx290_get_regulators(dev, imx290);
>  	if (ret < 0) {
>  		dev_err(dev, "Cannot get regulators\n");
> -		goto err_endpoint;
> +		return ret;

Using dev_err_probe this can be a one-liner as well.

>  	}
> 
>  	imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
>  						   
GPIOD_OUT_HIGH);
>  	if (IS_ERR(imx290->rst_gpio)) {
>  		dev_err(dev, "Cannot get reset gpio\n");
> -		ret = PTR_ERR(imx290->rst_gpio);
> -		goto err_endpoint;
> +		return PTR_ERR(imx290->rst_gpio);

Using dev_err_probe this can be a one-liner as well.

Regards,
Alexander

>  	}
> 
>  	mutex_init(&imx290->lock);
> @@ -1279,16 +1292,12 @@ static int imx290_probe(struct i2c_client *client)
>  	pm_runtime_enable(dev);
>  	pm_runtime_idle(dev);
> 
> -	v4l2_fwnode_endpoint_free(&ep);
> -
>  	return 0;
> 
>  err_subdev:
>  	imx290_subdev_cleanup(imx290);
>  err_mutex:
>  	mutex_destroy(&imx290->lock);
> -err_endpoint:
> -	v4l2_fwnode_endpoint_free(&ep);
> 
>  	return ret;
>  }





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

* Re: [PATCH v1 09/15] media: i2c: imx290: Use dev_err_probe()
  2022-11-22 22:32 ` [PATCH v1 09/15] media: i2c: imx290: Use dev_err_probe() Laurent Pinchart
@ 2022-11-23  8:16   ` Alexander Stein
  0 siblings, 0 replies; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  8:16 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:44 CET schrieb Laurent Pinchart:
> Improve error handling in the probe() function with dev_err_probe().
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 20 ++++++++------------
>  1 file changed, 8 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 655f676df3d2..d423860402fd 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -1222,10 +1222,9 @@ static int imx290_probe(struct i2c_client *client)
> 
>  	/* get system clock (xclk) */
>  	imx290->xclk = devm_clk_get(dev, "xclk");
> -	if (IS_ERR(imx290->xclk)) {
> -		dev_err(dev, "Could not get xclk");
> -		return PTR_ERR(imx290->xclk);
> -	}
> +	if (IS_ERR(imx290->xclk))
> +		return dev_err_probe(dev, PTR_ERR(imx290->xclk),
> +				     "Could not get xclk");
> 
>  	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
>  				       &xclk_freq);
> @@ -1248,17 +1247,14 @@ static int imx290_probe(struct i2c_client *client)
>  	}
> 
>  	ret = imx290_get_regulators(dev, imx290);
> -	if (ret < 0) {
> -		dev_err(dev, "Cannot get regulators\n");
> -		return ret;
> -	}
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Cannot get regulators\n");
> 
>  	imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
>  						   
GPIOD_OUT_HIGH);
> -	if (IS_ERR(imx290->rst_gpio)) {
> -		dev_err(dev, "Cannot get reset gpio\n");
> -		return PTR_ERR(imx290->rst_gpio);
> -	}
> +	if (IS_ERR(imx290->rst_gpio))
> +		return dev_err_probe(dev, PTR_ERR(imx290->rst_gpio),
> +				     "Cannot get reset gpio\n");
> 
>  	mutex_init(&imx290->lock);

Oh, you already addressed my dev_err_probe comments on the last patch :)
For this one:
Reviewed-by: Alexander Stein <alexander.stein@ew.tq-group.com>




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

* Re: [PATCH v1 10/15] media: i2c: imx290: Factor out clock initialization to separate function
  2022-11-22 22:32 ` [PATCH v1 10/15] media: i2c: imx290: Factor out clock initialization to separate function Laurent Pinchart
@ 2022-11-23  8:18   ` Alexander Stein
  0 siblings, 0 replies; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  8:18 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:45 CET schrieb Laurent Pinchart:
> Move the external clock initialization code from probe() to a separate
> function to improve readability. No functional change intended.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 57 +++++++++++++++++++++++---------------
>  1 file changed, 35 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index d423860402fd..848de4c90d3b 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -1117,6 +1117,34 @@ static int imx290_get_regulators(struct device *dev,
> struct imx290 *imx290) imx290->supplies);
>  }
> 
> +static int imx290_init_clk(struct imx290 *imx290)
> +{
> +	u32 xclk_freq;
> +	int ret;
> +
> +	ret = fwnode_property_read_u32(dev_fwnode(imx290->dev),
> +				       "clock-frequency", &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) {
> +		dev_err(imx290->dev, "External clock frequency %u is not 
supported\n",
> +			xclk_freq);
> +		return -EINVAL;
> +	}
> +
> +	ret = clk_set_rate(imx290->xclk, xclk_freq);
> +	if (ret) {
> +		dev_err(imx290->dev, "Could not set xclk frequency\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
>  /*
>   * Returns 0 if all link frequencies used by the driver for the given
> number * of MIPI data lanes are mentioned in the device tree, or the value
> of the @@ -1201,7 +1229,6 @@ static int imx290_probe(struct i2c_client
> *client) {
>  	struct device *dev = &client->dev;
>  	struct imx290 *imx290;
> -	u32 xclk_freq;
>  	u32 chip_id;
>  	int ret;
> 
> @@ -1220,32 +1247,12 @@ static int imx290_probe(struct i2c_client *client)
>  	if (ret)
>  		return ret;
> 
> -	/* get system clock (xclk) */
> +	/* Acquire resources. */
>  	imx290->xclk = devm_clk_get(dev, "xclk");
>  	if (IS_ERR(imx290->xclk))
>  		return dev_err_probe(dev, PTR_ERR(imx290->xclk),
>  				     "Could not get xclk");
> 
> -	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
> -				       &xclk_freq);
> -	if (ret) {
> -		dev_err(dev, "Could not get xclk frequency\n");
> -		return ret;
> -	}
> -
> -	/* external clock must be 37.125 MHz */
> -	if (xclk_freq != 37125000) {
> -		dev_err(dev, "External clock frequency %u is not 
supported\n",
> -			xclk_freq);
> -		return -EINVAL;
> -	}
> -
> -	ret = clk_set_rate(imx290->xclk, xclk_freq);
> -	if (ret) {
> -		dev_err(dev, "Could not set xclk frequency\n");
> -		return ret;
> -	}
> -
>  	ret = imx290_get_regulators(dev, imx290);
>  	if (ret < 0)
>  		return dev_err_probe(dev, ret, "Cannot get regulators\n");
> @@ -1256,8 +1263,14 @@ static int imx290_probe(struct i2c_client *client)
>  		return dev_err_probe(dev, PTR_ERR(imx290->rst_gpio),
>  				     "Cannot get reset gpio\n");
> 
> +	/* Initialize external clock frequency. */
> +	ret = imx290_init_clk(imx290);
> +	if (ret)
> +		return ret;
> +
>  	mutex_init(&imx290->lock);
> 
> +	/* Initialize and register subdev. */
>  	ret = imx290_subdev_init(imx290);
>  	if (ret)
>  		goto err_mutex;

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




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

* Re: [PATCH v1 11/15] media: i2c: imx290: Use V4L2 subdev active state
  2022-11-22 22:32 ` [PATCH v1 11/15] media: i2c: imx290: Use V4L2 subdev active state Laurent Pinchart
@ 2022-11-23  8:42   ` Alexander Stein
  2022-11-23 10:49     ` Laurent Pinchart
  0 siblings, 1 reply; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  8:42 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:46 CET schrieb Laurent Pinchart:
> Use the V4L2 subdev active state API to store the active format. This
> simplifies the driver not only by dropping the imx290 current_format
> field, but it also allows dropping the imx290 lock, replaced with the
> state lock.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 139 +++++++++++++++----------------------
>  1 file changed, 56 insertions(+), 83 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 848de4c90d3b..cd2154983341 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -177,12 +177,12 @@ struct imx290 {
>  	struct clk *xclk;
>  	struct regmap *regmap;
>  	u8 nlanes;
> -	u8 bpp;
> 
>  	struct v4l2_subdev sd;
>  	struct media_pad pad;
> -	struct v4l2_mbus_framefmt current_format;
> +
>  	const struct imx290_mode *current_mode;
> +	u8 bpp;
> 
>  	struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
>  	struct gpio_desc *rst_gpio;
> @@ -192,8 +192,6 @@ struct imx290 {
>  	struct v4l2_ctrl *pixel_rate;
>  	struct v4l2_ctrl *hblank;
>  	struct v4l2_ctrl *vblank;
> -
> -	struct mutex lock;
>  };
> 
>  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> @@ -524,14 +522,15 @@ static int imx290_set_black_level(struct imx290
> *imx290, black_level >> (16 - imx290->bpp), err);
>  }
> 
> -static int imx290_write_current_format(struct imx290 *imx290)
> +static int imx290_setup_format(struct imx290 *imx290,
> +			       const struct v4l2_mbus_framefmt *format)
>  {
>  	const struct imx290_regval *regs;
>  	unsigned int num_regs;
>  	unsigned int bpp;
>  	int ret;
> 
> -	switch (imx290->current_format.code) {
> +	switch (format->code) {
>  	case MEDIA_BUS_FMT_SRGGB10_1X10:
>  		regs = imx290_10bit_settings;
>  		num_regs = ARRAY_SIZE(imx290_10bit_settings);
> @@ -564,12 +563,17 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  {
>  	struct imx290 *imx290 = container_of(ctrl->handler,
>  					     struct imx290, ctrls);
> +	const struct v4l2_mbus_framefmt *format;
> +	struct v4l2_subdev_state *state;
>  	int ret = 0;
> 
>  	/* V4L2 controls values will be applied only when power is already 
up */
>  	if (!pm_runtime_get_if_in_use(imx290->dev))
>  		return 0;
> 
> +	state = v4l2_subdev_get_locked_active_state(&imx290->sd);
> +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> +
>  	switch (ctrl->id) {
>  	case V4L2_CID_ANALOGUE_GAIN:
>  		ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
> @@ -646,11 +650,10 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  	int ret;
> 
>  	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
> -	if (ret < 0)
> +	if (ret)

This is an unrelated change.

>  		return ret;
> 
>  	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> -	imx290->ctrls.lock = &imx290->lock;
> 
>  	/*
>  	 * The sensor has an analog gain and a digital gain, both controlled
> @@ -715,11 +718,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  		return ret;
>  	}
> 
> -	mutex_lock(imx290->ctrls.lock);
> -	imx290_ctrl_update(imx290, &imx290->current_format,
> -			   imx290->current_mode);
> -	mutex_unlock(imx290->ctrls.lock);
> -
>  	return 0;
>  }
> 
> @@ -728,8 +726,10 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>   */
> 
>  /* Start streaming */
> -static int imx290_start_streaming(struct imx290 *imx290)
> +static int imx290_start_streaming(struct imx290 *imx290,
> +				  struct v4l2_subdev_state *state)
>  {
> +	const struct v4l2_mbus_framefmt *format;
>  	int ret;
> 
>  	/* Set init register settings */
> @@ -742,7 +742,8 @@ static int imx290_start_streaming(struct imx290 *imx290)
> }
> 
>  	/* Apply the register values related to current frame format */
> -	ret = imx290_write_current_format(imx290);
> +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> +	ret = imx290_setup_format(imx290, format);
>  	if (ret < 0) {
>  		dev_err(imx290->dev, "Could not set frame format\n");
>  		return ret;
> @@ -762,7 +763,7 @@ static int imx290_start_streaming(struct imx290 *imx290)
> return ret;
> 
>  	/* Apply customized values from user */
> -	ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> +	ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);

Why is it safe to ignore the lock now? There is no user-specified lock for 
imx290->ctrls.lock, but instead imx290->ctrls._lock is set, no?

>  	if (ret) {
>  		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
>  		return ret;
> @@ -791,39 +792,32 @@ static int imx290_stop_streaming(struct imx290
> *imx290) static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
>  {
>  	struct imx290 *imx290 = to_imx290(sd);
> +	struct v4l2_subdev_state *state;
>  	int ret = 0;
> 
> +	state = v4l2_subdev_lock_and_get_active_state(sd);
> +
>  	if (enable) {
>  		ret = pm_runtime_resume_and_get(imx290->dev);
>  		if (ret < 0)
> -			goto unlock_and_return;
> +			goto unlock;
> 
> -		ret = imx290_start_streaming(imx290);
> +		ret = imx290_start_streaming(imx290, state);
>  		if (ret) {
>  			dev_err(imx290->dev, "Start stream failed\n");
>  			pm_runtime_put(imx290->dev);
> -			goto unlock_and_return;
> +			goto unlock;
>  		}
>  	} else {
>  		imx290_stop_streaming(imx290);
>  		pm_runtime_put(imx290->dev);
>  	}
> 
> -unlock_and_return:
> -
> +unlock:
> +	v4l2_subdev_unlock_state(state);
>  	return ret;
>  }
> 
> -static struct v4l2_mbus_framefmt *
> -imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state
> *state, -		      u32 which)
> -{
> -	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
> -		return &imx290->current_format;
> -	else
> -		return v4l2_subdev_get_try_format(&imx290->sd, state, 0);
> -}
> -
>  static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
>  				 struct v4l2_subdev_state *sd_state,
>  				 struct v4l2_subdev_mbus_code_enum 
*code)
> @@ -858,23 +852,6 @@ static int imx290_enum_frame_size(struct v4l2_subdev
> *sd, return 0;
>  }
> 
> -static int imx290_get_fmt(struct v4l2_subdev *sd,
> -			  struct v4l2_subdev_state *sd_state,
> -			  struct v4l2_subdev_format *fmt)
> -{
> -	struct imx290 *imx290 = to_imx290(sd);
> -	struct v4l2_mbus_framefmt *framefmt;
> -
> -	mutex_lock(&imx290->lock);
> -
> -	framefmt = imx290_get_pad_format(imx290, sd_state, fmt->which);
> -	fmt->format = *framefmt;
> -
> -	mutex_unlock(&imx290->lock);
> -
> -	return 0;
> -}
> -
>  static int imx290_set_fmt(struct v4l2_subdev *sd,
>  			  struct v4l2_subdev_state *sd_state,
>  			  struct v4l2_subdev_format *fmt)
> @@ -884,8 +861,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
>  	struct v4l2_mbus_framefmt *format;
>  	unsigned int i;
> 
> -	mutex_lock(&imx290->lock);
> -
>  	mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
>  				      imx290_modes_num(imx290), width, 
height,
>  				      fmt->format.width, fmt-
>format.height);
> @@ -903,7 +878,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
>  	fmt->format.code = imx290_formats[i].code;
>  	fmt->format.field = V4L2_FIELD_NONE;
> 
> -	format = imx290_get_pad_format(imx290, sd_state, fmt->which);
> +	format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> 
>  	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
>  		imx290->current_mode = mode;
> @@ -914,8 +889,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> 
>  	*format = fmt->format;
> 
> -	mutex_unlock(&imx290->lock);
> -
>  	return 0;
>  }
> 
> @@ -923,14 +896,11 @@ 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) {
>  	case V4L2_SEL_TGT_CROP: {
> -		format = imx290_get_pad_format(imx290, sd_state, sel-
>which);
> -
> -		mutex_lock(&imx290->lock);
> +		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;
> @@ -939,7 +909,6 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
>  		sel->r.width = format->width;
>  		sel->r.height = format->height;
> 
> -		mutex_unlock(&imx290->lock);
>  		return 0;
>  	}
> 
> @@ -968,11 +937,13 @@ static int imx290_get_selection(struct v4l2_subdev
> *sd, static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
>  				  struct v4l2_subdev_state *sd_state)
>  {
> -	struct v4l2_subdev_format fmt = { 0 };
> -
> -	fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : 
V4L2_SUBDEV_FORMAT_ACTIVE;
> -	fmt.format.width = 1920;
> -	fmt.format.height = 1080;
> +	struct v4l2_subdev_format fmt = {
> +		.which = V4L2_SUBDEV_FORMAT_TRY,
> +		.format = {
> +			.width = 1920,
> +			.height = 1080,
> +		},
> +	};
> 
>  	imx290_set_fmt(subdev, sd_state, &fmt);
> 
> @@ -987,7 +958,7 @@ static const struct v4l2_subdev_pad_ops imx290_pad_ops =
> { .init_cfg = imx290_entity_init_cfg,
>  	.enum_mbus_code = imx290_enum_mbus_code,
>  	.enum_frame_size = imx290_enum_frame_size,
> -	.get_fmt = imx290_get_fmt,
> +	.get_fmt = v4l2_subdev_get_fmt,
>  	.set_fmt = imx290_set_fmt,
>  	.get_selection = imx290_get_selection,
>  };
> @@ -1004,20 +975,12 @@ static const struct media_entity_operations
> imx290_subdev_entity_ops = { static int imx290_subdev_init(struct imx290
> *imx290)
>  {
>  	struct i2c_client *client = to_i2c_client(imx290->dev);
> +	const struct v4l2_mbus_framefmt *format;
> +	struct v4l2_subdev_state *state;
>  	int ret;
> 
> -	/*
> -	 * Initialize the frame format. In particular, imx290->current_mode
> -	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() 
call
> -	 * below relies on these fields.
> -	 */
> -	imx290_entity_init_cfg(&imx290->sd, NULL);
> -
> -	ret = imx290_ctrl_init(imx290);
> -	if (ret < 0) {
> -		dev_err(imx290->dev, "Control initialization error %d\n", 
ret);
> -		return ret;
> -	}
> +	imx290->current_mode = &imx290_modes_ptr(imx290)[0];
> +	imx290->bpp = imx290_formats[0].bpp;
> 
>  	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
>  	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> @@ -1032,6 +995,22 @@ static int imx290_subdev_init(struct imx290 *imx290)
>  		return ret;
>  	}
> 
> +	ret = imx290_ctrl_init(imx290);
> +	if (ret < 0) {
> +		dev_err(imx290->dev, "Control initialization error %d\n", 
ret);
> +		media_entity_cleanup(&imx290->sd.entity);
> +		return ret;
> +	}
> +
> +	imx290->sd.state_lock = imx290->ctrls.lock;
> +
> +	v4l2_subdev_init_finalize(&imx290->sd);
> +
> +	state = v4l2_subdev_lock_and_get_active_state(&imx290->sd);
> +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> +	imx290_ctrl_update(imx290, format, imx290->current_mode);
> +	v4l2_subdev_unlock_state(state);

Do you need to check for state == NULL here?

> +
>  	return 0;
>  }
> 
> @@ -1268,12 +1247,10 @@ static int imx290_probe(struct i2c_client *client)
>  	if (ret)
>  		return ret;
> 
> -	mutex_init(&imx290->lock);
> -
>  	/* Initialize and register subdev. */
>  	ret = imx290_subdev_init(imx290);
>  	if (ret)
> -		goto err_mutex;
> +		return ret;
> 
>  	ret = v4l2_async_register_subdev(&imx290->sd);
>  	if (ret < 0) {
> @@ -1305,8 +1282,6 @@ static int imx290_probe(struct i2c_client *client)
> 
>  err_subdev:
>  	imx290_subdev_cleanup(imx290);
> -err_mutex:
> -	mutex_destroy(&imx290->lock);
> 
>  	return ret;
>  }
> @@ -1319,8 +1294,6 @@ static void imx290_remove(struct i2c_client *client)
>  	v4l2_async_unregister_subdev(sd);
>  	imx290_subdev_cleanup(imx290);
> 
> -	mutex_destroy(&imx290->lock);
> -
>  	pm_runtime_disable(imx290->dev);
>  	if (!pm_runtime_status_suspended(imx290->dev))
>  		imx290_power_off(imx290->dev);





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

* Re: [PATCH v1 12/15] media: i2c: imx290: Rename, extend and expand usage of imx290_pixfmt
  2022-11-22 22:32 ` [PATCH v1 12/15] media: i2c: imx290: Rename, extend and expand usage of imx290_pixfmt Laurent Pinchart
@ 2022-11-23  8:53   ` Alexander Stein
  0 siblings, 0 replies; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  8:53 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for the patch.

Am Dienstag, 22. November 2022, 23:32:47 CET schrieb Laurent Pinchart:
> The imx290_pixfmt structure contains information about formats,
> currently limited to the bpp value. Extend it with the register settings
> for each format, and rename it to imx290_format_info to make its purpose
> clearer. Add a function named imx290_format_info() to look up format
> info for a media bus code, and use it through the code. This allows
> dropping the imx290 bpp field as the value is now looked up dynamically.
> 
> The error handling in imx290_setup_format() can also be dropped, as the
> format is guaranteed by imx290_set_fmt() to be valid.

This looks a bit weird accessing pointers returned by imx290_format_info() 
unconditionally at some places, while imx290_format_info() might return NULL.
But AFAICS the cases where this might happen, due to userspace interactions, 
are checked.

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

> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 87 ++++++++++++++++++++------------------
>  1 file changed, 45 insertions(+), 42 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index cd2154983341..d279d6dd0cb4 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -182,7 +182,6 @@ struct imx290 {
>  	struct media_pad pad;
> 
>  	const struct imx290_mode *current_mode;
> -	u8 bpp;
> 
>  	struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
>  	struct gpio_desc *rst_gpio;
> @@ -414,16 +413,41 @@ static inline int imx290_modes_num(const struct imx290
> *imx290) return ARRAY_SIZE(imx290_modes_4lanes);
>  }
> 
> -struct imx290_pixfmt {
> +struct imx290_format_info {
>  	u32 code;
>  	u8 bpp;
> +	const struct imx290_regval *regs;
> +	unsigned int num_regs;
>  };
> 
> -static const struct imx290_pixfmt imx290_formats[] = {
> -	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
> -	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
> +static const struct imx290_format_info imx290_formats[] = {
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
> +		.bpp = 10,
> +		.regs = imx290_10bit_settings,
> +		.num_regs = ARRAY_SIZE(imx290_10bit_settings),
> +	}, {
> +		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
> +		.bpp = 12,
> +		.regs = imx290_12bit_settings,
> +		.num_regs = ARRAY_SIZE(imx290_12bit_settings),
> +	}
>  };
> 
> +static const struct imx290_format_info *imx290_format_info(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) {
> +		const struct imx290_format_info *info = 
&imx290_formats[i];
> +
> +		if (info->code == code)
> +			return info;
> +	}
> +
> +	return NULL;
> +}
> +
>  /*
> ---------------------------------------------------------------------------
> -- * Register access
>   */
> @@ -516,43 +540,31 @@ static int imx290_set_data_lanes(struct imx290
> *imx290) }
> 
>  static int imx290_set_black_level(struct imx290 *imx290,
> +				  const struct v4l2_mbus_framefmt 
*format,
>  				  unsigned int black_level, int *err)
>  {
> +	unsigned int bpp = imx290_format_info(format->code)->bpp;
> +
>  	return imx290_write(imx290, IMX290_BLKLEVEL,
> -			    black_level >> (16 - imx290->bpp), err);
> +			    black_level >> (16 - bpp), err);
>  }
> 
>  static int imx290_setup_format(struct imx290 *imx290,
>  			       const struct v4l2_mbus_framefmt *format)
>  {
> -	const struct imx290_regval *regs;
> -	unsigned int num_regs;
> -	unsigned int bpp;
> +	const struct imx290_format_info *info;
>  	int ret;
> 
> -	switch (format->code) {
> -	case MEDIA_BUS_FMT_SRGGB10_1X10:
> -		regs = imx290_10bit_settings;
> -		num_regs = ARRAY_SIZE(imx290_10bit_settings);
> -		bpp = 10;
> -		break;
> -	case MEDIA_BUS_FMT_SRGGB12_1X12:
> -		regs = imx290_12bit_settings;
> -		num_regs = ARRAY_SIZE(imx290_12bit_settings);
> -		bpp = 12;
> -		break;
> -	default:
> -		dev_err(imx290->dev, "Unknown pixel format\n");
> -		return -EINVAL;
> -	}
> +	info = imx290_format_info(format->code);
> 
> -	ret = imx290_set_register_array(imx290, regs, num_regs);
> +	ret = imx290_set_register_array(imx290, info->regs, info->num_regs);
>  	if (ret < 0) {
>  		dev_err(imx290->dev, "Could not set format registers\n");
>  		return ret;
>  	}
> 
> -	return imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT, 
&ret);
> +	return imx290_set_black_level(imx290, format,
> +				      IMX290_BLACK_LEVEL_DEFAULT, 
&ret);
>  }
> 
>  /*
> ---------------------------------------------------------------------------
> - @@ -586,7 +598,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> 
>  	case V4L2_CID_TEST_PATTERN:
>  		if (ctrl->val) {
> -			imx290_set_black_level(imx290, 0, &ret);
> +			imx290_set_black_level(imx290, format, 0, &ret);
>  			usleep_range(10000, 11000);
>  			imx290_write(imx290, IMX290_PGCTRL,
>  				     (u8)(IMX290_PGCTRL_REGEN |
> @@ -595,8 +607,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>  		} else {
>  			imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
>  			usleep_range(10000, 11000);
> -			imx290_set_black_level(imx290, 
IMX290_BLACK_LEVEL_DEFAULT,
> -					       &ret);
> +			imx290_set_black_level(imx290, format,
> +					       
IMX290_BLACK_LEVEL_DEFAULT, &ret);
>  		}
>  		break;
>  	default:
> @@ -635,7 +647,7 @@ static void imx290_ctrl_update(struct imx290 *imx290,
> 
>  	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
>  	pixel_rate = link_freq * 2 * imx290->nlanes;
> -	do_div(pixel_rate, imx290->bpp);
> +	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);
> @@ -837,8 +849,7 @@ static int imx290_enum_frame_size(struct v4l2_subdev
> *sd, const struct imx290 *imx290 = to_imx290(sd);
>  	const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290);
> 
> -	if ((fse->code != imx290_formats[0].code) &&
> -	    (fse->code != imx290_formats[1].code))
> +	if (!imx290_format_info(fse->code))
>  		return -EINVAL;
> 
>  	if (fse->index >= imx290_modes_num(imx290))
> @@ -859,7 +870,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
>  	struct imx290 *imx290 = to_imx290(sd);
>  	const struct imx290_mode *mode;
>  	struct v4l2_mbus_framefmt *format;
> -	unsigned int i;
> 
>  	mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
>  				      imx290_modes_num(imx290), width, 
height,
> @@ -868,21 +878,15 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
>  	fmt->format.width = mode->width;
>  	fmt->format.height = mode->height;
> 
> -	for (i = 0; i < ARRAY_SIZE(imx290_formats); i++)
> -		if (imx290_formats[i].code == fmt->format.code)
> -			break;
> +	if (!imx290_format_info(fmt->format.code))
> +		fmt->format.code = imx290_formats[0].code;
> 
> -	if (i >= ARRAY_SIZE(imx290_formats))
> -		i = 0;
> -
> -	fmt->format.code = imx290_formats[i].code;
>  	fmt->format.field = V4L2_FIELD_NONE;
> 
>  	format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> 
>  	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
>  		imx290->current_mode = mode;
> -		imx290->bpp = imx290_formats[i].bpp;
> 
>  		imx290_ctrl_update(imx290, &fmt->format, mode);
>  	}
> @@ -980,7 +984,6 @@ static int imx290_subdev_init(struct imx290 *imx290)
>  	int ret;
> 
>  	imx290->current_mode = &imx290_modes_ptr(imx290)[0];
> -	imx290->bpp = imx290_formats[0].bpp;
> 
>  	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
>  	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;





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

* Re: [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time
  2022-11-22 22:32 ` [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time Laurent Pinchart
@ 2022-11-23  9:01   ` Alexander Stein
  2022-11-23 10:16   ` Dave Stevenson
  1 sibling, 0 replies; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  9:01 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:49 CET schrieb Laurent Pinchart:
> There's no need to configure the data lanes in the runtime PM resume
> handler. Do so in imx290_start_streaming() instead.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index dbed703fa199..4dfa090f918d 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -753,6 +753,9 @@ static int imx290_start_streaming(struct imx290 *imx290,
> return ret;
>  	}
> 
> +	/* Set data lane count */
> +	imx290_set_data_lanes(imx290);
> +
>  	/* 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);
> @@ -1052,9 +1055,6 @@ static int imx290_power_on(struct device *dev)
>  	gpiod_set_value_cansleep(imx290->rst_gpio, 0);
>  	usleep_range(30000, 31000);
> 
> -	/* Set data lane count */
> -	imx290_set_data_lanes(imx290);
> -
>  	return 0;
>  }

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




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

* Re: [PATCH v1 15/15] media: i2c: imx290: Simplify imx290_set_data_lanes()
  2022-11-22 22:32 ` [PATCH v1 15/15] media: i2c: imx290: Simplify imx290_set_data_lanes() Laurent Pinchart
@ 2022-11-23  9:04   ` Alexander Stein
  2022-11-23 10:25     ` Laurent Pinchart
  0 siblings, 1 reply; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  9:04 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:50 CET schrieb Laurent Pinchart:
> There's no need to check for an incorrect number of data lanes in
> imx290_set_data_lanes() as the value is validated at probe() time. Drop
> the check.
> 
> The PHY_LANE_NUM and CSI_LANE_MODE registers are programmed with a value
> equal to the number of lanes minus one. Compute it instead of handling
> it in the switch/case.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 17 +++++------------
>  1 file changed, 5 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 4dfa090f918d..369db35a7afd 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -512,28 +512,21 @@ static int imx290_set_register_array(struct imx290
> *imx290,
> 
>  static int imx290_set_data_lanes(struct imx290 *imx290)
>  {
> -	int ret = 0, laneval, frsel;
> +	int ret = 0;

You can remove ret and make function returning void as well as there 
(currently) is no error handling at all. The current single caller also 
ignores the return value.

Regards,
Alexander

> +	u32 frsel;
> 
>  	switch (imx290->nlanes) {
>  	case 2:
> -		laneval = 0x01;
> +	default:
>  		frsel = 0x02;
>  		break;
>  	case 4:
> -		laneval = 0x03;
>  		frsel = 0x01;
>  		break;
> -	default:
> -		/*
> -		 * We should never hit this since the data lane count is
> -		 * validated in probe itself
> -		 */
> -		dev_err(imx290->dev, "Lane configuration not 
supported\n");
> -		return -EINVAL;
>  	}
> 
> -	imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
> -	imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
> +	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);
> 
>  	return ret;





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

* Re: [PATCH v1 05/15] media: i2c: imx290: Pass format and mode to imx290_calc_pixel_rate()
  2022-11-22 22:32 ` [PATCH v1 05/15] media: i2c: imx290: Pass format and mode to imx290_calc_pixel_rate() Laurent Pinchart
@ 2022-11-23  9:06   ` Alexander Stein
  0 siblings, 0 replies; 50+ messages in thread
From: Alexander Stein @ 2022-11-23  9:06 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hello Laurent,

thanks for working on this.

Am Dienstag, 22. November 2022, 23:32:40 CET schrieb Laurent Pinchart:
> Avoid accessing the imx290 current_format and current_mode fields in
> imx290_calc_pixel_rate() to prepare for the removal of those fields.
> Among the two callers of the function, imx290_ctrl_update() has an
> explicit mode pointer already, and we can also give it a format pointer.
> Use those explicitly.
> 
> While at it, inline the imx290_get_link_freq() function in
> imx290_calc_pixel_rate() as it is only called there.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 27 ++++++++++++---------------
>  1 file changed, 12 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 2d198b167853..a676ce93f96e 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -547,21 +547,15 @@ static int imx290_write_current_format(struct imx290
> *imx290) return 0;
>  }
> 
> -static s64 imx290_get_link_freq(struct imx290 *imx290)
> +static u64 imx290_calc_pixel_rate(struct imx290 *imx290,
> +				  const struct v4l2_mbus_framefmt 
*format,

You can remove format here. It's unused and gets removed in the next patch 
anyway.

Regards,
Alexander

> +				  const struct imx290_mode *mode)
>  {
> -	u8 index = imx290->current_mode->link_freq_index;
> -
> -	return *(imx290_link_freqs_ptr(imx290) + index);
> -}
> -
> -static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
> -{
> -	s64 link_freq = imx290_get_link_freq(imx290);
> -	u8 nlanes = imx290->nlanes;
> +	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 * nlanes;
> +	pixel_rate = link_freq * 2 * imx290->nlanes;
>  	do_div(pixel_rate, imx290->bpp);
>  	return pixel_rate;
>  }
> @@ -635,6 +629,7 @@ static const char * const imx290_test_pattern_menu[] = {
> };
> 
>  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;
> @@ -642,7 +637,7 @@ static void imx290_ctrl_update(struct imx290 *imx290,
> 
>  	__v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
>  	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
> -				 imx290_calc_pixel_rate(imx290));
> +				 imx290_calc_pixel_rate(imx290, 
format, mode));
> 
>  	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
>  	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> @@ -652,6 +647,7 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  {
>  	struct v4l2_fwnode_device_properties props;
>  	unsigned int blank;
> +	u64 pixel_rate;
>  	int ret;
> 
>  	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
> @@ -689,10 +685,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>  	if (imx290->link_freq)
>  		imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> 
> +	pixel_rate = imx290_calc_pixel_rate(imx290, &imx290->current_format,
> +					    imx290->current_mode);
>  	imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, 
&imx290_ctrl_ops,
>  					       V4L2_CID_PIXEL_RATE,
> -					       1, INT_MAX, 1,
> -					       
imx290_calc_pixel_rate(imx290));
> +					       1, INT_MAX, 1, 
pixel_rate);
> 
>  	v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
>  				     V4L2_CID_TEST_PATTERN,
> @@ -913,7 +910,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
>  		imx290->current_mode = mode;
>  		imx290->bpp = imx290_formats[i].bpp;
> 
> -		imx290_ctrl_update(imx290, mode);
> +		imx290_ctrl_update(imx290, &fmt->format, mode);
>  	}
> 
>  	*format = fmt->format;





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

* Re: [PATCH v1 06/15] media: i2c: imx290: Compute pixel rate and blanking in one place
  2022-11-22 22:32 ` [PATCH v1 06/15] media: i2c: imx290: Compute pixel rate and blanking in one place Laurent Pinchart
  2022-11-23  7:59   ` Alexander Stein
@ 2022-11-23  9:58   ` Dave Stevenson
  1 sibling, 0 replies; 50+ messages in thread
From: Dave Stevenson @ 2022-11-23  9:58 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Laurent

On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> The hblank, vblank, pixel rate and link frequency values and limits are
> currently computed when creating controls, in imx290_ctrl_init(), and
> updated in imx290_ctrl_update(). This duplicates the logic in different
> places. Simplify the code by setting the control values and limits to
> hardcoded values when creating the controls, and call
> imx290_ctrl_update() to then update them.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 46 +++++++++++++++++---------------------
>  1 file changed, 20 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index a676ce93f96e..135ed55592a1 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -547,19 +547,6 @@ static int imx290_write_current_format(struct imx290 *imx290)
>         return 0;
>  }
>
> -static u64 imx290_calc_pixel_rate(struct imx290 *imx290,
> -                                 const struct v4l2_mbus_framefmt *format,
> -                                 const struct imx290_mode *mode)
> -{
> -       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->bpp);
> -       return pixel_rate;
> -}
> -
>  /* ----------------------------------------------------------------------------
>   * Controls
>   */
> @@ -634,10 +621,15 @@ 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->bpp);
>
>         __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
> -       __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
> -                                imx290_calc_pixel_rate(imx290, format, mode));
> +       __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);
> @@ -646,8 +638,6 @@ static void imx290_ctrl_update(struct imx290 *imx290,
>  static int imx290_ctrl_init(struct imx290 *imx290)
>  {
>         struct v4l2_fwnode_device_properties props;
> -       unsigned int blank;
> -       u64 pixel_rate;
>         int ret;
>
>         ret = v4l2_fwnode_device_parse(imx290->dev, &props);
> @@ -677,6 +667,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>                           V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
>                           IMX290_VMAX_DEFAULT - 2);
>
> +       /*
> +        * Set the link frequency, pixel rate, horizontal blanking and vertical
> +        * blanking it hardcoded values, they will be updated by

s/it/to
otherwise the comment makes no sense.

  Dave

> +        * imx290_ctrl_update().
> +        */
>         imx290->link_freq =
>                 v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
>                                        V4L2_CID_LINK_FREQ,
> @@ -685,28 +680,22 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>         if (imx290->link_freq)
>                 imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>
> -       pixel_rate = imx290_calc_pixel_rate(imx290, &imx290->current_format,
> -                                           imx290->current_mode);
>         imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
>                                                V4L2_CID_PIXEL_RATE,
> -                                              1, INT_MAX, 1, pixel_rate);
> +                                              1, INT_MAX, 1, 1);
>
>         v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops,
>                                      V4L2_CID_TEST_PATTERN,
>                                      ARRAY_SIZE(imx290_test_pattern_menu) - 1,
>                                      0, 0, imx290_test_pattern_menu);
>
> -       blank = imx290->current_mode->hmax - imx290->current_mode->width;
>         imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> -                                          V4L2_CID_HBLANK, blank, blank, 1,
> -                                          blank);
> +                                          V4L2_CID_HBLANK, 1, 1, 1, 1);
>         if (imx290->hblank)
>                 imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>
> -       blank = IMX290_VMAX_DEFAULT - imx290->current_mode->height;
>         imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
> -                                          V4L2_CID_VBLANK, blank, blank, 1,
> -                                          blank);
> +                                          V4L2_CID_VBLANK, 1, 1, 1, 1);
>         if (imx290->vblank)
>                 imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>
> @@ -721,6 +710,11 @@ static int imx290_ctrl_init(struct imx290 *imx290)
>                 return ret;
>         }
>
> +       mutex_lock(imx290->ctrls.lock);
> +       imx290_ctrl_update(imx290, &imx290->current_format,
> +                          imx290->current_mode);
> +       mutex_unlock(imx290->ctrls.lock);
> +
>         return 0;
>  }
>
> --
> Regards,
>
> Laurent Pinchart
>

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

* Re: [PATCH v1 02/15] media: i2c: imx290: Factor out subdev init and cleanup to functions
  2022-11-23  7:44   ` Alexander Stein
@ 2022-11-23 10:04     ` Laurent Pinchart
  0 siblings, 0 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-23 10:04 UTC (permalink / raw)
  To: Alexander Stein; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Alexander,

On Wed, Nov 23, 2022 at 08:44:45AM +0100, Alexander Stein wrote:
> Am Dienstag, 22. November 2022, 23:32:37 CET schrieb Laurent Pinchart:
> > The probe() function is large. Make it more readable by factoring the
> > subdev initialization code out. While at it, rename the error labels as
> > the "free_" prefix isn't accurate.
> > 
> > No functional change intended.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  drivers/media/i2c/imx290.c | 109 +++++++++++++++++++++----------------
> >  1 file changed, 62 insertions(+), 47 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index aae4f51941a1..4dbf218e7a63 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -1015,6 +1015,46 @@ static const struct media_entity_operations
> > imx290_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate,
> >  };
> > 
> > +static int imx290_subdev_init(struct imx290 *imx290)
> > +{
> > +	struct i2c_client *client = to_i2c_client(imx290->dev);
> > +	int ret;
> > +
> > +	/*
> > +	 * Initialize the frame format. In particular, imx290->current_mode
> > +	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() 
> call
> > +	 * below relies on these fields.
> > +	 */
> > +	imx290_entity_init_cfg(&imx290->sd, NULL);
> > +
> > +	ret = imx290_ctrl_init(imx290);
> > +	if (ret < 0) {
> > +		dev_err(imx290->dev, "Control initialization error %d\n", 
> ret);
> > +		return ret;
> > +	}
> > +
> > +	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> > +	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +	imx290->sd.dev = imx290->dev;
> > +	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
> > +	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> > +
> > +	imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
> > +	ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
> > +	if (ret < 0) {
> > +		dev_err(imx290->dev, "Could not register media entity\n");
> > +		return ret;
> 
> AFAICS in this error path v4l2_ctrl_handler_free is not called later on, 
> because imx290_subdev_cleanup is skipped due to goto err_mutex.

Good catch, and easy to fix. I'll handle it in v2.

> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void imx290_subdev_cleanup(struct imx290 *imx290)
> > +{
> > +	media_entity_cleanup(&imx290->sd.entity);
> > +	v4l2_ctrl_handler_free(&imx290->ctrls);
> > +}
> > +
> >  /*
> > ---------------------------------------------------------------------------
> > - * Power management
> >   */
> > @@ -1148,10 +1188,10 @@ static int imx290_probe(struct i2c_client *client)
> >  	fwnode_handle_put(endpoint);
> >  	if (ret == -ENXIO) {
> >  		dev_err(dev, "Unsupported bus type, should be CSI2\n");
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	} else if (ret) {
> >  		dev_err(dev, "Parsing endpoint node failed\n");
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	}
> > 
> >  	/* Get number of data lanes */
> > @@ -1159,7 +1199,7 @@ static int imx290_probe(struct i2c_client *client)
> >  	if (imx290->nlanes != 2 && imx290->nlanes != 4) {
> >  		dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
> >  		ret = -EINVAL;
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	}
> > 
> >  	dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
> > @@ -1167,7 +1207,7 @@ static int imx290_probe(struct i2c_client *client)
> >  	if (!ep.nr_of_link_frequencies) {
> >  		dev_err(dev, "link-frequency property not found in DT\n");
> >  		ret = -EINVAL;
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	}
> > 
> >  	/* Check that link frequences for all the modes are in device tree 
> */
> > @@ -1175,7 +1215,7 @@ static int imx290_probe(struct i2c_client *client)
> >  	if (fq) {
> >  		dev_err(dev, "Link frequency of %lld is not supported\n", 
> fq);
> >  		ret = -EINVAL;
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	}
> > 
> >  	/* get system clock (xclk) */
> > @@ -1183,14 +1223,14 @@ static int imx290_probe(struct i2c_client *client)
> >  	if (IS_ERR(imx290->xclk)) {
> >  		dev_err(dev, "Could not get xclk");
> >  		ret = PTR_ERR(imx290->xclk);
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	}
> > 
> >  	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
> >  				       &xclk_freq);
> >  	if (ret) {
> >  		dev_err(dev, "Could not get xclk frequency\n");
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	}
> > 
> >  	/* external clock must be 37.125 MHz */
> > @@ -1198,19 +1238,19 @@ static int imx290_probe(struct i2c_client *client)
> >  		dev_err(dev, "External clock frequency %u is not 
> supported\n",
> >  			xclk_freq);
> >  		ret = -EINVAL;
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	}
> > 
> >  	ret = clk_set_rate(imx290->xclk, xclk_freq);
> >  	if (ret) {
> >  		dev_err(dev, "Could not set xclk frequency\n");
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	}
> > 
> >  	ret = imx290_get_regulators(dev, imx290);
> >  	if (ret < 0) {
> >  		dev_err(dev, "Cannot get regulators\n");
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	}
> > 
> >  	imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
> > @@ -1218,55 +1258,33 @@ static int imx290_probe(struct i2c_client *client)
> >  	if (IS_ERR(imx290->rst_gpio)) {
> >  		dev_err(dev, "Cannot get reset gpio\n");
> >  		ret = PTR_ERR(imx290->rst_gpio);
> > -		goto free_err;
> > +		goto err_endpoint;
> >  	}
> > 
> >  	mutex_init(&imx290->lock);
> > 
> > -	/*
> > -	 * Initialize the frame format. In particular, imx290->current_mode
> > -	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() 
> call
> > -	 * below relies on these fields.
> > -	 */
> > -	imx290_entity_init_cfg(&imx290->sd, NULL);
> > -
> > -	ret = imx290_ctrl_init(imx290);
> > -	if (ret < 0) {
> > -		dev_err(dev, "Control initialization error %d\n", ret);
> > -		goto free_mutex;
> > -	}
> > -
> > -	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> > -	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > -	imx290->sd.dev = &client->dev;
> > -	imx290->sd.entity.ops = &imx290_subdev_entity_ops;
> > -	imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> > -
> > -	imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
> > -	ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
> > -	if (ret < 0) {
> > -		dev_err(dev, "Could not register media entity\n");
> > -		goto free_ctrl;
> > -	}
> > +	ret = imx290_subdev_init(imx290);
> > +	if (ret)
> > +		goto err_mutex;
> > 
> >  	ret = v4l2_async_register_subdev(&imx290->sd);
> >  	if (ret < 0) {
> >  		dev_err(dev, "Could not register v4l2 device\n");
> > -		goto free_entity;
> > +		goto err_subdev;
> >  	}
> > 
> >  	/* Power on the device to match runtime PM state below */
> >  	ret = imx290_power_on(dev);
> >  	if (ret < 0) {
> >  		dev_err(dev, "Could not power on the device\n");
> > -		goto free_entity;
> > +		goto err_subdev;
> >  	}
> > 
> >  	ret = imx290_read(imx290, IMX290_CHIP_ID, &chip_id);
> >  	if (ret) {
> >  		dev_err(dev, "Could not read chip ID: %d\n", ret);
> >  		imx290_power_off(dev);
> > -		goto free_entity;
> > +		goto err_subdev;
> >  	}
> > 
> >  	dev_info(dev, "chip ID 0x%04x\n", chip_id);
> > @@ -1279,13 +1297,11 @@ static int imx290_probe(struct i2c_client *client)
> > 
> >  	return 0;
> > 
> > -free_entity:
> > -	media_entity_cleanup(&imx290->sd.entity);
> > -free_ctrl:
> > -	v4l2_ctrl_handler_free(&imx290->ctrls);
> > -free_mutex:
> > +err_subdev:
> > +	imx290_subdev_cleanup(imx290);
> > +err_mutex:
> >  	mutex_destroy(&imx290->lock);
> > -free_err:
> > +err_endpoint:
> >  	v4l2_fwnode_endpoint_free(&ep);
> > 
> >  	return ret;
> > @@ -1297,8 +1313,7 @@ static void imx290_remove(struct i2c_client *client)
> >  	struct imx290 *imx290 = to_imx290(sd);
> > 
> >  	v4l2_async_unregister_subdev(sd);
> > -	media_entity_cleanup(&sd->entity);
> > -	v4l2_ctrl_handler_free(sd->ctrl_handler);
> > +	imx290_subdev_cleanup(imx290);
> > 
> >  	mutex_destroy(&imx290->lock);

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function
  2022-11-22 22:32 ` [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function Laurent Pinchart
  2022-11-23  8:03   ` Alexander Stein
@ 2022-11-23 10:08   ` Dave Stevenson
  2022-11-23 11:00     ` Laurent Pinchart
  1 sibling, 1 reply; 50+ messages in thread
From: Dave Stevenson @ 2022-11-23 10:08 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Laurent

On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> The black level programmed in the BLKLEVEL register depends on the
> output format. The black level value computation is currently performed
> in imx290_set_ctrl(), in addition to having different black level values
> in the output-specific register value tables. Move it to a separate
> function to simplify the imx290_set_ctrl() code.

Does this actually help?
The sensor only supports 10 and 12 bpp readout, so making it generic
for any bpp is rather redundant.
If you are looking at having different on-sensor black level values
kicking around, then tuning data goes out the window.

OK we do appear to have duplication between imx290_[10|12]bit_settings
and imx290_set_ctrl, but I don't get the need to compute it.

  Dave

> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 53 +++++++++++++++++++++-----------------
>  1 file changed, 29 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index 135ed55592a1..d9fc6c44b0f0 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -152,6 +152,9 @@
>  #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH             1920
>  #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT            1080
>
> +/* Equivalent value for 16bpp */
> +#define IMX290_BLACK_LEVEL_DEFAULT                     3840
> +
>  #define IMX290_NUM_SUPPLIES                            3
>
>  struct imx290_regval {
> @@ -315,7 +318,6 @@ static const struct imx290_regval imx290_10bit_settings[] = {
>         { IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
>         { IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
>         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
> -       { IMX290_BLKLEVEL, 60 },
>  };
>
>  static const struct imx290_regval imx290_12bit_settings[] = {
> @@ -325,7 +327,6 @@ static const struct imx290_regval imx290_12bit_settings[] = {
>         { IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
>         { IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
>         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
> -       { IMX290_BLKLEVEL, 240 },
>  };
>
>  /* supported link frequencies */
> @@ -516,35 +517,43 @@ static int imx290_set_data_lanes(struct imx290 *imx290)
>         return ret;
>  }
>
> +static int imx290_set_black_level(struct imx290 *imx290,
> +                                 unsigned int black_level, int *err)
> +{
> +       return imx290_write(imx290, IMX290_BLKLEVEL,
> +                           black_level >> (16 - imx290->bpp), err);
> +}
> +
>  static int imx290_write_current_format(struct imx290 *imx290)
>  {
> +       const struct imx290_regval *regs;
> +       unsigned int num_regs;
> +       unsigned int bpp;
>         int ret;
>
>         switch (imx290->current_format.code) {
>         case MEDIA_BUS_FMT_SRGGB10_1X10:
> -               ret = imx290_set_register_array(imx290, imx290_10bit_settings,
> -                                               ARRAY_SIZE(
> -                                                       imx290_10bit_settings));
> -               if (ret < 0) {
> -                       dev_err(imx290->dev, "Could not set format registers\n");
> -                       return ret;
> -               }
> +               regs = imx290_10bit_settings;
> +               num_regs = ARRAY_SIZE(imx290_10bit_settings);
> +               bpp = 10;
>                 break;
>         case MEDIA_BUS_FMT_SRGGB12_1X12:
> -               ret = imx290_set_register_array(imx290, imx290_12bit_settings,
> -                                               ARRAY_SIZE(
> -                                                       imx290_12bit_settings));
> -               if (ret < 0) {
> -                       dev_err(imx290->dev, "Could not set format registers\n");
> -                       return ret;
> -               }
> +               regs = imx290_12bit_settings;
> +               num_regs = ARRAY_SIZE(imx290_12bit_settings);
> +               bpp = 12;
>                 break;
>         default:
>                 dev_err(imx290->dev, "Unknown pixel format\n");
>                 return -EINVAL;
>         }
>
> -       return 0;
> +       ret = imx290_set_register_array(imx290, regs, num_regs);
> +       if (ret < 0) {
> +               dev_err(imx290->dev, "Could not set format registers\n");
> +               return ret;
> +       }
> +
> +       return imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT, &ret);
>  }
>
>  /* ----------------------------------------------------------------------------
> @@ -573,7 +582,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>
>         case V4L2_CID_TEST_PATTERN:
>                 if (ctrl->val) {
> -                       imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
> +                       imx290_set_black_level(imx290, 0, &ret);
>                         usleep_range(10000, 11000);
>                         imx290_write(imx290, IMX290_PGCTRL,
>                                      (u8)(IMX290_PGCTRL_REGEN |
> @@ -582,12 +591,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
>                 } else {
>                         imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
>                         usleep_range(10000, 11000);
> -                       if (imx290->bpp == 10)
> -                               imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
> -                                            &ret);
> -                       else /* 12 bits per pixel */
> -                               imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
> -                                            &ret);
> +                       imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT,
> +                                              &ret);
>                 }
>                 break;
>         default:
> --
> Regards,
>
> Laurent Pinchart
>

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

* Re: [PATCH v1 03/15] media: i2c: imx290: Factor out control update code to a function
  2022-11-23  7:51   ` Alexander Stein
@ 2022-11-23 10:08     ` Laurent Pinchart
  0 siblings, 0 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-23 10:08 UTC (permalink / raw)
  To: Alexander Stein; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Alexander,

On Wed, Nov 23, 2022 at 08:51:24AM +0100, Alexander Stein wrote:
> Am Dienstag, 22. November 2022, 23:32:38 CET schrieb Laurent Pinchart:
> > Move the control update code to a separate function to group it with all
> > the control-related code and make imx290_set_fmt() more readable.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  drivers/media/i2c/imx290.c | 36 ++++++++++++++++--------------------
> >  1 file changed, 16 insertions(+), 20 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 4dbf218e7a63..eb295502d0c3 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -639,6 +639,21 @@ static const char * const imx290_test_pattern_menu[] =
> > { "000/555h Toggle Pattern",
> >  };
> > 
> > +static void imx290_ctrl_update(struct imx290 *imx290,
> > +			       const struct imx290_mode *mode)
> > +{
> > +	unsigned int hblank = mode->hmax - mode->width;
> > +	unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> > +
> > +	__v4l2_ctrl_s_ctrl(imx290->link_freq,
> > +			   imx290_get_link_freq_index(imx290));
> > +	__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
> > +				 imx290_calc_pixel_rate(imx290));
> 
> I do not know the details, but I assume that imx290_ctrl_init already detects, 
> using imx290->ctrls.error, if one of those controls have _not_ been created.

No, there's a bug here. It's fixed in latter patches in the series I
think, but this breaks bisection. I'll fix it.

> If so:
> Acked-by: Alexander Stein <alexander.stein@ew.tq-group.com>
> 
> > +
> > +	__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank);
> > +	__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
> > +}
> > +
> >  static int imx290_ctrl_init(struct imx290 *imx290)
> >  {
> >  	struct v4l2_fwnode_device_properties props;
> > @@ -904,26 +919,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> >  		imx290->current_mode = mode;
> >  		imx290->bpp = imx290_formats[i].bpp;
> > 
> > -		if (imx290->link_freq)
> > -			__v4l2_ctrl_s_ctrl(imx290->link_freq,
> > -					   imx290_get_link_freq_index(imx290));
> > -		if (imx290->pixel_rate)
> > -			__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
> > -					imx290_calc_pixel_rate(imx290));
> > -
> > -		if (imx290->hblank) {
> > -			unsigned int hblank = mode->hmax - mode->width;
> > -
> > -			__v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank,
> > -						 1, hblank);
> > -		}
> > -
> > -		if (imx290->vblank) {
> > -			unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
> > -
> > -			__v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank,
> > -						 1, vblank);
> > -		}
> > +		imx290_ctrl_update(imx290, mode);
> >  	}
> > 
> >  	*format = fmt->format;

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 08/15] media: i2c: imx290: Factor out DT parsing to separate function
  2022-11-23  8:14   ` Alexander Stein
@ 2022-11-23 10:16     ` Laurent Pinchart
  0 siblings, 0 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-23 10:16 UTC (permalink / raw)
  To: Alexander Stein; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Alexander,

On Wed, Nov 23, 2022 at 09:14:34AM +0100, Alexander Stein wrote:
> Am Dienstag, 22. November 2022, 23:32:43 CET schrieb Laurent Pinchart:
> > Make the probe() function more readable by factoring out the DT parsing
> > code to a separate function. No functional change intended.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  drivers/media/i2c/imx290.c | 97 +++++++++++++++++++++-----------------
> >  1 file changed, 53 insertions(+), 44 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index d9fc6c44b0f0..655f676df3d2 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -1139,112 +1139,125 @@ static s64 imx290_check_link_freqs(const struct
> > imx290 *imx290, return 0;
> >  }
> > 
> > -static int imx290_probe(struct i2c_client *client)
> > +static int imx290_parse_dt(struct imx290 *imx290)
> >  {
> > -	struct device *dev = &client->dev;
> > -	struct fwnode_handle *endpoint;
> >  	/* Only CSI2 is supported for now: */
> >  	struct v4l2_fwnode_endpoint ep = {
> >  		.bus_type = V4L2_MBUS_CSI2_DPHY
> >  	};
> > -	struct imx290 *imx290;
> > -	u32 xclk_freq;
> > -	u32 chip_id;
> 
> I assume your patch for reading the chip ID slipped into this series.
> Either add it to the series or rearrange your patches.

Oops. I'll fix that.

> > +	struct fwnode_handle *endpoint;
> > +	int ret;
> >  	s64 fq;
> > -	int ret;
> > 
> > -	imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
> > -	if (!imx290)
> > -		return -ENOMEM;
> > -
> > -	imx290->dev = dev;
> > -	imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config);
> > -	if (IS_ERR(imx290->regmap)) {
> > -		dev_err(dev, "Unable to initialize I2C\n");
> > -		return -ENODEV;
> > -	}
> > -
> > -	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
> > +	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), NULL);
> >  	if (!endpoint) {
> > -		dev_err(dev, "Endpoint node not found\n");
> > +		dev_err(imx290->dev, "Endpoint node not found\n");
> >  		return -EINVAL;
> >  	}
> > 
> >  	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
> >  	fwnode_handle_put(endpoint);
> >  	if (ret == -ENXIO) {
> > -		dev_err(dev, "Unsupported bus type, should be CSI2\n");
> > -		goto err_endpoint;
> > +		dev_err(imx290->dev, "Unsupported bus type, should be CSI2\n");
> > +		goto done;
> >  	} else if (ret) {
> > -		dev_err(dev, "Parsing endpoint node failed\n");
> > -		goto err_endpoint;
> > +		dev_err(imx290->dev, "Parsing endpoint node failed\n");
> > +		goto done;
> >  	}
> > 
> >  	/* Get number of data lanes */
> >  	imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes;
> >  	if (imx290->nlanes != 2 && imx290->nlanes != 4) {
> > -		dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
> > +		dev_err(imx290->dev, "Invalid data lanes: %d\n", imx290->nlanes);
> >  		ret = -EINVAL;
> > -		goto err_endpoint;
> > +		goto done;
> >  	}
> > 
> > -	dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
> > +	dev_dbg(imx290->dev, "Using %u data lanes\n", imx290->nlanes);
> > 
> >  	if (!ep.nr_of_link_frequencies) {
> > -		dev_err(dev, "link-frequency property not found in DT\n");
> > +		dev_err(imx290->dev, "link-frequency property not found in DT\n");
> >  		ret = -EINVAL;
> > -		goto err_endpoint;
> > +		goto done;
> >  	}
> > 
> >  	/* Check that link frequences for all the modes are in device tree 
> */
> >  	fq = imx290_check_link_freqs(imx290, &ep);
> >  	if (fq) {
> > -		dev_err(dev, "Link frequency of %lld is not supported\n", fq);
> > +		dev_err(imx290->dev, "Link frequency of %lld is not supported\n",
> > +			fq);
> >  		ret = -EINVAL;
> > -		goto err_endpoint;
> > +		goto done;
> >  	}
> > 
> > +	ret = 0;
> > +
> > +done:
> > +	v4l2_fwnode_endpoint_free(&ep);
> > +	return ret;
> > +}
> > +
> > +static int imx290_probe(struct i2c_client *client)
> > +{
> > +	struct device *dev = &client->dev;
> > +	struct imx290 *imx290;
> > +	u32 xclk_freq;
> > +	u32 chip_id;
> > +	int ret;
> > +
> > +	imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL);
> > +	if (!imx290)
> > +		return -ENOMEM;
> > +
> > +	imx290->dev = dev;
> > +	imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config);
> > +	if (IS_ERR(imx290->regmap)) {
> > +		dev_err(dev, "Unable to initialize I2C\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	ret = imx290_parse_dt(imx290);
> > +	if (ret)
> > +		return ret;
> > +
> >  	/* get system clock (xclk) */
> >  	imx290->xclk = devm_clk_get(dev, "xclk");
> >  	if (IS_ERR(imx290->xclk)) {
> >  		dev_err(dev, "Could not get xclk");
> > -		ret = PTR_ERR(imx290->xclk);
> > -		goto err_endpoint;
> > +		return PTR_ERR(imx290->xclk);
> 
> Mh, using dev_err_probe this can be a one-liner.

It's done later in the series :-)

> >  	}
> > 
> >  	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
> >  				       &xclk_freq);
> >  	if (ret) {
> >  		dev_err(dev, "Could not get xclk frequency\n");
> > -		goto err_endpoint;
> > +		return ret;
> >  	}
> > 
> >  	/* external clock must be 37.125 MHz */
> >  	if (xclk_freq != 37125000) {
> >  		dev_err(dev, "External clock frequency %u is not supported\n",
> >  			xclk_freq);
> > -		ret = -EINVAL;
> > -		goto err_endpoint;
> > +		return -EINVAL;
> >  	}
> > 
> >  	ret = clk_set_rate(imx290->xclk, xclk_freq);
> >  	if (ret) {
> >  		dev_err(dev, "Could not set xclk frequency\n");
> > -		goto err_endpoint;
> > +		return ret;
> >  	}
> > 
> >  	ret = imx290_get_regulators(dev, imx290);
> >  	if (ret < 0) {
> >  		dev_err(dev, "Cannot get regulators\n");
> > -		goto err_endpoint;
> > +		return ret;
> 
> Using dev_err_probe this can be a one-liner as well.
> 
> >  	}
> > 
> >  	imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
> >  						   
> GPIOD_OUT_HIGH);
> >  	if (IS_ERR(imx290->rst_gpio)) {
> >  		dev_err(dev, "Cannot get reset gpio\n");
> > -		ret = PTR_ERR(imx290->rst_gpio);
> > -		goto err_endpoint;
> > +		return PTR_ERR(imx290->rst_gpio);
> 
> Using dev_err_probe this can be a one-liner as well.
> 
> >  	}
> > 
> >  	mutex_init(&imx290->lock);
> > @@ -1279,16 +1292,12 @@ static int imx290_probe(struct i2c_client *client)
> >  	pm_runtime_enable(dev);
> >  	pm_runtime_idle(dev);
> > 
> > -	v4l2_fwnode_endpoint_free(&ep);
> > -
> >  	return 0;
> > 
> >  err_subdev:
> >  	imx290_subdev_cleanup(imx290);
> >  err_mutex:
> >  	mutex_destroy(&imx290->lock);
> > -err_endpoint:
> > -	v4l2_fwnode_endpoint_free(&ep);
> > 
> >  	return ret;
> >  }

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time
  2022-11-22 22:32 ` [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time Laurent Pinchart
  2022-11-23  9:01   ` Alexander Stein
@ 2022-11-23 10:16   ` Dave Stevenson
  2022-11-23 14:28     ` Laurent Pinchart
  1 sibling, 1 reply; 50+ messages in thread
From: Dave Stevenson @ 2022-11-23 10:16 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Laurent

On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> There's no need to configure the data lanes in the runtime PM resume
> handler. Do so in imx290_start_streaming() instead.

Interesting as I had Sakari advocating putting clock mode selection
register control in "power on" for my recent ov9282 series. Is there
any consistency?

  Dave

https://patchwork.linuxtv.org/project/linux-media/patch/20221005152809.3785786-9-dave.stevenson@raspberrypi.com/#141118

> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index dbed703fa199..4dfa090f918d 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -753,6 +753,9 @@ static int imx290_start_streaming(struct imx290 *imx290,
>                 return ret;
>         }
>
> +       /* Set data lane count */
> +       imx290_set_data_lanes(imx290);
> +
>         /* 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);
> @@ -1052,9 +1055,6 @@ static int imx290_power_on(struct device *dev)
>         gpiod_set_value_cansleep(imx290->rst_gpio, 0);
>         usleep_range(30000, 31000);
>
> -       /* Set data lane count */
> -       imx290_set_data_lanes(imx290);
> -
>         return 0;
>  }
>
> --
> Regards,
>
> Laurent Pinchart
>

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

* Re: [PATCH v1 15/15] media: i2c: imx290: Simplify imx290_set_data_lanes()
  2022-11-23  9:04   ` Alexander Stein
@ 2022-11-23 10:25     ` Laurent Pinchart
  0 siblings, 0 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-23 10:25 UTC (permalink / raw)
  To: Alexander Stein; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Alexander,

On Wed, Nov 23, 2022 at 10:04:58AM +0100, Alexander Stein wrote:
> Am Dienstag, 22. November 2022, 23:32:50 CET schrieb Laurent Pinchart:
> > There's no need to check for an incorrect number of data lanes in
> > imx290_set_data_lanes() as the value is validated at probe() time. Drop
> > the check.
> > 
> > The PHY_LANE_NUM and CSI_LANE_MODE registers are programmed with a value
> > equal to the number of lanes minus one. Compute it instead of handling
> > it in the switch/case.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  drivers/media/i2c/imx290.c | 17 +++++------------
> >  1 file changed, 5 insertions(+), 12 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 4dfa090f918d..369db35a7afd 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -512,28 +512,21 @@ static int imx290_set_register_array(struct imx290
> > *imx290,
> > 
> >  static int imx290_set_data_lanes(struct imx290 *imx290)
> >  {
> > -	int ret = 0, laneval, frsel;
> > +	int ret = 0;
> 
> You can remove ret and make function returning void as well as there 
> (currently) is no error handling at all. The current single caller also 
> ignores the return value.

There's error handling in this function, in the imx290_write() calls.
The ret variable should stay at least for that. As for making the
function void, I would rather fix the caller. I'll add a patch for that
in v2.

> > +	u32 frsel;
> > 
> >  	switch (imx290->nlanes) {
> >  	case 2:
> > -		laneval = 0x01;
> > +	default:
> >  		frsel = 0x02;
> >  		break;
> >  	case 4:
> > -		laneval = 0x03;
> >  		frsel = 0x01;
> >  		break;
> > -	default:
> > -		/*
> > -		 * We should never hit this since the data lane count is
> > -		 * validated in probe itself
> > -		 */
> > -		dev_err(imx290->dev, "Lane configuration not supported\n");
> > -		return -EINVAL;
> >  	}
> > 
> > -	imx290_write(imx290, IMX290_PHY_LANE_NUM, laneval, &ret);
> > -	imx290_write(imx290, IMX290_CSI_LANE_MODE, laneval, &ret);
> > +	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);
> > 
> >  	return ret;

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 11/15] media: i2c: imx290: Use V4L2 subdev active state
  2022-11-23  8:42   ` Alexander Stein
@ 2022-11-23 10:49     ` Laurent Pinchart
  2022-11-28 15:33       ` Alexander Stein
  0 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-23 10:49 UTC (permalink / raw)
  To: Alexander Stein; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Alexander,

On Wed, Nov 23, 2022 at 09:42:10AM +0100, Alexander Stein wrote:
> Am Dienstag, 22. November 2022, 23:32:46 CET schrieb Laurent Pinchart:
> > Use the V4L2 subdev active state API to store the active format. This
> > simplifies the driver not only by dropping the imx290 current_format
> > field, but it also allows dropping the imx290 lock, replaced with the
> > state lock.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  drivers/media/i2c/imx290.c | 139 +++++++++++++++----------------------
> >  1 file changed, 56 insertions(+), 83 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 848de4c90d3b..cd2154983341 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -177,12 +177,12 @@ struct imx290 {
> >  	struct clk *xclk;
> >  	struct regmap *regmap;
> >  	u8 nlanes;
> > -	u8 bpp;
> > 
> >  	struct v4l2_subdev sd;
> >  	struct media_pad pad;
> > -	struct v4l2_mbus_framefmt current_format;
> > +
> >  	const struct imx290_mode *current_mode;
> > +	u8 bpp;
> > 
> >  	struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
> >  	struct gpio_desc *rst_gpio;
> > @@ -192,8 +192,6 @@ struct imx290 {
> >  	struct v4l2_ctrl *pixel_rate;
> >  	struct v4l2_ctrl *hblank;
> >  	struct v4l2_ctrl *vblank;
> > -
> > -	struct mutex lock;
> >  };
> > 
> >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > @@ -524,14 +522,15 @@ static int imx290_set_black_level(struct imx290
> > *imx290, black_level >> (16 - imx290->bpp), err);
> >  }
> > 
> > -static int imx290_write_current_format(struct imx290 *imx290)
> > +static int imx290_setup_format(struct imx290 *imx290,
> > +			       const struct v4l2_mbus_framefmt *format)
> >  {
> >  	const struct imx290_regval *regs;
> >  	unsigned int num_regs;
> >  	unsigned int bpp;
> >  	int ret;
> > 
> > -	switch (imx290->current_format.code) {
> > +	switch (format->code) {
> >  	case MEDIA_BUS_FMT_SRGGB10_1X10:
> >  		regs = imx290_10bit_settings;
> >  		num_regs = ARRAY_SIZE(imx290_10bit_settings);
> > @@ -564,12 +563,17 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> >  {
> >  	struct imx290 *imx290 = container_of(ctrl->handler,
> >  					     struct imx290, ctrls);
> > +	const struct v4l2_mbus_framefmt *format;
> > +	struct v4l2_subdev_state *state;
> >  	int ret = 0;
> > 
> >  	/* V4L2 controls values will be applied only when power is already up */
> >  	if (!pm_runtime_get_if_in_use(imx290->dev))
> >  		return 0;
> > 
> > +	state = v4l2_subdev_get_locked_active_state(&imx290->sd);
> > +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> > +
> >  	switch (ctrl->id) {
> >  	case V4L2_CID_ANALOGUE_GAIN:
> >  		ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
> > @@ -646,11 +650,10 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >  	int ret;
> > 
> >  	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
> > -	if (ret < 0)
> > +	if (ret)
> 
> This is an unrelated change.

I'll drop it.

> >  		return ret;
> > 
> >  	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> > -	imx290->ctrls.lock = &imx290->lock;
> > 
> >  	/*
> >  	 * The sensor has an analog gain and a digital gain, both controlled
> > @@ -715,11 +718,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >  		return ret;
> >  	}
> > 
> > -	mutex_lock(imx290->ctrls.lock);
> > -	imx290_ctrl_update(imx290, &imx290->current_format,
> > -			   imx290->current_mode);
> > -	mutex_unlock(imx290->ctrls.lock);
> > -
> >  	return 0;
> >  }
> > 
> > @@ -728,8 +726,10 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> >   */
> > 
> >  /* Start streaming */
> > -static int imx290_start_streaming(struct imx290 *imx290)
> > +static int imx290_start_streaming(struct imx290 *imx290,
> > +				  struct v4l2_subdev_state *state)
> >  {
> > +	const struct v4l2_mbus_framefmt *format;
> >  	int ret;
> > 
> >  	/* Set init register settings */
> > @@ -742,7 +742,8 @@ static int imx290_start_streaming(struct imx290 *imx290)
> > }
> > 
> >  	/* Apply the register values related to current frame format */
> > -	ret = imx290_write_current_format(imx290);
> > +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> > +	ret = imx290_setup_format(imx290, format);
> >  	if (ret < 0) {
> >  		dev_err(imx290->dev, "Could not set frame format\n");
> >  		return ret;
> > @@ -762,7 +763,7 @@ static int imx290_start_streaming(struct imx290 *imx290)
> > return ret;
> > 
> >  	/* Apply customized values from user */
> > -	ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> > +	ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> 
> Why is it safe to ignore the lock now? There is no user-specified lock for 
> imx290->ctrls.lock, but instead imx290->ctrls._lock is set, no?

That's because the control handler and subdev state lock are the same.
The imx290_start_streaming() function starts by calling
v4l2_subdev_lock_and_get_active_state(), which takes the lock, so we
must not lock again with v4l2_ctrl_handler_setup().

> >  	if (ret) {
> >  		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
> >  		return ret;
> > @@ -791,39 +792,32 @@ static int imx290_stop_streaming(struct imx290
> > *imx290) static int imx290_set_stream(struct v4l2_subdev *sd, int enable)
> >  {
> >  	struct imx290 *imx290 = to_imx290(sd);
> > +	struct v4l2_subdev_state *state;
> >  	int ret = 0;
> > 
> > +	state = v4l2_subdev_lock_and_get_active_state(sd);
> > +
> >  	if (enable) {
> >  		ret = pm_runtime_resume_and_get(imx290->dev);
> >  		if (ret < 0)
> > -			goto unlock_and_return;
> > +			goto unlock;
> > 
> > -		ret = imx290_start_streaming(imx290);
> > +		ret = imx290_start_streaming(imx290, state);
> >  		if (ret) {
> >  			dev_err(imx290->dev, "Start stream failed\n");
> >  			pm_runtime_put(imx290->dev);
> > -			goto unlock_and_return;
> > +			goto unlock;
> >  		}
> >  	} else {
> >  		imx290_stop_streaming(imx290);
> >  		pm_runtime_put(imx290->dev);
> >  	}
> > 
> > -unlock_and_return:
> > -
> > +unlock:
> > +	v4l2_subdev_unlock_state(state);
> >  	return ret;
> >  }
> > 
> > -static struct v4l2_mbus_framefmt *
> > -imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state
> > *state, -		      u32 which)
> > -{
> > -	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
> > -		return &imx290->current_format;
> > -	else
> > -		return v4l2_subdev_get_try_format(&imx290->sd, state, 0);
> > -}
> > -
> >  static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
> >  				 struct v4l2_subdev_state *sd_state,
> >  				 struct v4l2_subdev_mbus_code_enum *code)
> > @@ -858,23 +852,6 @@ static int imx290_enum_frame_size(struct v4l2_subdev
> > *sd, return 0;
> >  }
> > 
> > -static int imx290_get_fmt(struct v4l2_subdev *sd,
> > -			  struct v4l2_subdev_state *sd_state,
> > -			  struct v4l2_subdev_format *fmt)
> > -{
> > -	struct imx290 *imx290 = to_imx290(sd);
> > -	struct v4l2_mbus_framefmt *framefmt;
> > -
> > -	mutex_lock(&imx290->lock);
> > -
> > -	framefmt = imx290_get_pad_format(imx290, sd_state, fmt->which);
> > -	fmt->format = *framefmt;
> > -
> > -	mutex_unlock(&imx290->lock);
> > -
> > -	return 0;
> > -}
> > -
> >  static int imx290_set_fmt(struct v4l2_subdev *sd,
> >  			  struct v4l2_subdev_state *sd_state,
> >  			  struct v4l2_subdev_format *fmt)
> > @@ -884,8 +861,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> >  	struct v4l2_mbus_framefmt *format;
> >  	unsigned int i;
> > 
> > -	mutex_lock(&imx290->lock);
> > -
> >  	mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
> >  				      imx290_modes_num(imx290), width, height,
> >  				      fmt->format.width, fmt-
> >format.height);
> > @@ -903,7 +878,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> >  	fmt->format.code = imx290_formats[i].code;
> >  	fmt->format.field = V4L2_FIELD_NONE;
> > 
> > -	format = imx290_get_pad_format(imx290, sd_state, fmt->which);
> > +	format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> > 
> >  	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> >  		imx290->current_mode = mode;
> > @@ -914,8 +889,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> > 
> >  	*format = fmt->format;
> > 
> > -	mutex_unlock(&imx290->lock);
> > -
> >  	return 0;
> >  }
> > 
> > @@ -923,14 +896,11 @@ 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) {
> >  	case V4L2_SEL_TGT_CROP: {
> > -		format = imx290_get_pad_format(imx290, sd_state, sel->which);
> > -
> > -		mutex_lock(&imx290->lock);
> > +		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;
> > @@ -939,7 +909,6 @@ static int imx290_get_selection(struct v4l2_subdev *sd,
> >  		sel->r.width = format->width;
> >  		sel->r.height = format->height;
> > 
> > -		mutex_unlock(&imx290->lock);
> >  		return 0;
> >  	}
> > 
> > @@ -968,11 +937,13 @@ static int imx290_get_selection(struct v4l2_subdev
> > *sd, static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
> >  				  struct v4l2_subdev_state *sd_state)
> >  {
> > -	struct v4l2_subdev_format fmt = { 0 };
> > -
> > -	fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
> > -	fmt.format.width = 1920;
> > -	fmt.format.height = 1080;
> > +	struct v4l2_subdev_format fmt = {
> > +		.which = V4L2_SUBDEV_FORMAT_TRY,
> > +		.format = {
> > +			.width = 1920,
> > +			.height = 1080,
> > +		},
> > +	};
> > 
> >  	imx290_set_fmt(subdev, sd_state, &fmt);
> > 
> > @@ -987,7 +958,7 @@ static const struct v4l2_subdev_pad_ops imx290_pad_ops =
> > { .init_cfg = imx290_entity_init_cfg,
> >  	.enum_mbus_code = imx290_enum_mbus_code,
> >  	.enum_frame_size = imx290_enum_frame_size,
> > -	.get_fmt = imx290_get_fmt,
> > +	.get_fmt = v4l2_subdev_get_fmt,
> >  	.set_fmt = imx290_set_fmt,
> >  	.get_selection = imx290_get_selection,
> >  };
> > @@ -1004,20 +975,12 @@ static const struct media_entity_operations
> > imx290_subdev_entity_ops = { static int imx290_subdev_init(struct imx290
> > *imx290)
> >  {
> >  	struct i2c_client *client = to_i2c_client(imx290->dev);
> > +	const struct v4l2_mbus_framefmt *format;
> > +	struct v4l2_subdev_state *state;
> >  	int ret;
> > 
> > -	/*
> > -	 * Initialize the frame format. In particular, imx290->current_mode
> > -	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
> > -	 * below relies on these fields.
> > -	 */
> > -	imx290_entity_init_cfg(&imx290->sd, NULL);
> > -
> > -	ret = imx290_ctrl_init(imx290);
> > -	if (ret < 0) {
> > -		dev_err(imx290->dev, "Control initialization error %d\n", ret);
> > -		return ret;
> > -	}
> > +	imx290->current_mode = &imx290_modes_ptr(imx290)[0];
> > +	imx290->bpp = imx290_formats[0].bpp;
> > 
> >  	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> >  	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > @@ -1032,6 +995,22 @@ static int imx290_subdev_init(struct imx290 *imx290)
> >  		return ret;
> >  	}
> > 
> > +	ret = imx290_ctrl_init(imx290);
> > +	if (ret < 0) {
> > +		dev_err(imx290->dev, "Control initialization error %d\n", ret);
> > +		media_entity_cleanup(&imx290->sd.entity);
> > +		return ret;
> > +	}
> > +
> > +	imx290->sd.state_lock = imx290->ctrls.lock;
> > +
> > +	v4l2_subdev_init_finalize(&imx290->sd);
> > +
> > +	state = v4l2_subdev_lock_and_get_active_state(&imx290->sd);
> > +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> > +	imx290_ctrl_update(imx290, format, imx290->current_mode);
> > +	v4l2_subdev_unlock_state(state);
> 
> Do you need to check for state == NULL here?

No, v4l2_subdev_lock_and_get_active_state() can only return NULL if the
subdev driver doesn't have an active state, and
v4l2_subdev_init_finalize() guarantees it has one.

> > +
> >  	return 0;
> >  }
> > 
> > @@ -1268,12 +1247,10 @@ static int imx290_probe(struct i2c_client *client)
> >  	if (ret)
> >  		return ret;
> > 
> > -	mutex_init(&imx290->lock);
> > -
> >  	/* Initialize and register subdev. */
> >  	ret = imx290_subdev_init(imx290);
> >  	if (ret)
> > -		goto err_mutex;
> > +		return ret;
> > 
> >  	ret = v4l2_async_register_subdev(&imx290->sd);
> >  	if (ret < 0) {
> > @@ -1305,8 +1282,6 @@ static int imx290_probe(struct i2c_client *client)
> > 
> >  err_subdev:
> >  	imx290_subdev_cleanup(imx290);
> > -err_mutex:
> > -	mutex_destroy(&imx290->lock);
> > 
> >  	return ret;
> >  }
> > @@ -1319,8 +1294,6 @@ static void imx290_remove(struct i2c_client *client)
> >  	v4l2_async_unregister_subdev(sd);
> >  	imx290_subdev_cleanup(imx290);
> > 
> > -	mutex_destroy(&imx290->lock);
> > -
> >  	pm_runtime_disable(imx290->dev);
> >  	if (!pm_runtime_status_suspended(imx290->dev))
> >  		imx290_power_off(imx290->dev);

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function
  2022-11-23 10:08   ` Dave Stevenson
@ 2022-11-23 11:00     ` Laurent Pinchart
  2022-11-24 15:10       ` Dave Stevenson
  0 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-23 11:00 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Dave,

On Wed, Nov 23, 2022 at 10:08:19AM +0000, Dave Stevenson wrote:
> On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart wrote:
> >
> > The black level programmed in the BLKLEVEL register depends on the
> > output format. The black level value computation is currently performed
> > in imx290_set_ctrl(), in addition to having different black level values
> > in the output-specific register value tables. Move it to a separate
> > function to simplify the imx290_set_ctrl() code.
> 
> Does this actually help?
> The sensor only supports 10 and 12 bpp readout, so making it generic
> for any bpp is rather redundant.
> If you are looking at having different on-sensor black level values
> kicking around, then tuning data goes out the window.

This may be needed for some use cases, but that's not what I'm after
here. By the way, could you explain why we can't just set it to 0 in the
sensor and avoid subtracting it in the ISP (at least for the data
pedestal value that is added on purpose in the sensor, there can also be
an additional black level due to other reasons) ? I'm sure there's a
reason :-)

> OK we do appear to have duplication between imx290_[10|12]bit_settings
> and imx290_set_ctrl, but I don't get the need to compute it.

The idea was to centralize black level setting in a single place, to
prepare for the removal of he imx290->bpp field. It's really in
imx290_set_ctrl() that it matters, I could keep the value in the
imx290_10bit_settings and imx290_12bit_settings arrays, but I thought I
could as well do it the same way everywhere.

> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  drivers/media/i2c/imx290.c | 53 +++++++++++++++++++++-----------------
> >  1 file changed, 29 insertions(+), 24 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index 135ed55592a1..d9fc6c44b0f0 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -152,6 +152,9 @@
> >  #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH             1920
> >  #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT            1080
> >
> > +/* Equivalent value for 16bpp */
> > +#define IMX290_BLACK_LEVEL_DEFAULT                     3840
> > +
> >  #define IMX290_NUM_SUPPLIES                            3
> >
> >  struct imx290_regval {
> > @@ -315,7 +318,6 @@ static const struct imx290_regval imx290_10bit_settings[] = {
> >         { IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
> >         { IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
> >         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
> > -       { IMX290_BLKLEVEL, 60 },
> >  };
> >
> >  static const struct imx290_regval imx290_12bit_settings[] = {
> > @@ -325,7 +327,6 @@ static const struct imx290_regval imx290_12bit_settings[] = {
> >         { IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
> >         { IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
> >         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
> > -       { IMX290_BLKLEVEL, 240 },
> >  };
> >
> >  /* supported link frequencies */
> > @@ -516,35 +517,43 @@ static int imx290_set_data_lanes(struct imx290 *imx290)
> >         return ret;
> >  }
> >
> > +static int imx290_set_black_level(struct imx290 *imx290,
> > +                                 unsigned int black_level, int *err)
> > +{
> > +       return imx290_write(imx290, IMX290_BLKLEVEL,
> > +                           black_level >> (16 - imx290->bpp), err);
> > +}
> > +
> >  static int imx290_write_current_format(struct imx290 *imx290)
> >  {
> > +       const struct imx290_regval *regs;
> > +       unsigned int num_regs;
> > +       unsigned int bpp;
> >         int ret;
> >
> >         switch (imx290->current_format.code) {
> >         case MEDIA_BUS_FMT_SRGGB10_1X10:
> > -               ret = imx290_set_register_array(imx290, imx290_10bit_settings,
> > -                                               ARRAY_SIZE(
> > -                                                       imx290_10bit_settings));
> > -               if (ret < 0) {
> > -                       dev_err(imx290->dev, "Could not set format registers\n");
> > -                       return ret;
> > -               }
> > +               regs = imx290_10bit_settings;
> > +               num_regs = ARRAY_SIZE(imx290_10bit_settings);
> > +               bpp = 10;
> >                 break;
> >         case MEDIA_BUS_FMT_SRGGB12_1X12:
> > -               ret = imx290_set_register_array(imx290, imx290_12bit_settings,
> > -                                               ARRAY_SIZE(
> > -                                                       imx290_12bit_settings));
> > -               if (ret < 0) {
> > -                       dev_err(imx290->dev, "Could not set format registers\n");
> > -                       return ret;
> > -               }
> > +               regs = imx290_12bit_settings;
> > +               num_regs = ARRAY_SIZE(imx290_12bit_settings);
> > +               bpp = 12;
> >                 break;
> >         default:
> >                 dev_err(imx290->dev, "Unknown pixel format\n");
> >                 return -EINVAL;
> >         }
> >
> > -       return 0;
> > +       ret = imx290_set_register_array(imx290, regs, num_regs);
> > +       if (ret < 0) {
> > +               dev_err(imx290->dev, "Could not set format registers\n");
> > +               return ret;
> > +       }
> > +
> > +       return imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT, &ret);
> >  }
> >
> >  /* ----------------------------------------------------------------------------
> > @@ -573,7 +582,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> >
> >         case V4L2_CID_TEST_PATTERN:
> >                 if (ctrl->val) {
> > -                       imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
> > +                       imx290_set_black_level(imx290, 0, &ret);
> >                         usleep_range(10000, 11000);
> >                         imx290_write(imx290, IMX290_PGCTRL,
> >                                      (u8)(IMX290_PGCTRL_REGEN |
> > @@ -582,12 +591,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> >                 } else {
> >                         imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
> >                         usleep_range(10000, 11000);
> > -                       if (imx290->bpp == 10)
> > -                               imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
> > -                                            &ret);
> > -                       else /* 12 bits per pixel */
> > -                               imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
> > -                                            &ret);
> > +                       imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT,
> > +                                              &ret);
> >                 }
> >                 break;
> >         default:

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time
  2022-11-23 10:16   ` Dave Stevenson
@ 2022-11-23 14:28     ` Laurent Pinchart
  2022-11-24 18:02       ` Dave Stevenson
  0 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-23 14:28 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Dave,

On Wed, Nov 23, 2022 at 10:16:58AM +0000, Dave Stevenson wrote:
> On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart wrote:
> >
> > There's no need to configure the data lanes in the runtime PM resume
> > handler. Do so in imx290_start_streaming() instead.
> 
> Interesting as I had Sakari advocating putting clock mode selection
> register control in "power on" for my recent ov9282 series. Is there
> any consistency?

No there isn't :-) There hasn't been any official rule so far, so it's
no surprise different drivers exhibit different behaviours. I'm all for
standardization when possible though.

Overall, I think there's a general agreement that the runtime PM resume
handler needs to control everything required to make the sensor
accessible by software. That covers at least hard reset, regulators and
clocks.

For software settings, it's less clear. If the sensor requires a
software reset sequence immediately after power on, it makes sense to
also handle that in the runtime PM resume handler. Same thing for any
other initialization required to reach a quiescent state (for instance
there are many sensors that start streaming automatically right after
power on for a reason I can't understand, so that needs to be disabled).

This means that the runtime PM handler will thus access the sensor over
I2C. We may not want to do so in probe() before having a chance to probe
it (by reading an ID register for instance). The power on sequence could
be split in two to handle this, with one function that powers the sensor
up, and the other one that handles software initialization. Both would
be called from the runtime PM resume handler, and in probe(), we could
first power on the sensor, identify it, and then initialize it. I think
that will be fine on DT platforms as we don't need to RPM-resume the I2C
device in probe before accessing it as far as I can tell, given that the
probe() function should be called with the I2C controller RPM-resumed.
I'll let Sakari confirms if this works for ACPI).

For other settings, I wouldn't handle them in the runtime PM resume
handler. In this particular case, the number of data lanes could even
vary based on the sensor mode (we don't do so at the moment), so
.s_stream() time seems better to me.

> https://patchwork.linuxtv.org/project/linux-media/patch/20221005152809.3785786-9-dave.stevenson@raspberrypi.com/#141118
> 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  drivers/media/i2c/imx290.c | 6 +++---
> >  1 file changed, 3 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index dbed703fa199..4dfa090f918d 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -753,6 +753,9 @@ static int imx290_start_streaming(struct imx290 *imx290,
> >                 return ret;
> >         }
> >
> > +       /* Set data lane count */
> > +       imx290_set_data_lanes(imx290);
> > +
> >         /* 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);
> > @@ -1052,9 +1055,6 @@ static int imx290_power_on(struct device *dev)
> >         gpiod_set_value_cansleep(imx290->rst_gpio, 0);
> >         usleep_range(30000, 31000);
> >
> > -       /* Set data lane count */
> > -       imx290_set_data_lanes(imx290);
> > -
> >         return 0;
> >  }
> >

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function
  2022-11-23 11:00     ` Laurent Pinchart
@ 2022-11-24 15:10       ` Dave Stevenson
  2022-11-24 16:57         ` Laurent Pinchart
  0 siblings, 1 reply; 50+ messages in thread
From: Dave Stevenson @ 2022-11-24 15:10 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam, David Plowman

Hi Laurent

On Wed, 23 Nov 2022 at 11:00, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Dave,
>
> On Wed, Nov 23, 2022 at 10:08:19AM +0000, Dave Stevenson wrote:
> > On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart wrote:
> > >
> > > The black level programmed in the BLKLEVEL register depends on the
> > > output format. The black level value computation is currently performed
> > > in imx290_set_ctrl(), in addition to having different black level values
> > > in the output-specific register value tables. Move it to a separate
> > > function to simplify the imx290_set_ctrl() code.
> >
> > Does this actually help?
> > The sensor only supports 10 and 12 bpp readout, so making it generic
> > for any bpp is rather redundant.
> > If you are looking at having different on-sensor black level values
> > kicking around, then tuning data goes out the window.
>
> This may be needed for some use cases, but that's not what I'm after
> here. By the way, could you explain why we can't just set it to 0 in the
> sensor and avoid subtracting it in the ISP (at least for the data
> pedestal value that is added on purpose in the sensor, there can also be
> an additional black level due to other reasons) ? I'm sure there's a
> reason :-)

A quick play does imply that it works with 0 being defined in both
places, but personally I'd prefer following the manufacturer's
guidance on this sort of setting. David P may have some bright ideas
as to why a pedestal is desired.

There is a little bit of me thinking that if it were 0, then the mono
variant of the sensor with a lens exhibiting minimal vignetting could
be used directly for 10 or 12bit data under controlled lighting.
However that is a very specific and unusual use case, so not worth
potentially compromising IQ for normal use cases.

> > OK we do appear to have duplication between imx290_[10|12]bit_settings
> > and imx290_set_ctrl, but I don't get the need to compute it.
>
> The idea was to centralize black level setting in a single place, to
> prepare for the removal of he imx290->bpp field. It's really in
> imx290_set_ctrl() that it matters, I could keep the value in the
> imx290_10bit_settings and imx290_12bit_settings arrays, but I thought I
> could as well do it the same way everywhere.

I'd missed that it was reprogramming the black level for the test patterns.

Changing the black level when choosing a test pattern seems a very odd
thing to do. You're wanting to test the pipeline as configured for the
sensor, but you're artificially changing the incoming data and
therefore any processing steps are misconfigured.

Looking at the data it's weird. Viewing the data as
V4L2_PIX_FMT_SRGGB12 Pattern 4 as is gives a 12 pixel pattern of
(bytes reversed):
0105 0105 c204 c204 3e01 3e01 0000 0000 0000 0000 0000 0105
repeated multiple times along each line, and then that line repeated
vertically. So we definitely have pixels below the black level.
Alter the black level back to the default 0xf0 and you get:
f105 f105 b205 b205 2e02 2e02 0000 0000 9c00 9c00 0000 f105
So it's added the offset of 0xf0 to the non-zero pixels, but only some
of the zero ones and then not by the configured amount.

My datasheets have no reference to a PGCTRL register or 0x308c, so I
can't say if Sony has provided any guidance on needing to change black
level.
Perhaps Manivannan has some further information on this as he added
the test pattern support.


As it stands the code retains the existing (weird) behaviour, and
drops the duplication, so I'll add:

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > ---
> > >  drivers/media/i2c/imx290.c | 53 +++++++++++++++++++++-----------------
> > >  1 file changed, 29 insertions(+), 24 deletions(-)
> > >
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index 135ed55592a1..d9fc6c44b0f0 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -152,6 +152,9 @@
> > >  #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH             1920
> > >  #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT            1080
> > >
> > > +/* Equivalent value for 16bpp */
> > > +#define IMX290_BLACK_LEVEL_DEFAULT                     3840
> > > +
> > >  #define IMX290_NUM_SUPPLIES                            3
> > >
> > >  struct imx290_regval {
> > > @@ -315,7 +318,6 @@ static const struct imx290_regval imx290_10bit_settings[] = {
> > >         { IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
> > >         { IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
> > >         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
> > > -       { IMX290_BLKLEVEL, 60 },
> > >  };
> > >
> > >  static const struct imx290_regval imx290_12bit_settings[] = {
> > > @@ -325,7 +327,6 @@ static const struct imx290_regval imx290_12bit_settings[] = {
> > >         { IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
> > >         { IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
> > >         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
> > > -       { IMX290_BLKLEVEL, 240 },
> > >  };
> > >
> > >  /* supported link frequencies */
> > > @@ -516,35 +517,43 @@ static int imx290_set_data_lanes(struct imx290 *imx290)
> > >         return ret;
> > >  }
> > >
> > > +static int imx290_set_black_level(struct imx290 *imx290,
> > > +                                 unsigned int black_level, int *err)
> > > +{
> > > +       return imx290_write(imx290, IMX290_BLKLEVEL,
> > > +                           black_level >> (16 - imx290->bpp), err);
> > > +}
> > > +
> > >  static int imx290_write_current_format(struct imx290 *imx290)
> > >  {
> > > +       const struct imx290_regval *regs;
> > > +       unsigned int num_regs;
> > > +       unsigned int bpp;
> > >         int ret;
> > >
> > >         switch (imx290->current_format.code) {
> > >         case MEDIA_BUS_FMT_SRGGB10_1X10:
> > > -               ret = imx290_set_register_array(imx290, imx290_10bit_settings,
> > > -                                               ARRAY_SIZE(
> > > -                                                       imx290_10bit_settings));
> > > -               if (ret < 0) {
> > > -                       dev_err(imx290->dev, "Could not set format registers\n");
> > > -                       return ret;
> > > -               }
> > > +               regs = imx290_10bit_settings;
> > > +               num_regs = ARRAY_SIZE(imx290_10bit_settings);
> > > +               bpp = 10;
> > >                 break;
> > >         case MEDIA_BUS_FMT_SRGGB12_1X12:
> > > -               ret = imx290_set_register_array(imx290, imx290_12bit_settings,
> > > -                                               ARRAY_SIZE(
> > > -                                                       imx290_12bit_settings));
> > > -               if (ret < 0) {
> > > -                       dev_err(imx290->dev, "Could not set format registers\n");
> > > -                       return ret;
> > > -               }
> > > +               regs = imx290_12bit_settings;
> > > +               num_regs = ARRAY_SIZE(imx290_12bit_settings);
> > > +               bpp = 12;
> > >                 break;
> > >         default:
> > >                 dev_err(imx290->dev, "Unknown pixel format\n");
> > >                 return -EINVAL;
> > >         }
> > >
> > > -       return 0;
> > > +       ret = imx290_set_register_array(imx290, regs, num_regs);
> > > +       if (ret < 0) {
> > > +               dev_err(imx290->dev, "Could not set format registers\n");
> > > +               return ret;
> > > +       }
> > > +
> > > +       return imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT, &ret);
> > >  }
> > >
> > >  /* ----------------------------------------------------------------------------
> > > @@ -573,7 +582,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > >
> > >         case V4L2_CID_TEST_PATTERN:
> > >                 if (ctrl->val) {
> > > -                       imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
> > > +                       imx290_set_black_level(imx290, 0, &ret);
> > >                         usleep_range(10000, 11000);
> > >                         imx290_write(imx290, IMX290_PGCTRL,
> > >                                      (u8)(IMX290_PGCTRL_REGEN |
> > > @@ -582,12 +591,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > >                 } else {
> > >                         imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
> > >                         usleep_range(10000, 11000);
> > > -                       if (imx290->bpp == 10)
> > > -                               imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
> > > -                                            &ret);
> > > -                       else /* 12 bits per pixel */
> > > -                               imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
> > > -                                            &ret);
> > > +                       imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT,
> > > +                                              &ret);
> > >                 }
> > >                 break;
> > >         default:
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function
  2022-11-24 15:10       ` Dave Stevenson
@ 2022-11-24 16:57         ` Laurent Pinchart
  2022-11-24 17:16           ` David Plowman
  0 siblings, 1 reply; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-24 16:57 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam, David Plowman

Hi Dave,

On Thu, Nov 24, 2022 at 03:10:02PM +0000, Dave Stevenson wrote:
> On Wed, 23 Nov 2022 at 11:00, Laurent Pinchart wrote:
> > On Wed, Nov 23, 2022 at 10:08:19AM +0000, Dave Stevenson wrote:
> > > On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart wrote:
> > > >
> > > > The black level programmed in the BLKLEVEL register depends on the
> > > > output format. The black level value computation is currently performed
> > > > in imx290_set_ctrl(), in addition to having different black level values
> > > > in the output-specific register value tables. Move it to a separate
> > > > function to simplify the imx290_set_ctrl() code.
> > >
> > > Does this actually help?
> > > The sensor only supports 10 and 12 bpp readout, so making it generic
> > > for any bpp is rather redundant.
> > > If you are looking at having different on-sensor black level values
> > > kicking around, then tuning data goes out the window.
> >
> > This may be needed for some use cases, but that's not what I'm after
> > here. By the way, could you explain why we can't just set it to 0 in the
> > sensor and avoid subtracting it in the ISP (at least for the data
> > pedestal value that is added on purpose in the sensor, there can also be
> > an additional black level due to other reasons) ? I'm sure there's a
> > reason :-)
> 
> A quick play does imply that it works with 0 being defined in both
> places, but personally I'd prefer following the manufacturer's
> guidance on this sort of setting. David P may have some bright ideas
> as to why a pedestal is desired.
> 
> There is a little bit of me thinking that if it were 0, then the mono
> variant of the sensor with a lens exhibiting minimal vignetting could
> be used directly for 10 or 12bit data under controlled lighting.
> However that is a very specific and unusual use case, so not worth
> potentially compromising IQ for normal use cases.

One reason I can think of is to get the dark areas away from
non-linearities in the analogue path, but I lack visibility (no pun
intended) on the real issues this could cause. Some insight from David
would indeed be very appreciated.

> > > OK we do appear to have duplication between imx290_[10|12]bit_settings
> > > and imx290_set_ctrl, but I don't get the need to compute it.
> >
> > The idea was to centralize black level setting in a single place, to
> > prepare for the removal of he imx290->bpp field. It's really in
> > imx290_set_ctrl() that it matters, I could keep the value in the
> > imx290_10bit_settings and imx290_12bit_settings arrays, but I thought I
> > could as well do it the same way everywhere.
> 
> I'd missed that it was reprogramming the black level for the test patterns.
> 
> Changing the black level when choosing a test pattern seems a very odd
> thing to do. You're wanting to test the pipeline as configured for the
> sensor, but you're artificially changing the incoming data and
> therefore any processing steps are misconfigured.

Now that you mention it, it's indeed very clear that there's a problem
there. We shouldn't have to change ISP parameters when switching to a
test pattern (some things may need to change in the algorithms though,
AGC and AWB won't necessarily work nicely).

> Looking at the data it's weird. Viewing the data as
> V4L2_PIX_FMT_SRGGB12 Pattern 4 as is gives a 12 pixel pattern of
> (bytes reversed):
> 0105 0105 c204 c204 3e01 3e01 0000 0000 0000 0000 0000 0105
> repeated multiple times along each line, and then that line repeated
> vertically. So we definitely have pixels below the black level.
> Alter the black level back to the default 0xf0 and you get:
> f105 f105 b205 b205 2e02 2e02 0000 0000 9c00 9c00 0000 f105
> So it's added the offset of 0xf0 to the non-zero pixels, but only some
> of the zero ones and then not by the configured amount.

What the... :-S

> My datasheets have no reference to a PGCTRL register or 0x308c, so I
> can't say if Sony has provided any guidance on needing to change black level.
> Perhaps Manivannan has some further information on this as he added
> the test pattern support.

I thought I saw documentation about that, but it was for the IMX296, not
the IMX290.

> As it stands the code retains the existing (weird) behaviour, and
> drops the duplication, so I'll add:
> 
> Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

Thanks. We can fix the test pattern on top.

> > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > ---
> > > >  drivers/media/i2c/imx290.c | 53 +++++++++++++++++++++-----------------
> > > >  1 file changed, 29 insertions(+), 24 deletions(-)
> > > >
> > > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > > index 135ed55592a1..d9fc6c44b0f0 100644
> > > > --- a/drivers/media/i2c/imx290.c
> > > > +++ b/drivers/media/i2c/imx290.c
> > > > @@ -152,6 +152,9 @@
> > > >  #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH             1920
> > > >  #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT            1080
> > > >
> > > > +/* Equivalent value for 16bpp */
> > > > +#define IMX290_BLACK_LEVEL_DEFAULT                     3840
> > > > +
> > > >  #define IMX290_NUM_SUPPLIES                            3
> > > >
> > > >  struct imx290_regval {
> > > > @@ -315,7 +318,6 @@ static const struct imx290_regval imx290_10bit_settings[] = {
> > > >         { IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
> > > >         { IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
> > > >         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
> > > > -       { IMX290_BLKLEVEL, 60 },
> > > >  };
> > > >
> > > >  static const struct imx290_regval imx290_12bit_settings[] = {
> > > > @@ -325,7 +327,6 @@ static const struct imx290_regval imx290_12bit_settings[] = {
> > > >         { IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
> > > >         { IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
> > > >         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
> > > > -       { IMX290_BLKLEVEL, 240 },
> > > >  };
> > > >
> > > >  /* supported link frequencies */
> > > > @@ -516,35 +517,43 @@ static int imx290_set_data_lanes(struct imx290 *imx290)
> > > >         return ret;
> > > >  }
> > > >
> > > > +static int imx290_set_black_level(struct imx290 *imx290,
> > > > +                                 unsigned int black_level, int *err)
> > > > +{
> > > > +       return imx290_write(imx290, IMX290_BLKLEVEL,
> > > > +                           black_level >> (16 - imx290->bpp), err);
> > > > +}
> > > > +
> > > >  static int imx290_write_current_format(struct imx290 *imx290)
> > > >  {
> > > > +       const struct imx290_regval *regs;
> > > > +       unsigned int num_regs;
> > > > +       unsigned int bpp;
> > > >         int ret;
> > > >
> > > >         switch (imx290->current_format.code) {
> > > >         case MEDIA_BUS_FMT_SRGGB10_1X10:
> > > > -               ret = imx290_set_register_array(imx290, imx290_10bit_settings,
> > > > -                                               ARRAY_SIZE(
> > > > -                                                       imx290_10bit_settings));
> > > > -               if (ret < 0) {
> > > > -                       dev_err(imx290->dev, "Could not set format registers\n");
> > > > -                       return ret;
> > > > -               }
> > > > +               regs = imx290_10bit_settings;
> > > > +               num_regs = ARRAY_SIZE(imx290_10bit_settings);
> > > > +               bpp = 10;
> > > >                 break;
> > > >         case MEDIA_BUS_FMT_SRGGB12_1X12:
> > > > -               ret = imx290_set_register_array(imx290, imx290_12bit_settings,
> > > > -                                               ARRAY_SIZE(
> > > > -                                                       imx290_12bit_settings));
> > > > -               if (ret < 0) {
> > > > -                       dev_err(imx290->dev, "Could not set format registers\n");
> > > > -                       return ret;
> > > > -               }
> > > > +               regs = imx290_12bit_settings;
> > > > +               num_regs = ARRAY_SIZE(imx290_12bit_settings);
> > > > +               bpp = 12;
> > > >                 break;
> > > >         default:
> > > >                 dev_err(imx290->dev, "Unknown pixel format\n");
> > > >                 return -EINVAL;
> > > >         }
> > > >
> > > > -       return 0;
> > > > +       ret = imx290_set_register_array(imx290, regs, num_regs);
> > > > +       if (ret < 0) {
> > > > +               dev_err(imx290->dev, "Could not set format registers\n");
> > > > +               return ret;
> > > > +       }
> > > > +
> > > > +       return imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT, &ret);
> > > >  }
> > > >
> > > >  /* ----------------------------------------------------------------------------
> > > > @@ -573,7 +582,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > >
> > > >         case V4L2_CID_TEST_PATTERN:
> > > >                 if (ctrl->val) {
> > > > -                       imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
> > > > +                       imx290_set_black_level(imx290, 0, &ret);
> > > >                         usleep_range(10000, 11000);
> > > >                         imx290_write(imx290, IMX290_PGCTRL,
> > > >                                      (u8)(IMX290_PGCTRL_REGEN |
> > > > @@ -582,12 +591,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > >                 } else {
> > > >                         imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
> > > >                         usleep_range(10000, 11000);
> > > > -                       if (imx290->bpp == 10)
> > > > -                               imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
> > > > -                                            &ret);
> > > > -                       else /* 12 bits per pixel */
> > > > -                               imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
> > > > -                                            &ret);
> > > > +                       imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT,
> > > > +                                              &ret);
> > > >                 }
> > > >                 break;
> > > >         default:

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function
  2022-11-24 16:57         ` Laurent Pinchart
@ 2022-11-24 17:16           ` David Plowman
  2022-11-24 18:02             ` Laurent Pinchart
  0 siblings, 1 reply; 50+ messages in thread
From: David Plowman @ 2022-11-24 17:16 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Dave Stevenson, linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi

On Thu, 24 Nov 2022 at 16:58, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Dave,
>
> On Thu, Nov 24, 2022 at 03:10:02PM +0000, Dave Stevenson wrote:
> > On Wed, 23 Nov 2022 at 11:00, Laurent Pinchart wrote:
> > > On Wed, Nov 23, 2022 at 10:08:19AM +0000, Dave Stevenson wrote:
> > > > On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart wrote:
> > > > >
> > > > > The black level programmed in the BLKLEVEL register depends on the
> > > > > output format. The black level value computation is currently performed
> > > > > in imx290_set_ctrl(), in addition to having different black level values
> > > > > in the output-specific register value tables. Move it to a separate
> > > > > function to simplify the imx290_set_ctrl() code.
> > > >
> > > > Does this actually help?
> > > > The sensor only supports 10 and 12 bpp readout, so making it generic
> > > > for any bpp is rather redundant.
> > > > If you are looking at having different on-sensor black level values
> > > > kicking around, then tuning data goes out the window.
> > >
> > > This may be needed for some use cases, but that's not what I'm after
> > > here. By the way, could you explain why we can't just set it to 0 in the
> > > sensor and avoid subtracting it in the ISP (at least for the data
> > > pedestal value that is added on purpose in the sensor, there can also be
> > > an additional black level due to other reasons) ? I'm sure there's a
> > > reason :-)
> >
> > A quick play does imply that it works with 0 being defined in both
> > places, but personally I'd prefer following the manufacturer's
> > guidance on this sort of setting. David P may have some bright ideas
> > as to why a pedestal is desired.
> >
> > There is a little bit of me thinking that if it were 0, then the mono
> > variant of the sensor with a lens exhibiting minimal vignetting could
> > be used directly for 10 or 12bit data under controlled lighting.
> > However that is a very specific and unusual use case, so not worth
> > potentially compromising IQ for normal use cases.
>
> One reason I can think of is to get the dark areas away from
> non-linearities in the analogue path, but I lack visibility (no pun
> intended) on the real issues this could cause. Some insight from David
> would indeed be very appreciated.

Not sure I can add very much! If you don't have a black level it's
certainly true that your "zero" pixels will have a very odd
distribution because the noise can't take them negative. Perhaps
there's a 50% chance of getting zero, and a 50% chance of positive
noise? So that would be like a 50% chance of zero, and 50% of coming
from a "half Normal" distribution? Which would give an expected value
of something like 0.4 * standard deviation. Or something like that!

David

>
> > > > OK we do appear to have duplication between imx290_[10|12]bit_settings
> > > > and imx290_set_ctrl, but I don't get the need to compute it.
> > >
> > > The idea was to centralize black level setting in a single place, to
> > > prepare for the removal of he imx290->bpp field. It's really in
> > > imx290_set_ctrl() that it matters, I could keep the value in the
> > > imx290_10bit_settings and imx290_12bit_settings arrays, but I thought I
> > > could as well do it the same way everywhere.
> >
> > I'd missed that it was reprogramming the black level for the test patterns.
> >
> > Changing the black level when choosing a test pattern seems a very odd
> > thing to do. You're wanting to test the pipeline as configured for the
> > sensor, but you're artificially changing the incoming data and
> > therefore any processing steps are misconfigured.
>
> Now that you mention it, it's indeed very clear that there's a problem
> there. We shouldn't have to change ISP parameters when switching to a
> test pattern (some things may need to change in the algorithms though,
> AGC and AWB won't necessarily work nicely).
>
> > Looking at the data it's weird. Viewing the data as
> > V4L2_PIX_FMT_SRGGB12 Pattern 4 as is gives a 12 pixel pattern of
> > (bytes reversed):
> > 0105 0105 c204 c204 3e01 3e01 0000 0000 0000 0000 0000 0105
> > repeated multiple times along each line, and then that line repeated
> > vertically. So we definitely have pixels below the black level.
> > Alter the black level back to the default 0xf0 and you get:
> > f105 f105 b205 b205 2e02 2e02 0000 0000 9c00 9c00 0000 f105
> > So it's added the offset of 0xf0 to the non-zero pixels, but only some
> > of the zero ones and then not by the configured amount.
>
> What the... :-S
>
> > My datasheets have no reference to a PGCTRL register or 0x308c, so I
> > can't say if Sony has provided any guidance on needing to change black level.
> > Perhaps Manivannan has some further information on this as he added
> > the test pattern support.
>
> I thought I saw documentation about that, but it was for the IMX296, not
> the IMX290.
>
> > As it stands the code retains the existing (weird) behaviour, and
> > drops the duplication, so I'll add:
> >
> > Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
>
> Thanks. We can fix the test pattern on top.
>
> > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > > ---
> > > > >  drivers/media/i2c/imx290.c | 53 +++++++++++++++++++++-----------------
> > > > >  1 file changed, 29 insertions(+), 24 deletions(-)
> > > > >
> > > > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > > > index 135ed55592a1..d9fc6c44b0f0 100644
> > > > > --- a/drivers/media/i2c/imx290.c
> > > > > +++ b/drivers/media/i2c/imx290.c
> > > > > @@ -152,6 +152,9 @@
> > > > >  #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH             1920
> > > > >  #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT            1080
> > > > >
> > > > > +/* Equivalent value for 16bpp */
> > > > > +#define IMX290_BLACK_LEVEL_DEFAULT                     3840
> > > > > +
> > > > >  #define IMX290_NUM_SUPPLIES                            3
> > > > >
> > > > >  struct imx290_regval {
> > > > > @@ -315,7 +318,6 @@ static const struct imx290_regval imx290_10bit_settings[] = {
> > > > >         { IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
> > > > >         { IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
> > > > >         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
> > > > > -       { IMX290_BLKLEVEL, 60 },
> > > > >  };
> > > > >
> > > > >  static const struct imx290_regval imx290_12bit_settings[] = {
> > > > > @@ -325,7 +327,6 @@ static const struct imx290_regval imx290_12bit_settings[] = {
> > > > >         { IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
> > > > >         { IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
> > > > >         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
> > > > > -       { IMX290_BLKLEVEL, 240 },
> > > > >  };
> > > > >
> > > > >  /* supported link frequencies */
> > > > > @@ -516,35 +517,43 @@ static int imx290_set_data_lanes(struct imx290 *imx290)
> > > > >         return ret;
> > > > >  }
> > > > >
> > > > > +static int imx290_set_black_level(struct imx290 *imx290,
> > > > > +                                 unsigned int black_level, int *err)
> > > > > +{
> > > > > +       return imx290_write(imx290, IMX290_BLKLEVEL,
> > > > > +                           black_level >> (16 - imx290->bpp), err);
> > > > > +}
> > > > > +
> > > > >  static int imx290_write_current_format(struct imx290 *imx290)
> > > > >  {
> > > > > +       const struct imx290_regval *regs;
> > > > > +       unsigned int num_regs;
> > > > > +       unsigned int bpp;
> > > > >         int ret;
> > > > >
> > > > >         switch (imx290->current_format.code) {
> > > > >         case MEDIA_BUS_FMT_SRGGB10_1X10:
> > > > > -               ret = imx290_set_register_array(imx290, imx290_10bit_settings,
> > > > > -                                               ARRAY_SIZE(
> > > > > -                                                       imx290_10bit_settings));
> > > > > -               if (ret < 0) {
> > > > > -                       dev_err(imx290->dev, "Could not set format registers\n");
> > > > > -                       return ret;
> > > > > -               }
> > > > > +               regs = imx290_10bit_settings;
> > > > > +               num_regs = ARRAY_SIZE(imx290_10bit_settings);
> > > > > +               bpp = 10;
> > > > >                 break;
> > > > >         case MEDIA_BUS_FMT_SRGGB12_1X12:
> > > > > -               ret = imx290_set_register_array(imx290, imx290_12bit_settings,
> > > > > -                                               ARRAY_SIZE(
> > > > > -                                                       imx290_12bit_settings));
> > > > > -               if (ret < 0) {
> > > > > -                       dev_err(imx290->dev, "Could not set format registers\n");
> > > > > -                       return ret;
> > > > > -               }
> > > > > +               regs = imx290_12bit_settings;
> > > > > +               num_regs = ARRAY_SIZE(imx290_12bit_settings);
> > > > > +               bpp = 12;
> > > > >                 break;
> > > > >         default:
> > > > >                 dev_err(imx290->dev, "Unknown pixel format\n");
> > > > >                 return -EINVAL;
> > > > >         }
> > > > >
> > > > > -       return 0;
> > > > > +       ret = imx290_set_register_array(imx290, regs, num_regs);
> > > > > +       if (ret < 0) {
> > > > > +               dev_err(imx290->dev, "Could not set format registers\n");
> > > > > +               return ret;
> > > > > +       }
> > > > > +
> > > > > +       return imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT, &ret);
> > > > >  }
> > > > >
> > > > >  /* ----------------------------------------------------------------------------
> > > > > @@ -573,7 +582,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > > >
> > > > >         case V4L2_CID_TEST_PATTERN:
> > > > >                 if (ctrl->val) {
> > > > > -                       imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
> > > > > +                       imx290_set_black_level(imx290, 0, &ret);
> > > > >                         usleep_range(10000, 11000);
> > > > >                         imx290_write(imx290, IMX290_PGCTRL,
> > > > >                                      (u8)(IMX290_PGCTRL_REGEN |
> > > > > @@ -582,12 +591,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > > >                 } else {
> > > > >                         imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
> > > > >                         usleep_range(10000, 11000);
> > > > > -                       if (imx290->bpp == 10)
> > > > > -                               imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
> > > > > -                                            &ret);
> > > > > -                       else /* 12 bits per pixel */
> > > > > -                               imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
> > > > > -                                            &ret);
> > > > > +                       imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT,
> > > > > +                                              &ret);
> > > > >                 }
> > > > >                 break;
> > > > >         default:
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function
  2022-11-24 17:16           ` David Plowman
@ 2022-11-24 18:02             ` Laurent Pinchart
  0 siblings, 0 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-24 18:02 UTC (permalink / raw)
  To: David Plowman
  Cc: Dave Stevenson, linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi David,

On Thu, Nov 24, 2022 at 05:16:17PM +0000, David Plowman wrote:
> On Thu, 24 Nov 2022 at 16:58, Laurent Pinchart wrote:
> > On Thu, Nov 24, 2022 at 03:10:02PM +0000, Dave Stevenson wrote:
> > > On Wed, 23 Nov 2022 at 11:00, Laurent Pinchart wrote:
> > > > On Wed, Nov 23, 2022 at 10:08:19AM +0000, Dave Stevenson wrote:
> > > > > On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart wrote:
> > > > > >
> > > > > > The black level programmed in the BLKLEVEL register depends on the
> > > > > > output format. The black level value computation is currently performed
> > > > > > in imx290_set_ctrl(), in addition to having different black level values
> > > > > > in the output-specific register value tables. Move it to a separate
> > > > > > function to simplify the imx290_set_ctrl() code.
> > > > >
> > > > > Does this actually help?
> > > > > The sensor only supports 10 and 12 bpp readout, so making it generic
> > > > > for any bpp is rather redundant.
> > > > > If you are looking at having different on-sensor black level values
> > > > > kicking around, then tuning data goes out the window.
> > > >
> > > > This may be needed for some use cases, but that's not what I'm after
> > > > here. By the way, could you explain why we can't just set it to 0 in the
> > > > sensor and avoid subtracting it in the ISP (at least for the data
> > > > pedestal value that is added on purpose in the sensor, there can also be
> > > > an additional black level due to other reasons) ? I'm sure there's a
> > > > reason :-)
> > >
> > > A quick play does imply that it works with 0 being defined in both
> > > places, but personally I'd prefer following the manufacturer's
> > > guidance on this sort of setting. David P may have some bright ideas
> > > as to why a pedestal is desired.
> > >
> > > There is a little bit of me thinking that if it were 0, then the mono
> > > variant of the sensor with a lens exhibiting minimal vignetting could
> > > be used directly for 10 or 12bit data under controlled lighting.
> > > However that is a very specific and unusual use case, so not worth
> > > potentially compromising IQ for normal use cases.
> >
> > One reason I can think of is to get the dark areas away from
> > non-linearities in the analogue path, but I lack visibility (no pun
> > intended) on the real issues this could cause. Some insight from David
> > would indeed be very appreciated.
> 
> Not sure I can add very much! If you don't have a black level it's
> certainly true that your "zero" pixels will have a very odd
> distribution because the noise can't take them negative. Perhaps
> there's a 50% chance of getting zero, and a 50% chance of positive
> noise? So that would be like a 50% chance of zero, and 50% of coming
> from a "half Normal" distribution? Which would give an expected value
> of something like 0.4 * standard deviation. Or something like that!

Thanks for the feedback. I wonder if a good rule would be to set the
pedestal to N * sigma, for some value of N, to avoid messing up the
noise distribution which could impact denoising negatively. Or maybe
that's not something that will happen in practice and the denoising
algorithms will be totally fine, maybe the only practical issue with no
pedestal would be the non-linearity of the analogue path close to 0.

> > > > > OK we do appear to have duplication between imx290_[10|12]bit_settings
> > > > > and imx290_set_ctrl, but I don't get the need to compute it.
> > > >
> > > > The idea was to centralize black level setting in a single place, to
> > > > prepare for the removal of he imx290->bpp field. It's really in
> > > > imx290_set_ctrl() that it matters, I could keep the value in the
> > > > imx290_10bit_settings and imx290_12bit_settings arrays, but I thought I
> > > > could as well do it the same way everywhere.
> > >
> > > I'd missed that it was reprogramming the black level for the test patterns.
> > >
> > > Changing the black level when choosing a test pattern seems a very odd
> > > thing to do. You're wanting to test the pipeline as configured for the
> > > sensor, but you're artificially changing the incoming data and
> > > therefore any processing steps are misconfigured.
> >
> > Now that you mention it, it's indeed very clear that there's a problem
> > there. We shouldn't have to change ISP parameters when switching to a
> > test pattern (some things may need to change in the algorithms though,
> > AGC and AWB won't necessarily work nicely).
> >
> > > Looking at the data it's weird. Viewing the data as
> > > V4L2_PIX_FMT_SRGGB12 Pattern 4 as is gives a 12 pixel pattern of
> > > (bytes reversed):
> > > 0105 0105 c204 c204 3e01 3e01 0000 0000 0000 0000 0000 0105
> > > repeated multiple times along each line, and then that line repeated
> > > vertically. So we definitely have pixels below the black level.
> > > Alter the black level back to the default 0xf0 and you get:
> > > f105 f105 b205 b205 2e02 2e02 0000 0000 9c00 9c00 0000 f105
> > > So it's added the offset of 0xf0 to the non-zero pixels, but only some
> > > of the zero ones and then not by the configured amount.
> >
> > What the... :-S
> >
> > > My datasheets have no reference to a PGCTRL register or 0x308c, so I
> > > can't say if Sony has provided any guidance on needing to change black level.
> > > Perhaps Manivannan has some further information on this as he added
> > > the test pattern support.
> >
> > I thought I saw documentation about that, but it was for the IMX296, not
> > the IMX290.
> >
> > > As it stands the code retains the existing (weird) behaviour, and
> > > drops the duplication, so I'll add:
> > >
> > > Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> >
> > Thanks. We can fix the test pattern on top.
> >
> > > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > > > ---
> > > > > >  drivers/media/i2c/imx290.c | 53 +++++++++++++++++++++-----------------
> > > > > >  1 file changed, 29 insertions(+), 24 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > > > > index 135ed55592a1..d9fc6c44b0f0 100644
> > > > > > --- a/drivers/media/i2c/imx290.c
> > > > > > +++ b/drivers/media/i2c/imx290.c
> > > > > > @@ -152,6 +152,9 @@
> > > > > >  #define IMX290_PIXEL_ARRAY_RECORDING_WIDTH             1920
> > > > > >  #define IMX290_PIXEL_ARRAY_RECORDING_HEIGHT            1080
> > > > > >
> > > > > > +/* Equivalent value for 16bpp */
> > > > > > +#define IMX290_BLACK_LEVEL_DEFAULT                     3840
> > > > > > +
> > > > > >  #define IMX290_NUM_SUPPLIES                            3
> > > > > >
> > > > > >  struct imx290_regval {
> > > > > > @@ -315,7 +318,6 @@ static const struct imx290_regval imx290_10bit_settings[] = {
> > > > > >         { IMX290_ADBIT2, IMX290_ADBIT2_10BIT },
> > > > > >         { IMX290_ADBIT3, IMX290_ADBIT3_10BIT },
> > > > > >         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 },
> > > > > > -       { IMX290_BLKLEVEL, 60 },
> > > > > >  };
> > > > > >
> > > > > >  static const struct imx290_regval imx290_12bit_settings[] = {
> > > > > > @@ -325,7 +327,6 @@ static const struct imx290_regval imx290_12bit_settings[] = {
> > > > > >         { IMX290_ADBIT2, IMX290_ADBIT2_12BIT },
> > > > > >         { IMX290_ADBIT3, IMX290_ADBIT3_12BIT },
> > > > > >         { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 },
> > > > > > -       { IMX290_BLKLEVEL, 240 },
> > > > > >  };
> > > > > >
> > > > > >  /* supported link frequencies */
> > > > > > @@ -516,35 +517,43 @@ static int imx290_set_data_lanes(struct imx290 *imx290)
> > > > > >         return ret;
> > > > > >  }
> > > > > >
> > > > > > +static int imx290_set_black_level(struct imx290 *imx290,
> > > > > > +                                 unsigned int black_level, int *err)
> > > > > > +{
> > > > > > +       return imx290_write(imx290, IMX290_BLKLEVEL,
> > > > > > +                           black_level >> (16 - imx290->bpp), err);
> > > > > > +}
> > > > > > +
> > > > > >  static int imx290_write_current_format(struct imx290 *imx290)
> > > > > >  {
> > > > > > +       const struct imx290_regval *regs;
> > > > > > +       unsigned int num_regs;
> > > > > > +       unsigned int bpp;
> > > > > >         int ret;
> > > > > >
> > > > > >         switch (imx290->current_format.code) {
> > > > > >         case MEDIA_BUS_FMT_SRGGB10_1X10:
> > > > > > -               ret = imx290_set_register_array(imx290, imx290_10bit_settings,
> > > > > > -                                               ARRAY_SIZE(
> > > > > > -                                                       imx290_10bit_settings));
> > > > > > -               if (ret < 0) {
> > > > > > -                       dev_err(imx290->dev, "Could not set format registers\n");
> > > > > > -                       return ret;
> > > > > > -               }
> > > > > > +               regs = imx290_10bit_settings;
> > > > > > +               num_regs = ARRAY_SIZE(imx290_10bit_settings);
> > > > > > +               bpp = 10;
> > > > > >                 break;
> > > > > >         case MEDIA_BUS_FMT_SRGGB12_1X12:
> > > > > > -               ret = imx290_set_register_array(imx290, imx290_12bit_settings,
> > > > > > -                                               ARRAY_SIZE(
> > > > > > -                                                       imx290_12bit_settings));
> > > > > > -               if (ret < 0) {
> > > > > > -                       dev_err(imx290->dev, "Could not set format registers\n");
> > > > > > -                       return ret;
> > > > > > -               }
> > > > > > +               regs = imx290_12bit_settings;
> > > > > > +               num_regs = ARRAY_SIZE(imx290_12bit_settings);
> > > > > > +               bpp = 12;
> > > > > >                 break;
> > > > > >         default:
> > > > > >                 dev_err(imx290->dev, "Unknown pixel format\n");
> > > > > >                 return -EINVAL;
> > > > > >         }
> > > > > >
> > > > > > -       return 0;
> > > > > > +       ret = imx290_set_register_array(imx290, regs, num_regs);
> > > > > > +       if (ret < 0) {
> > > > > > +               dev_err(imx290->dev, "Could not set format registers\n");
> > > > > > +               return ret;
> > > > > > +       }
> > > > > > +
> > > > > > +       return imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT, &ret);
> > > > > >  }
> > > > > >
> > > > > >  /* ----------------------------------------------------------------------------
> > > > > > @@ -573,7 +582,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > > > >
> > > > > >         case V4L2_CID_TEST_PATTERN:
> > > > > >                 if (ctrl->val) {
> > > > > > -                       imx290_write(imx290, IMX290_BLKLEVEL, 0, &ret);
> > > > > > +                       imx290_set_black_level(imx290, 0, &ret);
> > > > > >                         usleep_range(10000, 11000);
> > > > > >                         imx290_write(imx290, IMX290_PGCTRL,
> > > > > >                                      (u8)(IMX290_PGCTRL_REGEN |
> > > > > > @@ -582,12 +591,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > > > >                 } else {
> > > > > >                         imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret);
> > > > > >                         usleep_range(10000, 11000);
> > > > > > -                       if (imx290->bpp == 10)
> > > > > > -                               imx290_write(imx290, IMX290_BLKLEVEL, 0x3c,
> > > > > > -                                            &ret);
> > > > > > -                       else /* 12 bits per pixel */
> > > > > > -                               imx290_write(imx290, IMX290_BLKLEVEL, 0xf0,
> > > > > > -                                            &ret);
> > > > > > +                       imx290_set_black_level(imx290, IMX290_BLACK_LEVEL_DEFAULT,
> > > > > > +                                              &ret);
> > > > > >                 }
> > > > > >                 break;
> > > > > >         default:

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time
  2022-11-23 14:28     ` Laurent Pinchart
@ 2022-11-24 18:02       ` Dave Stevenson
  2022-11-24 19:19         ` Laurent Pinchart
  0 siblings, 1 reply; 50+ messages in thread
From: Dave Stevenson @ 2022-11-24 18:02 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Laurent

On Wed, 23 Nov 2022 at 14:29, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Dave,
>
> On Wed, Nov 23, 2022 at 10:16:58AM +0000, Dave Stevenson wrote:
> > On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart wrote:
> > >
> > > There's no need to configure the data lanes in the runtime PM resume
> > > handler. Do so in imx290_start_streaming() instead.
> >
> > Interesting as I had Sakari advocating putting clock mode selection
> > register control in "power on" for my recent ov9282 series. Is there
> > any consistency?
>
> No there isn't :-) There hasn't been any official rule so far, so it's
> no surprise different drivers exhibit different behaviours. I'm all for
> standardization when possible though.

Likewise! Standardisation is a good thing!

Sorry my comment was slightly tongue-in-cheek due to having had such a
similar thread with Sakari so recently. When a long-standing member of
the community then comes along with a similar patch it just reinforced
that, in the absence of any documented guidelines, it is all very much
ad-hoc.
It then frustrates me that these sorts of issues are then raised at
review, which either results in having to justify the choice, or
respinning patches often with time constraints if trying to hit a
merge window.

> Overall, I think there's a general agreement that the runtime PM resume
> handler needs to control everything required to make the sensor
> accessible by software. That covers at least hard reset, regulators and
> clocks.
>
> For software settings, it's less clear. If the sensor requires a
> software reset sequence immediately after power on, it makes sense to
> also handle that in the runtime PM resume handler. Same thing for any
> other initialization required to reach a quiescent state (for instance
> there are many sensors that start streaming automatically right after
> power on for a reason I can't understand, so that needs to be disabled).
>
> This means that the runtime PM handler will thus access the sensor over
> I2C. We may not want to do so in probe() before having a chance to probe
> it (by reading an ID register for instance). The power on sequence could
> be split in two to handle this, with one function that powers the sensor
> up, and the other one that handles software initialization. Both would
> be called from the runtime PM resume handler, and in probe(), we could
> first power on the sensor, identify it, and then initialize it. I think
> that will be fine on DT platforms as we don't need to RPM-resume the I2C
> device in probe before accessing it as far as I can tell, given that the
> probe() function should be called with the I2C controller RPM-resumed.
> I'll let Sakari confirms if this works for ACPI).

For ov9282 I'd also raised the issue that a fair number of sensor
drivers include a software reset in their lists of registers, which
will undo any settings done in resume.

As above, it was more of an observation than issue with this patch.
Alexander has already given an R-b, so there's limited point adding mine.

I'll try and test the series out tomorrow, and I will get around to
submitting my series on top.

Cheers.
  Dave

> For other settings, I wouldn't handle them in the runtime PM resume
> handler. In this particular case, the number of data lanes could even
> vary based on the sensor mode (we don't do so at the moment), so
> .s_stream() time seems better to me.
>
> > https://patchwork.linuxtv.org/project/linux-media/patch/20221005152809.3785786-9-dave.stevenson@raspberrypi.com/#141118
> >
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > ---
> > >  drivers/media/i2c/imx290.c | 6 +++---
> > >  1 file changed, 3 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index dbed703fa199..4dfa090f918d 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -753,6 +753,9 @@ static int imx290_start_streaming(struct imx290 *imx290,
> > >                 return ret;
> > >         }
> > >
> > > +       /* Set data lane count */
> > > +       imx290_set_data_lanes(imx290);
> > > +
> > >         /* 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);
> > > @@ -1052,9 +1055,6 @@ static int imx290_power_on(struct device *dev)
> > >         gpiod_set_value_cansleep(imx290->rst_gpio, 0);
> > >         usleep_range(30000, 31000);
> > >
> > > -       /* Set data lane count */
> > > -       imx290_set_data_lanes(imx290);
> > > -
> > >         return 0;
> > >  }
> > >
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [PATCH v1 02/15] media: i2c: imx290: Factor out subdev init and cleanup to functions
  2022-11-22 22:32 ` [PATCH v1 02/15] media: i2c: imx290: Factor out subdev init and cleanup to functions Laurent Pinchart
  2022-11-23  7:44   ` Alexander Stein
@ 2022-11-24 18:31   ` Dave Stevenson
  2022-11-24 19:25     ` Laurent Pinchart
  1 sibling, 1 reply; 50+ messages in thread
From: Dave Stevenson @ 2022-11-24 18:31 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Laurent

On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> The probe() function is large. Make it more readable by factoring the
> subdev initialization code out. While at it, rename the error labels as
> the "free_" prefix isn't accurate.
>
> No functional change intended.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/media/i2c/imx290.c | 109 +++++++++++++++++++++----------------
>  1 file changed, 62 insertions(+), 47 deletions(-)
>
> diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> index aae4f51941a1..4dbf218e7a63 100644
> --- a/drivers/media/i2c/imx290.c
> +++ b/drivers/media/i2c/imx290.c
> @@ -1015,6 +1015,46 @@ static const struct media_entity_operations imx290_subdev_entity_ops = {
>         .link_validate = v4l2_subdev_link_validate,
>  };
>
> +static int imx290_subdev_init(struct imx290 *imx290)
> +{
> +       struct i2c_client *client = to_i2c_client(imx290->dev);
> +       int ret;
> +
> +       /*
> +        * Initialize the frame format. In particular, imx290->current_mode
> +        * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
> +        * below relies on these fields.
> +        */
> +       imx290_entity_init_cfg(&imx290->sd, NULL);
> +
> +       ret = imx290_ctrl_init(imx290);
> +       if (ret < 0) {
> +               dev_err(imx290->dev, "Control initialization error %d\n", ret);
> +               return ret;
> +       }
> +
> +       v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> +       imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +       imx290->sd.dev = imx290->dev;
> +       imx290->sd.entity.ops = &imx290_subdev_entity_ops;
> +       imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +
> +       imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
> +       ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
> +       if (ret < 0) {
> +               dev_err(imx290->dev, "Could not register media entity\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static void imx290_subdev_cleanup(struct imx290 *imx290)
> +{
> +       media_entity_cleanup(&imx290->sd.entity);
> +       v4l2_ctrl_handler_free(&imx290->ctrls);
> +}
> +
>  /* ----------------------------------------------------------------------------
>   * Power management
>   */
> @@ -1148,10 +1188,10 @@ static int imx290_probe(struct i2c_client *client)
>         fwnode_handle_put(endpoint);
>         if (ret == -ENXIO) {
>                 dev_err(dev, "Unsupported bus type, should be CSI2\n");
> -               goto free_err;
> +               goto err_endpoint;
>         } else if (ret) {
>                 dev_err(dev, "Parsing endpoint node failed\n");
> -               goto free_err;
> +               goto err_endpoint;
>         }
>
>         /* Get number of data lanes */
> @@ -1159,7 +1199,7 @@ static int imx290_probe(struct i2c_client *client)
>         if (imx290->nlanes != 2 && imx290->nlanes != 4) {
>                 dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
>                 ret = -EINVAL;
> -               goto free_err;
> +               goto err_endpoint;
>         }
>
>         dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
> @@ -1167,7 +1207,7 @@ static int imx290_probe(struct i2c_client *client)
>         if (!ep.nr_of_link_frequencies) {
>                 dev_err(dev, "link-frequency property not found in DT\n");
>                 ret = -EINVAL;
> -               goto free_err;
> +               goto err_endpoint;
>         }
>
>         /* Check that link frequences for all the modes are in device tree */
> @@ -1175,7 +1215,7 @@ static int imx290_probe(struct i2c_client *client)
>         if (fq) {
>                 dev_err(dev, "Link frequency of %lld is not supported\n", fq);
>                 ret = -EINVAL;
> -               goto free_err;
> +               goto err_endpoint;
>         }
>
>         /* get system clock (xclk) */
> @@ -1183,14 +1223,14 @@ static int imx290_probe(struct i2c_client *client)
>         if (IS_ERR(imx290->xclk)) {
>                 dev_err(dev, "Could not get xclk");
>                 ret = PTR_ERR(imx290->xclk);
> -               goto free_err;
> +               goto err_endpoint;
>         }
>
>         ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
>                                        &xclk_freq);
>         if (ret) {
>                 dev_err(dev, "Could not get xclk frequency\n");
> -               goto free_err;
> +               goto err_endpoint;
>         }
>
>         /* external clock must be 37.125 MHz */
> @@ -1198,19 +1238,19 @@ static int imx290_probe(struct i2c_client *client)
>                 dev_err(dev, "External clock frequency %u is not supported\n",
>                         xclk_freq);
>                 ret = -EINVAL;
> -               goto free_err;
> +               goto err_endpoint;
>         }
>
>         ret = clk_set_rate(imx290->xclk, xclk_freq);
>         if (ret) {
>                 dev_err(dev, "Could not set xclk frequency\n");
> -               goto free_err;
> +               goto err_endpoint;
>         }
>
>         ret = imx290_get_regulators(dev, imx290);
>         if (ret < 0) {
>                 dev_err(dev, "Cannot get regulators\n");
> -               goto free_err;
> +               goto err_endpoint;
>         }
>
>         imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
> @@ -1218,55 +1258,33 @@ static int imx290_probe(struct i2c_client *client)
>         if (IS_ERR(imx290->rst_gpio)) {
>                 dev_err(dev, "Cannot get reset gpio\n");
>                 ret = PTR_ERR(imx290->rst_gpio);
> -               goto free_err;
> +               goto err_endpoint;
>         }
>
>         mutex_init(&imx290->lock);
>
> -       /*
> -        * Initialize the frame format. In particular, imx290->current_mode
> -        * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
> -        * below relies on these fields.
> -        */
> -       imx290_entity_init_cfg(&imx290->sd, NULL);
> -
> -       ret = imx290_ctrl_init(imx290);
> -       if (ret < 0) {
> -               dev_err(dev, "Control initialization error %d\n", ret);
> -               goto free_mutex;
> -       }
> -
> -       v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> -       imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> -       imx290->sd.dev = &client->dev;
> -       imx290->sd.entity.ops = &imx290_subdev_entity_ops;
> -       imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> -
> -       imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
> -       ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
> -       if (ret < 0) {
> -               dev_err(dev, "Could not register media entity\n");
> -               goto free_ctrl;
> -       }
> +       ret = imx290_subdev_init(imx290);
> +       if (ret)
> +               goto err_mutex;
>
>         ret = v4l2_async_register_subdev(&imx290->sd);
>         if (ret < 0) {
>                 dev_err(dev, "Could not register v4l2 device\n");
> -               goto free_entity;
> +               goto err_subdev;
>         }
>
>         /* Power on the device to match runtime PM state below */
>         ret = imx290_power_on(dev);
>         if (ret < 0) {
>                 dev_err(dev, "Could not power on the device\n");
> -               goto free_entity;
> +               goto err_subdev;
>         }
>
>         ret = imx290_read(imx290, IMX290_CHIP_ID, &chip_id);
>         if (ret) {
>                 dev_err(dev, "Could not read chip ID: %d\n", ret);
>                 imx290_power_off(dev);
> -               goto free_entity;
> +               goto err_subdev;
>         }

Which tree is this patch based on, as neither Sakari nor Mauro's trees
have this read in them.
I suspect an older patch which added it got dropped due to the worry
over Vision Component's modules which block 16 bit reads.

  Dave

>
>         dev_info(dev, "chip ID 0x%04x\n", chip_id);
> @@ -1279,13 +1297,11 @@ static int imx290_probe(struct i2c_client *client)
>
>         return 0;
>
> -free_entity:
> -       media_entity_cleanup(&imx290->sd.entity);
> -free_ctrl:
> -       v4l2_ctrl_handler_free(&imx290->ctrls);
> -free_mutex:
> +err_subdev:
> +       imx290_subdev_cleanup(imx290);
> +err_mutex:
>         mutex_destroy(&imx290->lock);
> -free_err:
> +err_endpoint:
>         v4l2_fwnode_endpoint_free(&ep);
>
>         return ret;
> @@ -1297,8 +1313,7 @@ static void imx290_remove(struct i2c_client *client)
>         struct imx290 *imx290 = to_imx290(sd);
>
>         v4l2_async_unregister_subdev(sd);
> -       media_entity_cleanup(&sd->entity);
> -       v4l2_ctrl_handler_free(sd->ctrl_handler);
> +       imx290_subdev_cleanup(imx290);
>
>         mutex_destroy(&imx290->lock);
>
> --
> Regards,
>
> Laurent Pinchart
>

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

* Re: [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time
  2022-11-24 18:02       ` Dave Stevenson
@ 2022-11-24 19:19         ` Laurent Pinchart
  0 siblings, 0 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-24 19:19 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Dave,

On Thu, Nov 24, 2022 at 06:02:54PM +0000, Dave Stevenson wrote:
> On Wed, 23 Nov 2022 at 14:29, Laurent Pinchart wrote:
> > On Wed, Nov 23, 2022 at 10:16:58AM +0000, Dave Stevenson wrote:
> > > On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart wrote:
> > > >
> > > > There's no need to configure the data lanes in the runtime PM resume
> > > > handler. Do so in imx290_start_streaming() instead.
> > >
> > > Interesting as I had Sakari advocating putting clock mode selection
> > > register control in "power on" for my recent ov9282 series. Is there
> > > any consistency?
> >
> > No there isn't :-) There hasn't been any official rule so far, so it's
> > no surprise different drivers exhibit different behaviours. I'm all for
> > standardization when possible though.
> 
> Likewise! Standardisation is a good thing!
> 
> Sorry my comment was slightly tongue-in-cheek due to having had such a
> similar thread with Sakari so recently. When a long-standing member of
> the community then comes along with a similar patch it just reinforced
> that, in the absence of any documented guidelines, it is all very much
> ad-hoc.
> It then frustrates me that these sorts of issues are then raised at
> review, which either results in having to justify the choice, or
> respinning patches often with time constraints if trying to hit a
> merge window.

I understand your frustration, and have experienced it too myself at
times. We're trying to align our messages, unfortunately it's mostly
ad-hoc. There are multiple reasons for that, sometimes we realize that
we've being done things wrong for a long time, or we need to experiment
to find the best option. The media subsystem being understaffed doesn't
help, and I think it also drives contributors away as a result, which
makes the problem worse.

With more time, I would really like to work on standardization of camera
sensor APIs in the media subsystem, both in-kernel and towards
userspace. We've discussed that in Dublin, you know how painful it is
today.

> > Overall, I think there's a general agreement that the runtime PM resume
> > handler needs to control everything required to make the sensor
> > accessible by software. That covers at least hard reset, regulators and
> > clocks.
> >
> > For software settings, it's less clear. If the sensor requires a
> > software reset sequence immediately after power on, it makes sense to
> > also handle that in the runtime PM resume handler. Same thing for any
> > other initialization required to reach a quiescent state (for instance
> > there are many sensors that start streaming automatically right after
> > power on for a reason I can't understand, so that needs to be disabled).
> >
> > This means that the runtime PM handler will thus access the sensor over
> > I2C. We may not want to do so in probe() before having a chance to probe
> > it (by reading an ID register for instance). The power on sequence could
> > be split in two to handle this, with one function that powers the sensor
> > up, and the other one that handles software initialization. Both would
> > be called from the runtime PM resume handler, and in probe(), we could
> > first power on the sensor, identify it, and then initialize it. I think
> > that will be fine on DT platforms as we don't need to RPM-resume the I2C
> > device in probe before accessing it as far as I can tell, given that the
> > probe() function should be called with the I2C controller RPM-resumed.
> > I'll let Sakari confirms if this works for ACPI).
> 
> For ov9282 I'd also raised the issue that a fair number of sensor
> drivers include a software reset in their lists of registers, which
> will undo any settings done in resume.
> 
> As above, it was more of an observation than issue with this patch.
> Alexander has already given an R-b, so there's limited point adding mine.
> 
> I'll try and test the series out tomorrow, and I will get around to
> submitting my series on top.

Great, I'll then test your patches on my board :-)

> > For other settings, I wouldn't handle them in the runtime PM resume
> > handler. In this particular case, the number of data lanes could even
> > vary based on the sensor mode (we don't do so at the moment), so
> > .s_stream() time seems better to me.
> >
> > > https://patchwork.linuxtv.org/project/linux-media/patch/20221005152809.3785786-9-dave.stevenson@raspberrypi.com/#141118
> > >
> > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > ---
> > > >  drivers/media/i2c/imx290.c | 6 +++---
> > > >  1 file changed, 3 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > > index dbed703fa199..4dfa090f918d 100644
> > > > --- a/drivers/media/i2c/imx290.c
> > > > +++ b/drivers/media/i2c/imx290.c
> > > > @@ -753,6 +753,9 @@ static int imx290_start_streaming(struct imx290 *imx290,
> > > >                 return ret;
> > > >         }
> > > >
> > > > +       /* Set data lane count */
> > > > +       imx290_set_data_lanes(imx290);
> > > > +
> > > >         /* 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);
> > > > @@ -1052,9 +1055,6 @@ static int imx290_power_on(struct device *dev)
> > > >         gpiod_set_value_cansleep(imx290->rst_gpio, 0);
> > > >         usleep_range(30000, 31000);
> > > >
> > > > -       /* Set data lane count */
> > > > -       imx290_set_data_lanes(imx290);
> > > > -
> > > >         return 0;
> > > >  }
> > > >

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 02/15] media: i2c: imx290: Factor out subdev init and cleanup to functions
  2022-11-24 18:31   ` Dave Stevenson
@ 2022-11-24 19:25     ` Laurent Pinchart
  0 siblings, 0 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-24 19:25 UTC (permalink / raw)
  To: Dave Stevenson; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Dave,

On Thu, Nov 24, 2022 at 06:31:51PM +0000, Dave Stevenson wrote:
> On Tue, 22 Nov 2022 at 22:34, Laurent Pinchart wrote:
> >
> > The probe() function is large. Make it more readable by factoring the
> > subdev initialization code out. While at it, rename the error labels as
> > the "free_" prefix isn't accurate.
> >
> > No functional change intended.
> >
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  drivers/media/i2c/imx290.c | 109 +++++++++++++++++++++----------------
> >  1 file changed, 62 insertions(+), 47 deletions(-)
> >
> > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > index aae4f51941a1..4dbf218e7a63 100644
> > --- a/drivers/media/i2c/imx290.c
> > +++ b/drivers/media/i2c/imx290.c
> > @@ -1015,6 +1015,46 @@ static const struct media_entity_operations imx290_subdev_entity_ops = {
> >         .link_validate = v4l2_subdev_link_validate,
> >  };
> >
> > +static int imx290_subdev_init(struct imx290 *imx290)
> > +{
> > +       struct i2c_client *client = to_i2c_client(imx290->dev);
> > +       int ret;
> > +
> > +       /*
> > +        * Initialize the frame format. In particular, imx290->current_mode
> > +        * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
> > +        * below relies on these fields.
> > +        */
> > +       imx290_entity_init_cfg(&imx290->sd, NULL);
> > +
> > +       ret = imx290_ctrl_init(imx290);
> > +       if (ret < 0) {
> > +               dev_err(imx290->dev, "Control initialization error %d\n", ret);
> > +               return ret;
> > +       }
> > +
> > +       v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> > +       imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +       imx290->sd.dev = imx290->dev;
> > +       imx290->sd.entity.ops = &imx290_subdev_entity_ops;
> > +       imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> > +
> > +       imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
> > +       ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
> > +       if (ret < 0) {
> > +               dev_err(imx290->dev, "Could not register media entity\n");
> > +               return ret;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static void imx290_subdev_cleanup(struct imx290 *imx290)
> > +{
> > +       media_entity_cleanup(&imx290->sd.entity);
> > +       v4l2_ctrl_handler_free(&imx290->ctrls);
> > +}
> > +
> >  /* ----------------------------------------------------------------------------
> >   * Power management
> >   */
> > @@ -1148,10 +1188,10 @@ static int imx290_probe(struct i2c_client *client)
> >         fwnode_handle_put(endpoint);
> >         if (ret == -ENXIO) {
> >                 dev_err(dev, "Unsupported bus type, should be CSI2\n");
> > -               goto free_err;
> > +               goto err_endpoint;
> >         } else if (ret) {
> >                 dev_err(dev, "Parsing endpoint node failed\n");
> > -               goto free_err;
> > +               goto err_endpoint;
> >         }
> >
> >         /* Get number of data lanes */
> > @@ -1159,7 +1199,7 @@ static int imx290_probe(struct i2c_client *client)
> >         if (imx290->nlanes != 2 && imx290->nlanes != 4) {
> >                 dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes);
> >                 ret = -EINVAL;
> > -               goto free_err;
> > +               goto err_endpoint;
> >         }
> >
> >         dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
> > @@ -1167,7 +1207,7 @@ static int imx290_probe(struct i2c_client *client)
> >         if (!ep.nr_of_link_frequencies) {
> >                 dev_err(dev, "link-frequency property not found in DT\n");
> >                 ret = -EINVAL;
> > -               goto free_err;
> > +               goto err_endpoint;
> >         }
> >
> >         /* Check that link frequences for all the modes are in device tree */
> > @@ -1175,7 +1215,7 @@ static int imx290_probe(struct i2c_client *client)
> >         if (fq) {
> >                 dev_err(dev, "Link frequency of %lld is not supported\n", fq);
> >                 ret = -EINVAL;
> > -               goto free_err;
> > +               goto err_endpoint;
> >         }
> >
> >         /* get system clock (xclk) */
> > @@ -1183,14 +1223,14 @@ static int imx290_probe(struct i2c_client *client)
> >         if (IS_ERR(imx290->xclk)) {
> >                 dev_err(dev, "Could not get xclk");
> >                 ret = PTR_ERR(imx290->xclk);
> > -               goto free_err;
> > +               goto err_endpoint;
> >         }
> >
> >         ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
> >                                        &xclk_freq);
> >         if (ret) {
> >                 dev_err(dev, "Could not get xclk frequency\n");
> > -               goto free_err;
> > +               goto err_endpoint;
> >         }
> >
> >         /* external clock must be 37.125 MHz */
> > @@ -1198,19 +1238,19 @@ static int imx290_probe(struct i2c_client *client)
> >                 dev_err(dev, "External clock frequency %u is not supported\n",
> >                         xclk_freq);
> >                 ret = -EINVAL;
> > -               goto free_err;
> > +               goto err_endpoint;
> >         }
> >
> >         ret = clk_set_rate(imx290->xclk, xclk_freq);
> >         if (ret) {
> >                 dev_err(dev, "Could not set xclk frequency\n");
> > -               goto free_err;
> > +               goto err_endpoint;
> >         }
> >
> >         ret = imx290_get_regulators(dev, imx290);
> >         if (ret < 0) {
> >                 dev_err(dev, "Cannot get regulators\n");
> > -               goto free_err;
> > +               goto err_endpoint;
> >         }
> >
> >         imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset",
> > @@ -1218,55 +1258,33 @@ static int imx290_probe(struct i2c_client *client)
> >         if (IS_ERR(imx290->rst_gpio)) {
> >                 dev_err(dev, "Cannot get reset gpio\n");
> >                 ret = PTR_ERR(imx290->rst_gpio);
> > -               goto free_err;
> > +               goto err_endpoint;
> >         }
> >
> >         mutex_init(&imx290->lock);
> >
> > -       /*
> > -        * Initialize the frame format. In particular, imx290->current_mode
> > -        * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
> > -        * below relies on these fields.
> > -        */
> > -       imx290_entity_init_cfg(&imx290->sd, NULL);
> > -
> > -       ret = imx290_ctrl_init(imx290);
> > -       if (ret < 0) {
> > -               dev_err(dev, "Control initialization error %d\n", ret);
> > -               goto free_mutex;
> > -       }
> > -
> > -       v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> > -       imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > -       imx290->sd.dev = &client->dev;
> > -       imx290->sd.entity.ops = &imx290_subdev_entity_ops;
> > -       imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> > -
> > -       imx290->pad.flags = MEDIA_PAD_FL_SOURCE;
> > -       ret = media_entity_pads_init(&imx290->sd.entity, 1, &imx290->pad);
> > -       if (ret < 0) {
> > -               dev_err(dev, "Could not register media entity\n");
> > -               goto free_ctrl;
> > -       }
> > +       ret = imx290_subdev_init(imx290);
> > +       if (ret)
> > +               goto err_mutex;
> >
> >         ret = v4l2_async_register_subdev(&imx290->sd);
> >         if (ret < 0) {
> >                 dev_err(dev, "Could not register v4l2 device\n");
> > -               goto free_entity;
> > +               goto err_subdev;
> >         }
> >
> >         /* Power on the device to match runtime PM state below */
> >         ret = imx290_power_on(dev);
> >         if (ret < 0) {
> >                 dev_err(dev, "Could not power on the device\n");
> > -               goto free_entity;
> > +               goto err_subdev;
> >         }
> >
> >         ret = imx290_read(imx290, IMX290_CHIP_ID, &chip_id);
> >         if (ret) {
> >                 dev_err(dev, "Could not read chip ID: %d\n", ret);
> >                 imx290_power_off(dev);
> > -               goto free_entity;
> > +               goto err_subdev;
> >         }
> 
> Which tree is this patch based on, as neither Sakari nor Mauro's trees
> have this read in them.
> I suspect an older patch which added it got dropped due to the worry
> over Vision Component's modules which block 16 bit reads.

There's a local patch indeed, which I incorrectly put at the bottom of
the branch. It will be fixed in v2.

> >
> >         dev_info(dev, "chip ID 0x%04x\n", chip_id);
> > @@ -1279,13 +1297,11 @@ static int imx290_probe(struct i2c_client *client)
> >
> >         return 0;
> >
> > -free_entity:
> > -       media_entity_cleanup(&imx290->sd.entity);
> > -free_ctrl:
> > -       v4l2_ctrl_handler_free(&imx290->ctrls);
> > -free_mutex:
> > +err_subdev:
> > +       imx290_subdev_cleanup(imx290);
> > +err_mutex:
> >         mutex_destroy(&imx290->lock);
> > -free_err:
> > +err_endpoint:
> >         v4l2_fwnode_endpoint_free(&ep);
> >
> >         return ret;
> > @@ -1297,8 +1313,7 @@ static void imx290_remove(struct i2c_client *client)
> >         struct imx290 *imx290 = to_imx290(sd);
> >
> >         v4l2_async_unregister_subdev(sd);
> > -       media_entity_cleanup(&sd->entity);
> > -       v4l2_ctrl_handler_free(sd->ctrl_handler);
> > +       imx290_subdev_cleanup(imx290);
> >
> >         mutex_destroy(&imx290->lock);
> >

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v1 11/15] media: i2c: imx290: Use V4L2 subdev active state
  2022-11-23 10:49     ` Laurent Pinchart
@ 2022-11-28 15:33       ` Alexander Stein
  2022-11-28 18:11         ` Laurent Pinchart
  0 siblings, 1 reply; 50+ messages in thread
From: Alexander Stein @ 2022-11-28 15:33 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Am Mittwoch, 23. November 2022, 11:49:49 CET schrieb Laurent Pinchart:
> Hi Alexander,
> 
> On Wed, Nov 23, 2022 at 09:42:10AM +0100, Alexander Stein wrote:
> > Am Dienstag, 22. November 2022, 23:32:46 CET schrieb Laurent Pinchart:
> > > Use the V4L2 subdev active state API to store the active format. This
> > > simplifies the driver not only by dropping the imx290 current_format
> > > field, but it also allows dropping the imx290 lock, replaced with the
> > > state lock.
> > > 
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > ---
> > > 
> > >  drivers/media/i2c/imx290.c | 139 +++++++++++++++----------------------
> > >  1 file changed, 56 insertions(+), 83 deletions(-)
> > > 
> > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > index 848de4c90d3b..cd2154983341 100644
> > > --- a/drivers/media/i2c/imx290.c
> > > +++ b/drivers/media/i2c/imx290.c
> > > @@ -177,12 +177,12 @@ struct imx290 {
> > > 
> > >  	struct clk *xclk;
> > >  	struct regmap *regmap;
> > >  	u8 nlanes;
> > > 
> > > -	u8 bpp;
> > > 
> > >  	struct v4l2_subdev sd;
> > >  	struct media_pad pad;
> > > 
> > > -	struct v4l2_mbus_framefmt current_format;
> > > +
> > > 
> > >  	const struct imx290_mode *current_mode;
> > > 
> > > +	u8 bpp;
> > > 
> > >  	struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
> > >  	struct gpio_desc *rst_gpio;
> > > 
> > > @@ -192,8 +192,6 @@ struct imx290 {
> > > 
> > >  	struct v4l2_ctrl *pixel_rate;
> > >  	struct v4l2_ctrl *hblank;
> > >  	struct v4l2_ctrl *vblank;
> > > 
> > > -
> > > -	struct mutex lock;
> > > 
> > >  };
> > >  
> > >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > > 
> > > @@ -524,14 +522,15 @@ static int imx290_set_black_level(struct imx290
> > > *imx290, black_level >> (16 - imx290->bpp), err);
> > > 
> > >  }
> > > 
> > > -static int imx290_write_current_format(struct imx290 *imx290)
> > > +static int imx290_setup_format(struct imx290 *imx290,
> > > +			       const struct v4l2_mbus_framefmt *format)
> > > 
> > >  {
> > >  
> > >  	const struct imx290_regval *regs;
> > >  	unsigned int num_regs;
> > >  	unsigned int bpp;
> > >  	int ret;
> > > 
> > > -	switch (imx290->current_format.code) {
> > > +	switch (format->code) {
> > > 
> > >  	case MEDIA_BUS_FMT_SRGGB10_1X10:
> > >  		regs = imx290_10bit_settings;
> > >  		num_regs = ARRAY_SIZE(imx290_10bit_settings);
> > > 
> > > @@ -564,12 +563,17 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > 
> > >  {
> > >  
> > >  	struct imx290 *imx290 = container_of(ctrl->handler,
> > >  	
> > >  					     struct imx290, ctrls);
> > > 
> > > +	const struct v4l2_mbus_framefmt *format;
> > > +	struct v4l2_subdev_state *state;
> > > 
> > >  	int ret = 0;
> > >  	
> > >  	/* V4L2 controls values will be applied only when power is already 
up
> > >  	*/
> > >  	if (!pm_runtime_get_if_in_use(imx290->dev))
> > >  	
> > >  		return 0;
> > > 
> > > +	state = v4l2_subdev_get_locked_active_state(&imx290->sd);
> > > +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> > > +
> > > 
> > >  	switch (ctrl->id) {
> > >  	
> > >  	case V4L2_CID_ANALOGUE_GAIN:
> > >  		ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
> > > 
> > > @@ -646,11 +650,10 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > 
> > >  	int ret;
> > >  	
> > >  	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
> > > 
> > > -	if (ret < 0)
> > > +	if (ret)
> > 
> > This is an unrelated change.
> 
> I'll drop it.
> 
> > >  		return ret;
> > >  	
> > >  	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> > > 
> > > -	imx290->ctrls.lock = &imx290->lock;
> > > 
> > >  	/*
> > >  	
> > >  	 * The sensor has an analog gain and a digital gain, both controlled
> > > 
> > > @@ -715,11 +718,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > 
> > >  		return ret;
> > >  	
> > >  	}
> > > 
> > > -	mutex_lock(imx290->ctrls.lock);
> > > -	imx290_ctrl_update(imx290, &imx290->current_format,
> > > -			   imx290->current_mode);
> > > -	mutex_unlock(imx290->ctrls.lock);
> > > -
> > > 
> > >  	return 0;
> > >  
> > >  }
> > > 
> > > @@ -728,8 +726,10 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > 
> > >   */
> > >  
> > >  /* Start streaming */
> > > 
> > > -static int imx290_start_streaming(struct imx290 *imx290)
> > > +static int imx290_start_streaming(struct imx290 *imx290,
> > > +				  struct v4l2_subdev_state *state)
> > > 
> > >  {
> > > 
> > > +	const struct v4l2_mbus_framefmt *format;
> > > 
> > >  	int ret;
> > >  	
> > >  	/* Set init register settings */
> > > 
> > > @@ -742,7 +742,8 @@ static int imx290_start_streaming(struct imx290
> > > *imx290) }
> > > 
> > >  	/* Apply the register values related to current frame format */
> > > 
> > > -	ret = imx290_write_current_format(imx290);
> > > +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> > > +	ret = imx290_setup_format(imx290, format);
> > > 
> > >  	if (ret < 0) {
> > >  	
> > >  		dev_err(imx290->dev, "Could not set frame format\n");
> > >  		return ret;
> > > 
> > > @@ -762,7 +763,7 @@ static int imx290_start_streaming(struct imx290
> > > *imx290) return ret;
> > > 
> > >  	/* Apply customized values from user */
> > > 
> > > -	ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> > > +	ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> > 
> > Why is it safe to ignore the lock now? There is no user-specified lock for
> > imx290->ctrls.lock, but instead imx290->ctrls._lock is set, no?
> 
> That's because the control handler and subdev state lock are the same.
> The imx290_start_streaming() function starts by calling
> v4l2_subdev_lock_and_get_active_state(), which takes the lock, so we
> must not lock again with v4l2_ctrl_handler_setup().

v4l2_subdev_lock_and_get_active_state() is called in imx290_set_stream, not 
imx290_start_streaming.
But while holding this lock imx290_start_streaming() is called. Okay, looks 
good.

> > >  	if (ret) {
> > >  	
> > >  		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
> > >  		return ret;
> > > 
> > > @@ -791,39 +792,32 @@ static int imx290_stop_streaming(struct imx290
> > > *imx290) static int imx290_set_stream(struct v4l2_subdev *sd, int
> > > enable)
> > > 
> > >  {
> > >  
> > >  	struct imx290 *imx290 = to_imx290(sd);
> > > 
> > > +	struct v4l2_subdev_state *state;
> > > 
> > >  	int ret = 0;
> > > 
> > > +	state = v4l2_subdev_lock_and_get_active_state(sd);
> > > +
> > > 
> > >  	if (enable) {
> > >  	
> > >  		ret = pm_runtime_resume_and_get(imx290->dev);
> > >  		if (ret < 0)
> > > 
> > > -			goto unlock_and_return;
> > > +			goto unlock;
> > > 
> > > -		ret = imx290_start_streaming(imx290);
> > > +		ret = imx290_start_streaming(imx290, state);
> > > 
> > >  		if (ret) {
> > >  		
> > >  			dev_err(imx290->dev, "Start stream failed\n");
> > >  			pm_runtime_put(imx290->dev);
> > > 
> > > -			goto unlock_and_return;
> > > +			goto unlock;
> > > 
> > >  		}
> > >  	
> > >  	} else {
> > >  	
> > >  		imx290_stop_streaming(imx290);
> > >  		pm_runtime_put(imx290->dev);
> > >  	
> > >  	}
> > > 
> > > -unlock_and_return:
> > > -
> > > +unlock:
> > > +	v4l2_subdev_unlock_state(state);
> > > 
> > >  	return ret;
> > >  
> > >  }
> > > 
> > > -static struct v4l2_mbus_framefmt *
> > > -imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state
> > > *state, -		      u32 which)
> > > -{
> > > -	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
> > > -		return &imx290->current_format;
> > > -	else
> > > -		return v4l2_subdev_get_try_format(&imx290->sd, state, 0);
> > > -}
> > > -
> > > 
> > >  static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
> > >  
> > >  				 struct v4l2_subdev_state *sd_state,
> > >  				 struct v4l2_subdev_mbus_code_enum 
*code)
> > > 
> > > @@ -858,23 +852,6 @@ static int imx290_enum_frame_size(struct
> > > v4l2_subdev
> > > *sd, return 0;
> > > 
> > >  }
> > > 
> > > -static int imx290_get_fmt(struct v4l2_subdev *sd,
> > > -			  struct v4l2_subdev_state *sd_state,
> > > -			  struct v4l2_subdev_format *fmt)
> > > -{
> > > -	struct imx290 *imx290 = to_imx290(sd);
> > > -	struct v4l2_mbus_framefmt *framefmt;
> > > -
> > > -	mutex_lock(&imx290->lock);
> > > -
> > > -	framefmt = imx290_get_pad_format(imx290, sd_state, fmt->which);
> > > -	fmt->format = *framefmt;
> > > -
> > > -	mutex_unlock(&imx290->lock);
> > > -
> > > -	return 0;
> > > -}
> > > -
> > > 
> > >  static int imx290_set_fmt(struct v4l2_subdev *sd,
> > >  
> > >  			  struct v4l2_subdev_state *sd_state,
> > >  			  struct v4l2_subdev_format *fmt)
> > > 
> > > @@ -884,8 +861,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> > > 
> > >  	struct v4l2_mbus_framefmt *format;
> > >  	unsigned int i;
> > > 
> > > -	mutex_lock(&imx290->lock);
> > > -
> > > 
> > >  	mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
> > >  	
> > >  				      imx290_modes_num(imx290), width, 
height,
> > >  				      fmt->format.width, fmt-
> > >
> > >format.height);
> > >
> > > @@ -903,7 +878,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> > > 
> > >  	fmt->format.code = imx290_formats[i].code;
> > >  	fmt->format.field = V4L2_FIELD_NONE;
> > > 
> > > -	format = imx290_get_pad_format(imx290, sd_state, fmt->which);
> > > +	format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> > > 
> > >  	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> > >  	
> > >  		imx290->current_mode = mode;
> > > 
> > > @@ -914,8 +889,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> > > 
> > >  	*format = fmt->format;
> > > 
> > > -	mutex_unlock(&imx290->lock);
> > > -
> > > 
> > >  	return 0;
> > >  
> > >  }
> > > 
> > > @@ -923,14 +896,11 @@ 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) {
> > >  	case V4L2_SEL_TGT_CROP: {
> > > 
> > > -		format = imx290_get_pad_format(imx290, sd_state, sel-
>which);
> > > -
> > > -		mutex_lock(&imx290->lock);
> > > +		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;
> > > 
> > > @@ -939,7 +909,6 @@ static int imx290_get_selection(struct v4l2_subdev
> > > *sd,
> > > 
> > >  		sel->r.width = format->width;
> > >  		sel->r.height = format->height;
> > > 
> > > -		mutex_unlock(&imx290->lock);
> > > 
> > >  		return 0;
> > >  	
> > >  	}
> > > 
> > > @@ -968,11 +937,13 @@ static int imx290_get_selection(struct v4l2_subdev
> > > *sd, static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
> > > 
> > >  				  struct v4l2_subdev_state *sd_state)
> > >  
> > >  {
> > > 
> > > -	struct v4l2_subdev_format fmt = { 0 };
> > > -
> > > -	fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY :
> > > V4L2_SUBDEV_FORMAT_ACTIVE; -	fmt.format.width = 1920;
> > > -	fmt.format.height = 1080;
> > > +	struct v4l2_subdev_format fmt = {
> > > +		.which = V4L2_SUBDEV_FORMAT_TRY,
> > > +		.format = {
> > > +			.width = 1920,
> > > +			.height = 1080,
> > > +		},
> > > +	};
> > > 
> > >  	imx290_set_fmt(subdev, sd_state, &fmt);
> > > 
> > > @@ -987,7 +958,7 @@ static const struct v4l2_subdev_pad_ops
> > > imx290_pad_ops = { .init_cfg = imx290_entity_init_cfg,
> > > 
> > >  	.enum_mbus_code = imx290_enum_mbus_code,
> > >  	.enum_frame_size = imx290_enum_frame_size,
> > > 
> > > -	.get_fmt = imx290_get_fmt,
> > > +	.get_fmt = v4l2_subdev_get_fmt,
> > > 
> > >  	.set_fmt = imx290_set_fmt,
> > >  	.get_selection = imx290_get_selection,
> > >  
> > >  };
> > > 
> > > @@ -1004,20 +975,12 @@ static const struct media_entity_operations
> > > imx290_subdev_entity_ops = { static int imx290_subdev_init(struct imx290
> > > *imx290)
> > > 
> > >  {
> > >  
> > >  	struct i2c_client *client = to_i2c_client(imx290->dev);
> > > 
> > > +	const struct v4l2_mbus_framefmt *format;
> > > +	struct v4l2_subdev_state *state;
> > > 
> > >  	int ret;
> > > 
> > > -	/*
> > > -	 * Initialize the frame format. In particular, imx290->current_mode
> > > -	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() 
call
> > > -	 * below relies on these fields.
> > > -	 */
> > > -	imx290_entity_init_cfg(&imx290->sd, NULL);
> > > -
> > > -	ret = imx290_ctrl_init(imx290);
> > > -	if (ret < 0) {
> > > -		dev_err(imx290->dev, "Control initialization error %d\n", 
ret);
> > > -		return ret;
> > > -	}
> > > +	imx290->current_mode = &imx290_modes_ptr(imx290)[0];
> > > +	imx290->bpp = imx290_formats[0].bpp;
> > > 
> > >  	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> > >  	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > > 
> > > @@ -1032,6 +995,22 @@ static int imx290_subdev_init(struct imx290
> > > *imx290)
> > > 
> > >  		return ret;
> > >  	
> > >  	}
> > > 
> > > +	ret = imx290_ctrl_init(imx290);
> > > +	if (ret < 0) {
> > > +		dev_err(imx290->dev, "Control initialization error %d\n", 
ret);
> > > +		media_entity_cleanup(&imx290->sd.entity);
> > > +		return ret;
> > > +	}
> > > +
> > > +	imx290->sd.state_lock = imx290->ctrls.lock;
> > > +
> > > +	v4l2_subdev_init_finalize(&imx290->sd);
> > > +
> > > +	state = v4l2_subdev_lock_and_get_active_state(&imx290->sd);
> > > +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> > > +	imx290_ctrl_update(imx290, format, imx290->current_mode);
> > > +	v4l2_subdev_unlock_state(state);
> > 
> > Do you need to check for state == NULL here?
> 
> No, v4l2_subdev_lock_and_get_active_state() can only return NULL if the
> subdev driver doesn't have an active state, and
> v4l2_subdev_init_finalize() guarantees it has one.

I see, but that is only true if v4l2_subdev_init_finalize()/
__v4l2_subdev_init_finalize() does not return an error. AFAICS it's possible 
due to memory allocation failure or pad init_cfg() fails.

Regards,
Alexander

> > > +
> > > 
> > >  	return 0;
> > >  
> > >  }
> > > 
> > > @@ -1268,12 +1247,10 @@ static int imx290_probe(struct i2c_client
> > > *client)
> > > 
> > >  	if (ret)
> > >  	
> > >  		return ret;
> > > 
> > > -	mutex_init(&imx290->lock);
> > > -
> > > 
> > >  	/* Initialize and register subdev. */
> > >  	ret = imx290_subdev_init(imx290);
> > >  	if (ret)
> > > 
> > > -		goto err_mutex;
> > > +		return ret;
> > > 
> > >  	ret = v4l2_async_register_subdev(&imx290->sd);
> > >  	if (ret < 0) {
> > > 
> > > @@ -1305,8 +1282,6 @@ static int imx290_probe(struct i2c_client *client)
> > > 
> > >  err_subdev:
> > >  	imx290_subdev_cleanup(imx290);
> > > 
> > > -err_mutex:
> > > -	mutex_destroy(&imx290->lock);
> > > 
> > >  	return ret;
> > >  
> > >  }
> > > 
> > > @@ -1319,8 +1294,6 @@ static void imx290_remove(struct i2c_client
> > > *client)
> > > 
> > >  	v4l2_async_unregister_subdev(sd);
> > >  	imx290_subdev_cleanup(imx290);
> > > 
> > > -	mutex_destroy(&imx290->lock);
> > > -
> > > 
> > >  	pm_runtime_disable(imx290->dev);
> > >  	if (!pm_runtime_status_suspended(imx290->dev))
> > >  	
> > >  		imx290_power_off(imx290->dev);





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

* Re: [PATCH v1 11/15] media: i2c: imx290: Use V4L2 subdev active state
  2022-11-28 15:33       ` Alexander Stein
@ 2022-11-28 18:11         ` Laurent Pinchart
  0 siblings, 0 replies; 50+ messages in thread
From: Laurent Pinchart @ 2022-11-28 18:11 UTC (permalink / raw)
  To: Alexander Stein; +Cc: linux-media, Sakari Ailus, Manivannan Sadhasivam

Hi Alexander,

On Mon, Nov 28, 2022 at 04:33:53PM +0100, Alexander Stein wrote:
> Am Mittwoch, 23. November 2022, 11:49:49 CET schrieb Laurent Pinchart:
> > On Wed, Nov 23, 2022 at 09:42:10AM +0100, Alexander Stein wrote:
> > > Am Dienstag, 22. November 2022, 23:32:46 CET schrieb Laurent Pinchart:
> > > > Use the V4L2 subdev active state API to store the active format. This
> > > > simplifies the driver not only by dropping the imx290 current_format
> > > > field, but it also allows dropping the imx290 lock, replaced with the
> > > > state lock.
> > > > 
> > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > ---
> > > > 
> > > >  drivers/media/i2c/imx290.c | 139 +++++++++++++++----------------------
> > > >  1 file changed, 56 insertions(+), 83 deletions(-)
> > > > 
> > > > diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
> > > > index 848de4c90d3b..cd2154983341 100644
> > > > --- a/drivers/media/i2c/imx290.c
> > > > +++ b/drivers/media/i2c/imx290.c
> > > > @@ -177,12 +177,12 @@ struct imx290 {
> > > > 
> > > >  	struct clk *xclk;
> > > >  	struct regmap *regmap;
> > > >  	u8 nlanes;
> > > > 
> > > > -	u8 bpp;
> > > > 
> > > >  	struct v4l2_subdev sd;
> > > >  	struct media_pad pad;
> > > > 
> > > > -	struct v4l2_mbus_framefmt current_format;
> > > > +
> > > > 
> > > >  	const struct imx290_mode *current_mode;
> > > > 
> > > > +	u8 bpp;
> > > > 
> > > >  	struct regulator_bulk_data supplies[IMX290_NUM_SUPPLIES];
> > > >  	struct gpio_desc *rst_gpio;
> > > > 
> > > > @@ -192,8 +192,6 @@ struct imx290 {
> > > > 
> > > >  	struct v4l2_ctrl *pixel_rate;
> > > >  	struct v4l2_ctrl *hblank;
> > > >  	struct v4l2_ctrl *vblank;
> > > > 
> > > > -
> > > > -	struct mutex lock;
> > > > 
> > > >  };
> > > >  
> > > >  static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
> > > > 
> > > > @@ -524,14 +522,15 @@ static int imx290_set_black_level(struct imx290
> > > > *imx290, black_level >> (16 - imx290->bpp), err);
> > > > 
> > > >  }
> > > > 
> > > > -static int imx290_write_current_format(struct imx290 *imx290)
> > > > +static int imx290_setup_format(struct imx290 *imx290,
> > > > +			       const struct v4l2_mbus_framefmt *format)
> > > > 
> > > >  {
> > > >  
> > > >  	const struct imx290_regval *regs;
> > > >  	unsigned int num_regs;
> > > >  	unsigned int bpp;
> > > >  	int ret;
> > > > 
> > > > -	switch (imx290->current_format.code) {
> > > > +	switch (format->code) {
> > > > 
> > > >  	case MEDIA_BUS_FMT_SRGGB10_1X10:
> > > >  		regs = imx290_10bit_settings;
> > > >  		num_regs = ARRAY_SIZE(imx290_10bit_settings);
> > > > 
> > > > @@ -564,12 +563,17 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
> > > > 
> > > >  {
> > > >  
> > > >  	struct imx290 *imx290 = container_of(ctrl->handler,
> > > >  	
> > > >  					     struct imx290, ctrls);
> > > > 
> > > > +	const struct v4l2_mbus_framefmt *format;
> > > > +	struct v4l2_subdev_state *state;
> > > > 
> > > >  	int ret = 0;
> > > >  	
> > > >  	/* V4L2 controls values will be applied only when power is already up
> > > >  	*/
> > > >  	if (!pm_runtime_get_if_in_use(imx290->dev))
> > > >  	
> > > >  		return 0;
> > > > 
> > > > +	state = v4l2_subdev_get_locked_active_state(&imx290->sd);
> > > > +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> > > > +
> > > > 
> > > >  	switch (ctrl->id) {
> > > >  	
> > > >  	case V4L2_CID_ANALOGUE_GAIN:
> > > >  		ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
> > > > 
> > > > @@ -646,11 +650,10 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > > 
> > > >  	int ret;
> > > >  	
> > > >  	ret = v4l2_fwnode_device_parse(imx290->dev, &props);
> > > > 
> > > > -	if (ret < 0)
> > > > +	if (ret)
> > > 
> > > This is an unrelated change.
> > 
> > I'll drop it.
> > 
> > > >  		return ret;
> > > >  	
> > > >  	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
> > > > 
> > > > -	imx290->ctrls.lock = &imx290->lock;
> > > > 
> > > >  	/*
> > > >  	
> > > >  	 * The sensor has an analog gain and a digital gain, both controlled
> > > > 
> > > > @@ -715,11 +718,6 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > > 
> > > >  		return ret;
> > > >  	
> > > >  	}
> > > > 
> > > > -	mutex_lock(imx290->ctrls.lock);
> > > > -	imx290_ctrl_update(imx290, &imx290->current_format,
> > > > -			   imx290->current_mode);
> > > > -	mutex_unlock(imx290->ctrls.lock);
> > > > -
> > > > 
> > > >  	return 0;
> > > >  
> > > >  }
> > > > 
> > > > @@ -728,8 +726,10 @@ static int imx290_ctrl_init(struct imx290 *imx290)
> > > > 
> > > >   */
> > > >  
> > > >  /* Start streaming */
> > > > 
> > > > -static int imx290_start_streaming(struct imx290 *imx290)
> > > > +static int imx290_start_streaming(struct imx290 *imx290,
> > > > +				  struct v4l2_subdev_state *state)
> > > > 
> > > >  {
> > > > 
> > > > +	const struct v4l2_mbus_framefmt *format;
> > > > 
> > > >  	int ret;
> > > >  	
> > > >  	/* Set init register settings */
> > > > 
> > > > @@ -742,7 +742,8 @@ static int imx290_start_streaming(struct imx290
> > > > *imx290) }
> > > > 
> > > >  	/* Apply the register values related to current frame format */
> > > > 
> > > > -	ret = imx290_write_current_format(imx290);
> > > > +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> > > > +	ret = imx290_setup_format(imx290, format);
> > > > 
> > > >  	if (ret < 0) {
> > > >  	
> > > >  		dev_err(imx290->dev, "Could not set frame format\n");
> > > >  		return ret;
> > > > 
> > > > @@ -762,7 +763,7 @@ static int imx290_start_streaming(struct imx290
> > > > *imx290) return ret;
> > > > 
> > > >  	/* Apply customized values from user */
> > > > 
> > > > -	ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> > > > +	ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
> > > 
> > > Why is it safe to ignore the lock now? There is no user-specified lock for
> > > imx290->ctrls.lock, but instead imx290->ctrls._lock is set, no?
> > 
> > That's because the control handler and subdev state lock are the same.
> > The imx290_start_streaming() function starts by calling
> > v4l2_subdev_lock_and_get_active_state(), which takes the lock, so we
> > must not lock again with v4l2_ctrl_handler_setup().
> 
> v4l2_subdev_lock_and_get_active_state() is called in imx290_set_stream, not 
> imx290_start_streaming.
> But while holding this lock imx290_start_streaming() is called. Okay, looks 
> good.
> 
> > > >  	if (ret) {
> > > >  	
> > > >  		dev_err(imx290->dev, "Could not sync v4l2 controls\n");
> > > >  		return ret;
> > > > 
> > > > @@ -791,39 +792,32 @@ static int imx290_stop_streaming(struct imx290
> > > > *imx290) static int imx290_set_stream(struct v4l2_subdev *sd, int
> > > > enable)
> > > > 
> > > >  {
> > > >  
> > > >  	struct imx290 *imx290 = to_imx290(sd);
> > > > 
> > > > +	struct v4l2_subdev_state *state;
> > > > 
> > > >  	int ret = 0;
> > > > 
> > > > +	state = v4l2_subdev_lock_and_get_active_state(sd);
> > > > +
> > > > 
> > > >  	if (enable) {
> > > >  	
> > > >  		ret = pm_runtime_resume_and_get(imx290->dev);
> > > >  		if (ret < 0)
> > > > 
> > > > -			goto unlock_and_return;
> > > > +			goto unlock;
> > > > 
> > > > -		ret = imx290_start_streaming(imx290);
> > > > +		ret = imx290_start_streaming(imx290, state);
> > > > 
> > > >  		if (ret) {
> > > >  		
> > > >  			dev_err(imx290->dev, "Start stream failed\n");
> > > >  			pm_runtime_put(imx290->dev);
> > > > 
> > > > -			goto unlock_and_return;
> > > > +			goto unlock;
> > > > 
> > > >  		}
> > > >  	
> > > >  	} else {
> > > >  	
> > > >  		imx290_stop_streaming(imx290);
> > > >  		pm_runtime_put(imx290->dev);
> > > >  	
> > > >  	}
> > > > 
> > > > -unlock_and_return:
> > > > -
> > > > +unlock:
> > > > +	v4l2_subdev_unlock_state(state);
> > > > 
> > > >  	return ret;
> > > >  
> > > >  }
> > > > 
> > > > -static struct v4l2_mbus_framefmt *
> > > > -imx290_get_pad_format(struct imx290 *imx290, struct v4l2_subdev_state
> > > > *state, -		      u32 which)
> > > > -{
> > > > -	if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
> > > > -		return &imx290->current_format;
> > > > -	else
> > > > -		return v4l2_subdev_get_try_format(&imx290->sd, state, 0);
> > > > -}
> > > > -
> > > > 
> > > >  static int imx290_enum_mbus_code(struct v4l2_subdev *sd,
> > > >  				 struct v4l2_subdev_state *sd_state,
> > > >  				 struct v4l2_subdev_mbus_code_enum *code)
> > > > 
> > > > @@ -858,23 +852,6 @@ static int imx290_enum_frame_size(struct v4l2_subdev
> > > > *sd, return 0;
> > > > 
> > > >  }
> > > > 
> > > > -static int imx290_get_fmt(struct v4l2_subdev *sd,
> > > > -			  struct v4l2_subdev_state *sd_state,
> > > > -			  struct v4l2_subdev_format *fmt)
> > > > -{
> > > > -	struct imx290 *imx290 = to_imx290(sd);
> > > > -	struct v4l2_mbus_framefmt *framefmt;
> > > > -
> > > > -	mutex_lock(&imx290->lock);
> > > > -
> > > > -	framefmt = imx290_get_pad_format(imx290, sd_state, fmt->which);
> > > > -	fmt->format = *framefmt;
> > > > -
> > > > -	mutex_unlock(&imx290->lock);
> > > > -
> > > > -	return 0;
> > > > -}
> > > > -
> > > > 
> > > >  static int imx290_set_fmt(struct v4l2_subdev *sd,
> > > >  
> > > >  			  struct v4l2_subdev_state *sd_state,
> > > >  			  struct v4l2_subdev_format *fmt)
> > > > 
> > > > @@ -884,8 +861,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> > > > 
> > > >  	struct v4l2_mbus_framefmt *format;
> > > >  	unsigned int i;
> > > > 
> > > > -	mutex_lock(&imx290->lock);
> > > > -
> > > > 
> > > >  	mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290),
> > > >  				      imx290_modes_num(imx290), width, height,
> > > >  				      fmt->format.width, fmt->format.height);
> > > >
> > > > @@ -903,7 +878,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> > > > 
> > > >  	fmt->format.code = imx290_formats[i].code;
> > > >  	fmt->format.field = V4L2_FIELD_NONE;
> > > > 
> > > > -	format = imx290_get_pad_format(imx290, sd_state, fmt->which);
> > > > +	format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
> > > > 
> > > >  	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> > > >  	
> > > >  		imx290->current_mode = mode;
> > > > 
> > > > @@ -914,8 +889,6 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
> > > > 
> > > >  	*format = fmt->format;
> > > > 
> > > > -	mutex_unlock(&imx290->lock);
> > > > -
> > > > 
> > > >  	return 0;
> > > >  
> > > >  }
> > > > 
> > > > @@ -923,14 +896,11 @@ 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) {
> > > >  	case V4L2_SEL_TGT_CROP: {
> > > > 
> > > > -		format = imx290_get_pad_format(imx290, sd_state, sel->which);
> > > > -
> > > > -		mutex_lock(&imx290->lock);
> > > > +		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;
> > > > 
> > > > @@ -939,7 +909,6 @@ static int imx290_get_selection(struct v4l2_subdev
> > > > *sd,
> > > > 
> > > >  		sel->r.width = format->width;
> > > >  		sel->r.height = format->height;
> > > > 
> > > > -		mutex_unlock(&imx290->lock);
> > > > 
> > > >  		return 0;
> > > >  	
> > > >  	}
> > > > 
> > > > @@ -968,11 +937,13 @@ static int imx290_get_selection(struct v4l2_subdev
> > > > *sd, static int imx290_entity_init_cfg(struct v4l2_subdev *subdev,
> > > > 
> > > >  				  struct v4l2_subdev_state *sd_state)
> > > >  
> > > >  {
> > > > 
> > > > -	struct v4l2_subdev_format fmt = { 0 };
> > > > -
> > > > -	fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY :
> > > > V4L2_SUBDEV_FORMAT_ACTIVE; -	fmt.format.width = 1920;
> > > > -	fmt.format.height = 1080;
> > > > +	struct v4l2_subdev_format fmt = {
> > > > +		.which = V4L2_SUBDEV_FORMAT_TRY,
> > > > +		.format = {
> > > > +			.width = 1920,
> > > > +			.height = 1080,
> > > > +		},
> > > > +	};
> > > > 
> > > >  	imx290_set_fmt(subdev, sd_state, &fmt);
> > > > 
> > > > @@ -987,7 +958,7 @@ static const struct v4l2_subdev_pad_ops
> > > > imx290_pad_ops = { .init_cfg = imx290_entity_init_cfg,
> > > > 
> > > >  	.enum_mbus_code = imx290_enum_mbus_code,
> > > >  	.enum_frame_size = imx290_enum_frame_size,
> > > > 
> > > > -	.get_fmt = imx290_get_fmt,
> > > > +	.get_fmt = v4l2_subdev_get_fmt,
> > > > 
> > > >  	.set_fmt = imx290_set_fmt,
> > > >  	.get_selection = imx290_get_selection,
> > > >  
> > > >  };
> > > > 
> > > > @@ -1004,20 +975,12 @@ static const struct media_entity_operations
> > > > imx290_subdev_entity_ops = { static int imx290_subdev_init(struct imx290
> > > > *imx290)
> > > > 
> > > >  {
> > > >  
> > > >  	struct i2c_client *client = to_i2c_client(imx290->dev);
> > > > 
> > > > +	const struct v4l2_mbus_framefmt *format;
> > > > +	struct v4l2_subdev_state *state;
> > > > 
> > > >  	int ret;
> > > > 
> > > > -	/*
> > > > -	 * Initialize the frame format. In particular, imx290->current_mode
> > > > -	 * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call
> > > > -	 * below relies on these fields.
> > > > -	 */
> > > > -	imx290_entity_init_cfg(&imx290->sd, NULL);
> > > > -
> > > > -	ret = imx290_ctrl_init(imx290);
> > > > -	if (ret < 0) {
> > > > -		dev_err(imx290->dev, "Control initialization error %d\n", ret);
> > > > -		return ret;
> > > > -	}
> > > > +	imx290->current_mode = &imx290_modes_ptr(imx290)[0];
> > > > +	imx290->bpp = imx290_formats[0].bpp;
> > > > 
> > > >  	v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
> > > >  	imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > > > 
> > > > @@ -1032,6 +995,22 @@ static int imx290_subdev_init(struct imx290 *imx290)
> > > > 
> > > >  		return ret;
> > > >  	
> > > >  	}
> > > > 
> > > > +	ret = imx290_ctrl_init(imx290);
> > > > +	if (ret < 0) {
> > > > +		dev_err(imx290->dev, "Control initialization error %d\n", ret);
> > > > +		media_entity_cleanup(&imx290->sd.entity);
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	imx290->sd.state_lock = imx290->ctrls.lock;
> > > > +
> > > > +	v4l2_subdev_init_finalize(&imx290->sd);
> > > > +
> > > > +	state = v4l2_subdev_lock_and_get_active_state(&imx290->sd);
> > > > +	format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0);
> > > > +	imx290_ctrl_update(imx290, format, imx290->current_mode);
> > > > +	v4l2_subdev_unlock_state(state);
> > > 
> > > Do you need to check for state == NULL here?
> > 
> > No, v4l2_subdev_lock_and_get_active_state() can only return NULL if the
> > subdev driver doesn't have an active state, and
> > v4l2_subdev_init_finalize() guarantees it has one.
> 
> I see, but that is only true if v4l2_subdev_init_finalize()/
> __v4l2_subdev_init_finalize() does not return an error. AFAICS it's possible 
> due to memory allocation failure or pad init_cfg() fails.

Good point, I'll check the return value of v4l2_subdev_init_finalize()..

> > > > +
> > > > 
> > > >  	return 0;
> > > >  
> > > >  }
> > > > 
> > > > @@ -1268,12 +1247,10 @@ static int imx290_probe(struct i2c_client *client)
> > > > 
> > > >  	if (ret)
> > > >  	
> > > >  		return ret;
> > > > 
> > > > -	mutex_init(&imx290->lock);
> > > > -
> > > > 
> > > >  	/* Initialize and register subdev. */
> > > >  	ret = imx290_subdev_init(imx290);
> > > >  	if (ret)
> > > > 
> > > > -		goto err_mutex;
> > > > +		return ret;
> > > > 
> > > >  	ret = v4l2_async_register_subdev(&imx290->sd);
> > > >  	if (ret < 0) {
> > > > 
> > > > @@ -1305,8 +1282,6 @@ static int imx290_probe(struct i2c_client *client)
> > > > 
> > > >  err_subdev:
> > > >  	imx290_subdev_cleanup(imx290);
> > > > 
> > > > -err_mutex:
> > > > -	mutex_destroy(&imx290->lock);
> > > > 
> > > >  	return ret;
> > > >  
> > > >  }
> > > > 
> > > > @@ -1319,8 +1294,6 @@ static void imx290_remove(struct i2c_client *client)
> > > > 
> > > >  	v4l2_async_unregister_subdev(sd);
> > > >  	imx290_subdev_cleanup(imx290);
> > > > 
> > > > -	mutex_destroy(&imx290->lock);
> > > > -
> > > > 
> > > >  	pm_runtime_disable(imx290->dev);
> > > >  	if (!pm_runtime_status_suspended(imx290->dev))
> > > >  	
> > > >  		imx290_power_off(imx290->dev);

-- 
Regards,

Laurent Pinchart

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

end of thread, other threads:[~2022-11-28 18:23 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-22 22:32 [PATCH v1 00/15] media: i2c: imx290: Miscellaneous improvements Laurent Pinchart
2022-11-22 22:32 ` [PATCH v1 01/15] media: i2c: imx290: Group functions in sections Laurent Pinchart
2022-11-23  7:46   ` Alexander Stein
2022-11-22 22:32 ` [PATCH v1 02/15] media: i2c: imx290: Factor out subdev init and cleanup to functions Laurent Pinchart
2022-11-23  7:44   ` Alexander Stein
2022-11-23 10:04     ` Laurent Pinchart
2022-11-24 18:31   ` Dave Stevenson
2022-11-24 19:25     ` Laurent Pinchart
2022-11-22 22:32 ` [PATCH v1 03/15] media: i2c: imx290: Factor out control update code to a function Laurent Pinchart
2022-11-23  7:51   ` Alexander Stein
2022-11-23 10:08     ` Laurent Pinchart
2022-11-22 22:32 ` [PATCH v1 04/15] media: i2c: imx290: Access link_freq_index directly Laurent Pinchart
2022-11-23  7:53   ` Alexander Stein
2022-11-22 22:32 ` [PATCH v1 05/15] media: i2c: imx290: Pass format and mode to imx290_calc_pixel_rate() Laurent Pinchart
2022-11-23  9:06   ` Alexander Stein
2022-11-22 22:32 ` [PATCH v1 06/15] media: i2c: imx290: Compute pixel rate and blanking in one place Laurent Pinchart
2022-11-23  7:59   ` Alexander Stein
2022-11-23  9:58   ` Dave Stevenson
2022-11-22 22:32 ` [PATCH v1 07/15] media: i2c: imx290: Factor out black level setting to a function Laurent Pinchart
2022-11-23  8:03   ` Alexander Stein
2022-11-23 10:08   ` Dave Stevenson
2022-11-23 11:00     ` Laurent Pinchart
2022-11-24 15:10       ` Dave Stevenson
2022-11-24 16:57         ` Laurent Pinchart
2022-11-24 17:16           ` David Plowman
2022-11-24 18:02             ` Laurent Pinchart
2022-11-22 22:32 ` [PATCH v1 08/15] media: i2c: imx290: Factor out DT parsing to separate function Laurent Pinchart
2022-11-23  8:14   ` Alexander Stein
2022-11-23 10:16     ` Laurent Pinchart
2022-11-22 22:32 ` [PATCH v1 09/15] media: i2c: imx290: Use dev_err_probe() Laurent Pinchart
2022-11-23  8:16   ` Alexander Stein
2022-11-22 22:32 ` [PATCH v1 10/15] media: i2c: imx290: Factor out clock initialization to separate function Laurent Pinchart
2022-11-23  8:18   ` Alexander Stein
2022-11-22 22:32 ` [PATCH v1 11/15] media: i2c: imx290: Use V4L2 subdev active state Laurent Pinchart
2022-11-23  8:42   ` Alexander Stein
2022-11-23 10:49     ` Laurent Pinchart
2022-11-28 15:33       ` Alexander Stein
2022-11-28 18:11         ` Laurent Pinchart
2022-11-22 22:32 ` [PATCH v1 12/15] media: i2c: imx290: Rename, extend and expand usage of imx290_pixfmt Laurent Pinchart
2022-11-23  8:53   ` Alexander Stein
2022-11-22 22:32 ` [PATCH v1 13/15] media: i2c: imx290: Use runtime PM autosuspend Laurent Pinchart
2022-11-22 22:32 ` [PATCH v1 14/15] media: i2c: imx290: Configure data lanes at start time Laurent Pinchart
2022-11-23  9:01   ` Alexander Stein
2022-11-23 10:16   ` Dave Stevenson
2022-11-23 14:28     ` Laurent Pinchart
2022-11-24 18:02       ` Dave Stevenson
2022-11-24 19:19         ` Laurent Pinchart
2022-11-22 22:32 ` [PATCH v1 15/15] media: i2c: imx290: Simplify imx290_set_data_lanes() Laurent Pinchart
2022-11-23  9:04   ` Alexander Stein
2022-11-23 10:25     ` 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.