* [PATCH v2 0/8] media: imx335: Vflip, active state and binning support
@ 2025-09-11 8:14 Jai Luthra
2025-09-11 8:14 ` [PATCH v2 1/8] media: imx335: Rectify name of mode struct Jai Luthra
` (7 more replies)
0 siblings, 8 replies; 14+ messages in thread
From: Jai Luthra @ 2025-09-11 8:14 UTC (permalink / raw)
To: Kieran Bingham, Sakari Ailus, Mauro Carvalho Chehab
Cc: linux-media, linux-kernel, Tommaso Merciai
Hi,
This series adds support for 2x2 binning mode for Sony IMX335, along
with some improvements like using subdev active state, using
{enable,disable}_streams APIs and fixing the native pixel array width to
match the datasheet.
These changes are done on top of a couple of already reviewed patches
from Umang's series from last year that added support for vertical flip [1]
[1]: https://lore.kernel.org/all/20240830062639.72947-1-umang.jain@ideasonboard.com/
There is some WIP to make the resolution freely configurable:
- [x] Add support for a cropped mode (1944x1100)
- [ ] Fix exposure timing limits when cropping
- [ ] Add support for a cropped binned mode
- [ ] Make it freely configurable (depends on new control for binning)
Available in the branch here:
https://github.com/jailuthra/linux/commits/imx335_binning
Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
---
Changes in v2:
- Split runtime PM re-ordering from the patch introducing state support
- Use v4l2_rect instead of macros for sensor native and crop sizes
- Add new patch to introduce enable/disable streams API support with
s_stream fallback helper
- Link to v1: https://lore.kernel.org/r/20250813-imx335_binning-v1-0-a42b687d8541@ideasonboard.com
---
Jai Luthra (6):
media: imx335: Update the native pixel array width
media: imx335: Update HBLANK range on mode change
media: imx335: Handle runtime PM in leaf functions
media: imx355: Use subdev active state
media: imx335: Support 2x2 binning
media: imx335: Switch to {enable,disable}_streams
Umang Jain (2):
media: imx335: Rectify name of mode struct
media: imx335: Support vertical flip
drivers/media/i2c/imx335.c | 516 ++++++++++++++++++++++++++++++---------------
1 file changed, 347 insertions(+), 169 deletions(-)
---
base-commit: 76d2d8f7b6348af5f524b7ed21b5ad7d9686971b
change-id: 20250606-imx335_binning-d43dce921b0a
Best regards,
--
Jai Luthra <jai.luthra@ideasonboard.com>
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v2 1/8] media: imx335: Rectify name of mode struct 2025-09-11 8:14 [PATCH v2 0/8] media: imx335: Vflip, active state and binning support Jai Luthra @ 2025-09-11 8:14 ` Jai Luthra 2025-09-11 8:14 ` [PATCH v2 2/8] media: imx335: Support vertical flip Jai Luthra ` (6 subsequent siblings) 7 siblings, 0 replies; 14+ messages in thread From: Jai Luthra @ 2025-09-11 8:14 UTC (permalink / raw) To: Kieran Bingham, Sakari Ailus, Mauro Carvalho Chehab Cc: linux-media, linux-kernel, Tommaso Merciai From: Umang Jain <umang.jain@ideasonboard.com> In commit 81495a59baeb ("media: imx335: Fix active area height discrepency") the height for the mode struct was rectified to '1944'. However, the name of mode struct is still reflecting to '1940'. Update it. Signed-off-by: Umang Jain <umang.jain@ideasonboard.com> Reviewed-by: Tommaso Merciai <tomm.merciai@gmail.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> --- drivers/media/i2c/imx335.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index c043df2f15fb25b3a56422092f99a1fd9a508fa9..213cfb7276611f522db0643186f25a8fef3c39db 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -252,7 +252,7 @@ static const int imx335_tpg_val[] = { }; /* Sensor mode registers */ -static const struct cci_reg_sequence mode_2592x1940_regs[] = { +static const struct cci_reg_sequence mode_2592x1944_regs[] = { { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY }, { IMX335_REG_MASTER_MODE, 0x00 }, { IMX335_REG_WINMODE, 0x04 }, @@ -416,8 +416,8 @@ static const struct imx335_mode supported_mode = { .vblank_max = 133060, .pclk = 396000000, .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2592x1940_regs), - .regs = mode_2592x1940_regs, + .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), + .regs = mode_2592x1944_regs, }, }; -- 2.51.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 2/8] media: imx335: Support vertical flip 2025-09-11 8:14 [PATCH v2 0/8] media: imx335: Vflip, active state and binning support Jai Luthra 2025-09-11 8:14 ` [PATCH v2 1/8] media: imx335: Rectify name of mode struct Jai Luthra @ 2025-09-11 8:14 ` Jai Luthra 2025-09-11 8:14 ` [PATCH v2 3/8] media: imx335: Update the native pixel array width Jai Luthra ` (5 subsequent siblings) 7 siblings, 0 replies; 14+ messages in thread From: Jai Luthra @ 2025-09-11 8:14 UTC (permalink / raw) To: Kieran Bingham, Sakari Ailus, Mauro Carvalho Chehab Cc: linux-media, linux-kernel, Tommaso Merciai From: Umang Jain <umang.jain@ideasonboard.com> Support vertical flip by setting REG_VREVERSE. Additional registers also needs to be set per mode, according to the readout direction (normal/inverted) as mentioned in the data sheet. Since the register IMX335_REG_AREA3_ST_ADR_1 is based on the flip (and is set via vflip related registers), it has been moved out of the 2592x1944 mode regs. Signed-off-by: Umang Jain <umang.jain@ideasonboard.com> Reviewed-by: Tommaso Merciai <tomm.merciai@gmail.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> --- drivers/media/i2c/imx335.c | 71 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 213cfb7276611f522db0643186f25a8fef3c39db..27baf6c9b426a324632db7e393514463611a5ae7 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -56,6 +56,9 @@ #define IMX335_AGAIN_STEP 1 #define IMX335_AGAIN_DEFAULT 0 +/* Vertical flip */ +#define IMX335_REG_VREVERSE CCI_REG8(0x304f) + #define IMX335_REG_TPG_TESTCLKEN CCI_REG8(0x3148) #define IMX335_REG_INCLKSEL1 CCI_REG16_LE(0x314c) @@ -155,6 +158,8 @@ static const char * const imx335_supply_name[] = { * @vblank_max: Maximum vertical blanking in lines * @pclk: Sensor pixel clock * @reg_list: Register list for sensor mode + * @vflip_normal: Register list vflip (normal readout) + * @vflip_inverted: Register list vflip (inverted readout) */ struct imx335_mode { u32 width; @@ -166,6 +171,8 @@ struct imx335_mode { u32 vblank_max; u64 pclk; struct imx335_reg_list reg_list; + struct imx335_reg_list vflip_normal; + struct imx335_reg_list vflip_inverted; }; /** @@ -183,6 +190,7 @@ struct imx335_mode { * @pclk_ctrl: Pointer to pixel clock control * @hblank_ctrl: Pointer to horizontal blanking control * @vblank_ctrl: Pointer to vertical blanking control + * @vflip: Pointer to vertical flip control * @exp_ctrl: Pointer to exposure control * @again_ctrl: Pointer to analog gain control * @vblank: Vertical blanking in lines @@ -207,6 +215,7 @@ struct imx335 { struct v4l2_ctrl *pclk_ctrl; struct v4l2_ctrl *hblank_ctrl; struct v4l2_ctrl *vblank_ctrl; + struct v4l2_ctrl *vflip; struct { struct v4l2_ctrl *exp_ctrl; struct v4l2_ctrl *again_ctrl; @@ -259,7 +268,6 @@ static const struct cci_reg_sequence mode_2592x1944_regs[] = { { IMX335_REG_HTRIMMING_START, 48 }, { IMX335_REG_HNUM, 2592 }, { IMX335_REG_Y_OUT_SIZE, 1944 }, - { IMX335_REG_AREA3_ST_ADR_1, 176 }, { IMX335_REG_AREA3_WIDTH_1, 3928 }, { IMX335_REG_OPB_SIZE_V, 0 }, { IMX335_REG_XVS_XHS_DRV, 0x00 }, @@ -333,6 +341,26 @@ static const struct cci_reg_sequence mode_2592x1944_regs[] = { { CCI_REG8(0x3a00), 0x00 }, }; +static const struct cci_reg_sequence mode_2592x1944_vflip_normal[] = { + { IMX335_REG_AREA3_ST_ADR_1, 176 }, + + /* Undocumented V-Flip related registers on Page 55 of datasheet. */ + { CCI_REG8(0x3081), 0x02, }, + { CCI_REG8(0x3083), 0x02, }, + { CCI_REG16_LE(0x30b6), 0x00 }, + { CCI_REG16_LE(0x3116), 0x08 }, +}; + +static const struct cci_reg_sequence mode_2592x1944_vflip_inverted[] = { + { IMX335_REG_AREA3_ST_ADR_1, 4112 }, + + /* Undocumented V-Flip related registers on Page 55 of datasheet. */ + { CCI_REG8(0x3081), 0xfe, }, + { CCI_REG8(0x3083), 0xfe, }, + { CCI_REG16_LE(0x30b6), 0x1fa }, + { CCI_REG16_LE(0x3116), 0x002 }, +}; + static const struct cci_reg_sequence raw10_framefmt_regs[] = { { IMX335_REG_ADBIT, 0x00 }, { IMX335_REG_MDBIT, 0x00 }, @@ -419,6 +447,14 @@ static const struct imx335_mode supported_mode = { .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), .regs = mode_2592x1944_regs, }, + .vflip_normal = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_normal), + .regs = mode_2592x1944_vflip_normal, + }, + .vflip_inverted = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_inverted), + .regs = mode_2592x1944_vflip_inverted, + }, }; /** @@ -492,6 +528,26 @@ static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain) return ret; } +static int imx335_update_vertical_flip(struct imx335 *imx335, u32 vflip) +{ + int ret = 0; + + if (vflip) + cci_multi_reg_write(imx335->cci, + imx335->cur_mode->vflip_inverted.regs, + imx335->cur_mode->vflip_inverted.num_of_regs, + &ret); + else + cci_multi_reg_write(imx335->cci, + imx335->cur_mode->vflip_normal.regs, + imx335->cur_mode->vflip_normal.num_of_regs, + &ret); + if (ret) + return ret; + + return cci_write(imx335->cci, IMX335_REG_VREVERSE, vflip, NULL); +} + static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index) { int ret = 0; @@ -593,6 +649,10 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) ret = imx335_update_exp_gain(imx335, exposure, analog_gain); + break; + case V4L2_CID_VFLIP: + ret = imx335_update_vertical_flip(imx335, ctrl->val); + break; case V4L2_CID_TEST_PATTERN: ret = imx335_update_test_pattern(imx335, ctrl->val); @@ -1175,7 +1235,7 @@ static int imx335_init_controls(struct imx335 *imx335) return ret; /* v4l2_fwnode_device_properties can add two more controls */ - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); if (ret) return ret; @@ -1210,6 +1270,13 @@ static int imx335_init_controls(struct imx335 *imx335) v4l2_ctrl_cluster(2, &imx335->exp_ctrl); + imx335->vflip = v4l2_ctrl_new_std(ctrl_hdlr, + &imx335_ctrl_ops, + V4L2_CID_VFLIP, + 0, 1, 1, 0); + if (imx335->vflip) + imx335->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + imx335->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &imx335_ctrl_ops, V4L2_CID_VBLANK, -- 2.51.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 3/8] media: imx335: Update the native pixel array width 2025-09-11 8:14 [PATCH v2 0/8] media: imx335: Vflip, active state and binning support Jai Luthra 2025-09-11 8:14 ` [PATCH v2 1/8] media: imx335: Rectify name of mode struct Jai Luthra 2025-09-11 8:14 ` [PATCH v2 2/8] media: imx335: Support vertical flip Jai Luthra @ 2025-09-11 8:14 ` Jai Luthra 2025-09-11 8:14 ` [PATCH v2 4/8] media: imx335: Update HBLANK range on mode change Jai Luthra ` (4 subsequent siblings) 7 siblings, 0 replies; 14+ messages in thread From: Jai Luthra @ 2025-09-11 8:14 UTC (permalink / raw) To: Kieran Bingham, Sakari Ailus, Mauro Carvalho Chehab Cc: linux-media, linux-kernel, Tommaso Merciai The sensor datasheet reports actual total number of pixels as 2696x2044. This becomes important for supporting 2x2 binning modes that can go beyond the current maximum pixel array width set here. Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> --- drivers/media/i2c/imx335.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 27baf6c9b426a324632db7e393514463611a5ae7..3522a6b1ca14e27c46a55e18e5bf51240ea5a6ee 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -124,12 +124,20 @@ #define IMX335_NUM_DATA_LANES 4 /* IMX335 native and active pixel array size. */ -#define IMX335_NATIVE_WIDTH 2616U -#define IMX335_NATIVE_HEIGHT 1964U -#define IMX335_PIXEL_ARRAY_LEFT 12U -#define IMX335_PIXEL_ARRAY_TOP 12U -#define IMX335_PIXEL_ARRAY_WIDTH 2592U -#define IMX335_PIXEL_ARRAY_HEIGHT 1944U +static const struct v4l2_rect imx335_native_area = { + .top = 0, + .left = 0, + .width = 2696, + .height = 2044, +}; + +static const struct v4l2_rect imx335_active_area = { + .top = 50, + .left = 52, + .width = 2592, + .height = 1944, +}; + /** * struct imx335_reg_list - imx335 sensor register list @@ -869,21 +877,13 @@ static int imx335_get_selection(struct v4l2_subdev *sd, { switch (sel->target) { case V4L2_SEL_TGT_NATIVE_SIZE: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = IMX335_NATIVE_WIDTH; - sel->r.height = IMX335_NATIVE_HEIGHT; - + sel->r = imx335_native_area; return 0; case V4L2_SEL_TGT_CROP: case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = IMX335_PIXEL_ARRAY_TOP; - sel->r.left = IMX335_PIXEL_ARRAY_LEFT; - sel->r.width = IMX335_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX335_PIXEL_ARRAY_HEIGHT; - + sel->r = imx335_active_area; return 0; } -- 2.51.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 4/8] media: imx335: Update HBLANK range on mode change 2025-09-11 8:14 [PATCH v2 0/8] media: imx335: Vflip, active state and binning support Jai Luthra ` (2 preceding siblings ...) 2025-09-11 8:14 ` [PATCH v2 3/8] media: imx335: Update the native pixel array width Jai Luthra @ 2025-09-11 8:14 ` Jai Luthra 2025-09-11 8:14 ` [PATCH v2 5/8] media: imx335: Handle runtime PM in leaf functions Jai Luthra ` (3 subsequent siblings) 7 siblings, 0 replies; 14+ messages in thread From: Jai Luthra @ 2025-09-11 8:14 UTC (permalink / raw) To: Kieran Bingham, Sakari Ailus, Mauro Carvalho Chehab Cc: linux-media, linux-kernel, Tommaso Merciai While switching modes, updating to a different value of HBLANK isn't sufficient, as this is a read-only control with a single allowed value, and thus hblank_min == hblank_max == hblank of the default mode. So to correctly update the user-facing value of the HBLANK parameter, which is necessary for correct framerate calculation, update the whole range when switching modes. Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> --- drivers/media/i2c/imx335.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 3522a6b1ca14e27c46a55e18e5bf51240ea5a6ee..62a76517ecb263a4fd7e7a593c02a3cdaf3da190 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -493,7 +493,8 @@ static int imx335_update_controls(struct imx335 *imx335, if (ret) return ret; - ret = __v4l2_ctrl_s_ctrl(imx335->hblank_ctrl, mode->hblank); + ret = __v4l2_ctrl_modify_range(imx335->hblank_ctrl, mode->hblank, + mode->hblank, 1, mode->hblank); if (ret) return ret; -- 2.51.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 5/8] media: imx335: Handle runtime PM in leaf functions 2025-09-11 8:14 [PATCH v2 0/8] media: imx335: Vflip, active state and binning support Jai Luthra ` (3 preceding siblings ...) 2025-09-11 8:14 ` [PATCH v2 4/8] media: imx335: Update HBLANK range on mode change Jai Luthra @ 2025-09-11 8:14 ` Jai Luthra 2025-09-11 11:14 ` Kieran Bingham 2025-09-11 8:14 ` [PATCH v2 6/8] media: imx355: Use subdev active state Jai Luthra ` (2 subsequent siblings) 7 siblings, 1 reply; 14+ messages in thread From: Jai Luthra @ 2025-09-11 8:14 UTC (permalink / raw) To: Kieran Bingham, Sakari Ailus, Mauro Carvalho Chehab Cc: linux-media, linux-kernel, Tommaso Merciai Simplify .s_stream callback implementation by moving the runtime PM calls to the leaf functions. This patch should not affect any functionality. Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> --- drivers/media/i2c/imx335.c | 51 ++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 62a76517ecb263a4fd7e7a593c02a3cdaf3da190..7631f41e6f1e65695fb76a66d9ac5a3588c69658 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -919,13 +919,17 @@ static int imx335_start_streaming(struct imx335 *imx335) const struct imx335_reg_list *reg_list; int ret; + ret = pm_runtime_resume_and_get(imx335->dev); + if (ret < 0) + return ret; + /* Setup PLL */ reg_list = &link_freq_reglist[__ffs(imx335->link_freq_bitmap)]; ret = cci_multi_reg_write(imx335->cci, reg_list->regs, reg_list->num_of_regs, NULL); if (ret) { dev_err(imx335->dev, "%s failed to set plls\n", __func__); - return ret; + goto err_rpm_put; } /* Write sensor mode registers */ @@ -934,27 +938,27 @@ static int imx335_start_streaming(struct imx335 *imx335) reg_list->num_of_regs, NULL); if (ret) { dev_err(imx335->dev, "fail to write initial registers\n"); - return ret; + goto err_rpm_put; } ret = imx335_set_framefmt(imx335); if (ret) { dev_err(imx335->dev, "%s failed to set frame format: %d\n", __func__, ret); - return ret; + goto err_rpm_put; } /* Configure lanes */ ret = cci_write(imx335->cci, IMX335_REG_LANEMODE, imx335->lane_mode, NULL); if (ret) - return ret; + goto err_rpm_put; /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler); if (ret) { dev_err(imx335->dev, "fail to setup handler\n"); - return ret; + goto err_rpm_put; } /* Start streaming */ @@ -962,25 +966,29 @@ static int imx335_start_streaming(struct imx335 *imx335) IMX335_MODE_STREAMING, NULL); if (ret) { dev_err(imx335->dev, "fail to start streaming\n"); - return ret; + goto err_rpm_put; } /* Initial regulator stabilization period */ usleep_range(18000, 20000); return 0; + +err_rpm_put: + pm_runtime_put(imx335->dev); + + return ret; } /** * imx335_stop_streaming() - Stop sensor stream * @imx335: pointer to imx335 device - * - * Return: 0 if successful, error code otherwise. */ -static int imx335_stop_streaming(struct imx335 *imx335) +static void imx335_stop_streaming(struct imx335 *imx335) { - return cci_write(imx335->cci, IMX335_REG_MODE_SELECT, - IMX335_MODE_STANDBY, NULL); + cci_write(imx335->cci, IMX335_REG_MODE_SELECT, + IMX335_MODE_STANDBY, NULL); + pm_runtime_put(imx335->dev); } /** @@ -993,30 +1001,15 @@ static int imx335_stop_streaming(struct imx335 *imx335) static int imx335_set_stream(struct v4l2_subdev *sd, int enable) { struct imx335 *imx335 = to_imx335(sd); - int ret; + int ret = 0; mutex_lock(&imx335->mutex); - if (enable) { - ret = pm_runtime_resume_and_get(imx335->dev); - if (ret) - goto error_unlock; - + if (enable) ret = imx335_start_streaming(imx335); - if (ret) - goto error_power_off; - } else { + else imx335_stop_streaming(imx335); - pm_runtime_put(imx335->dev); - } - - mutex_unlock(&imx335->mutex); - return 0; - -error_power_off: - pm_runtime_put(imx335->dev); -error_unlock: mutex_unlock(&imx335->mutex); return ret; -- 2.51.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 5/8] media: imx335: Handle runtime PM in leaf functions 2025-09-11 8:14 ` [PATCH v2 5/8] media: imx335: Handle runtime PM in leaf functions Jai Luthra @ 2025-09-11 11:14 ` Kieran Bingham 2025-09-15 6:28 ` Jai Luthra 0 siblings, 1 reply; 14+ messages in thread From: Kieran Bingham @ 2025-09-11 11:14 UTC (permalink / raw) To: Jai Luthra, Mauro Carvalho Chehab, Sakari Ailus Cc: linux-media, linux-kernel, Tommaso Merciai Quoting Jai Luthra (2025-09-11 09:14:21) > Simplify .s_stream callback implementation by moving the runtime PM > calls to the leaf functions. This patch should not affect any > functionality. > > Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> Looks reasonable to me and although the stop_streaming function changes to no longer return a value - it was previously unused so no functional change indeed as far as I can see: Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > --- > drivers/media/i2c/imx335.c | 51 ++++++++++++++++++++-------------------------- > 1 file changed, 22 insertions(+), 29 deletions(-) > > diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c > index 62a76517ecb263a4fd7e7a593c02a3cdaf3da190..7631f41e6f1e65695fb76a66d9ac5a3588c69658 100644 > --- a/drivers/media/i2c/imx335.c > +++ b/drivers/media/i2c/imx335.c > @@ -919,13 +919,17 @@ static int imx335_start_streaming(struct imx335 *imx335) > const struct imx335_reg_list *reg_list; > int ret; > > + ret = pm_runtime_resume_and_get(imx335->dev); > + if (ret < 0) > + return ret; > + > /* Setup PLL */ > reg_list = &link_freq_reglist[__ffs(imx335->link_freq_bitmap)]; > ret = cci_multi_reg_write(imx335->cci, reg_list->regs, > reg_list->num_of_regs, NULL); > if (ret) { > dev_err(imx335->dev, "%s failed to set plls\n", __func__); > - return ret; > + goto err_rpm_put; > } > > /* Write sensor mode registers */ > @@ -934,27 +938,27 @@ static int imx335_start_streaming(struct imx335 *imx335) > reg_list->num_of_regs, NULL); > if (ret) { > dev_err(imx335->dev, "fail to write initial registers\n"); > - return ret; > + goto err_rpm_put; > } > > ret = imx335_set_framefmt(imx335); > if (ret) { > dev_err(imx335->dev, "%s failed to set frame format: %d\n", > __func__, ret); > - return ret; > + goto err_rpm_put; > } > > /* Configure lanes */ > ret = cci_write(imx335->cci, IMX335_REG_LANEMODE, > imx335->lane_mode, NULL); > if (ret) > - return ret; > + goto err_rpm_put; > > /* Setup handler will write actual exposure and gain */ > ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler); > if (ret) { > dev_err(imx335->dev, "fail to setup handler\n"); > - return ret; > + goto err_rpm_put; > } > > /* Start streaming */ > @@ -962,25 +966,29 @@ static int imx335_start_streaming(struct imx335 *imx335) > IMX335_MODE_STREAMING, NULL); > if (ret) { > dev_err(imx335->dev, "fail to start streaming\n"); > - return ret; > + goto err_rpm_put; > } > > /* Initial regulator stabilization period */ > usleep_range(18000, 20000); > > return 0; > + > +err_rpm_put: > + pm_runtime_put(imx335->dev); > + > + return ret; > } > > /** > * imx335_stop_streaming() - Stop sensor stream > * @imx335: pointer to imx335 device > - * > - * Return: 0 if successful, error code otherwise. > */ > -static int imx335_stop_streaming(struct imx335 *imx335) > +static void imx335_stop_streaming(struct imx335 *imx335) > { > - return cci_write(imx335->cci, IMX335_REG_MODE_SELECT, > - IMX335_MODE_STANDBY, NULL); > + cci_write(imx335->cci, IMX335_REG_MODE_SELECT, > + IMX335_MODE_STANDBY, NULL); > + pm_runtime_put(imx335->dev); > } > > /** > @@ -993,30 +1001,15 @@ static int imx335_stop_streaming(struct imx335 *imx335) > static int imx335_set_stream(struct v4l2_subdev *sd, int enable) > { > struct imx335 *imx335 = to_imx335(sd); > - int ret; > + int ret = 0; > > mutex_lock(&imx335->mutex); > > - if (enable) { > - ret = pm_runtime_resume_and_get(imx335->dev); > - if (ret) > - goto error_unlock; > - > + if (enable) > ret = imx335_start_streaming(imx335); > - if (ret) > - goto error_power_off; > - } else { > + else > imx335_stop_streaming(imx335); > - pm_runtime_put(imx335->dev); > - } > - > - mutex_unlock(&imx335->mutex); > > - return 0; > - > -error_power_off: > - pm_runtime_put(imx335->dev); > -error_unlock: > mutex_unlock(&imx335->mutex); > > return ret; > > -- > 2.51.0 > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 5/8] media: imx335: Handle runtime PM in leaf functions 2025-09-11 11:14 ` Kieran Bingham @ 2025-09-15 6:28 ` Jai Luthra 0 siblings, 0 replies; 14+ messages in thread From: Jai Luthra @ 2025-09-15 6:28 UTC (permalink / raw) To: Kieran Bingham, Mauro Carvalho Chehab, Sakari Ailus Cc: linux-media, linux-kernel, Tommaso Merciai Hi Kieran, Thanks for the review. Quoting Kieran Bingham (2025-09-11 16:44:45) > Quoting Jai Luthra (2025-09-11 09:14:21) > > Simplify .s_stream callback implementation by moving the runtime PM > > calls to the leaf functions. This patch should not affect any > > functionality. > > > > Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> > > Looks reasonable to me and although the stop_streaming function changes > to no longer return a value - it was previously unused so no functional > change indeed as far as I can see: Yeah it was unused before. This anyway gets fixed when switching to streams API in the last patch of the series. > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > > --- > > drivers/media/i2c/imx335.c | 51 ++++++++++++++++++++-------------------------- > > 1 file changed, 22 insertions(+), 29 deletions(-) > > Thanks, Jai ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 6/8] media: imx355: Use subdev active state 2025-09-11 8:14 [PATCH v2 0/8] media: imx335: Vflip, active state and binning support Jai Luthra ` (4 preceding siblings ...) 2025-09-11 8:14 ` [PATCH v2 5/8] media: imx335: Handle runtime PM in leaf functions Jai Luthra @ 2025-09-11 8:14 ` Jai Luthra 2025-09-11 14:30 ` Kieran Bingham 2025-09-11 8:14 ` [PATCH v2 7/8] media: imx335: Support 2x2 binning Jai Luthra 2025-09-11 8:14 ` [PATCH v2 8/8] media: imx335: Switch to {enable,disable}_streams Jai Luthra 7 siblings, 1 reply; 14+ messages in thread From: Jai Luthra @ 2025-09-11 8:14 UTC (permalink / raw) To: Kieran Bingham, Sakari Ailus, Mauro Carvalho Chehab Cc: linux-media, linux-kernel, Tommaso Merciai Port the driver to use the subdev active state. This simplifies locking, and makes it easier to support different crop sizes for binned modes, by storing the crop rectangle inside the subdev state. Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> --- drivers/media/i2c/imx335.c | 79 ++++++++++++---------------------------------- 1 file changed, 21 insertions(+), 58 deletions(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 7631f41e6f1e65695fb76a66d9ac5a3588c69658..9b9bbe6473d545bc5618c0fe8191df0b18037cd8 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -204,7 +204,6 @@ struct imx335_mode { * @vblank: Vertical blanking in lines * @lane_mode: Mode for number of connected data lanes * @cur_mode: Pointer to current selected sensor mode - * @mutex: Mutex for serializing sensor controls * @link_freq_bitmap: Menu bitmap for link_freq_ctrl * @cur_mbus_code: Currently selected media bus format code */ @@ -231,7 +230,6 @@ struct imx335 { u32 vblank; u32 lane_mode; const struct imx335_mode *cur_mode; - struct mutex mutex; unsigned long link_freq_bitmap; u32 cur_mbus_code; }; @@ -766,36 +764,6 @@ static void imx335_fill_pad_format(struct imx335 *imx335, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * imx335_get_pad_format() - Get subdevice pad format - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ -static int imx335_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx335 *imx335 = to_imx335(sd); - - mutex_lock(&imx335->mutex); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *framefmt; - - framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); - fmt->format = *framefmt; - } else { - imx335_fill_pad_format(imx335, imx335->cur_mode, fmt); - } - - mutex_unlock(&imx335->mutex); - - return 0; -} - /** * imx335_set_pad_format() - Set subdevice pad format * @sd: pointer to imx335 V4L2 sub-device structure @@ -809,12 +777,12 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct imx335 *imx335 = to_imx335(sd); + struct v4l2_mbus_framefmt *format; const struct imx335_mode *mode; int i, ret = 0; - mutex_lock(&imx335->mutex); - mode = &supported_mode; + for (i = 0; i < ARRAY_SIZE(imx335_mbus_codes); i++) { if (imx335_mbus_codes[i] == fmt->format.code) imx335->cur_mbus_code = imx335_mbus_codes[i]; @@ -822,19 +790,15 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, imx335_fill_pad_format(imx335, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *framefmt; + format = v4l2_subdev_state_get_format(sd_state, fmt->pad); + *format = fmt->format; - framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); - *framefmt = fmt->format; - } else { + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ret = imx335_update_controls(imx335, mode); if (!ret) imx335->cur_mode = mode; } - mutex_unlock(&imx335->mutex); - return ret; } @@ -854,12 +818,10 @@ static int imx335_init_state(struct v4l2_subdev *sd, fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; imx335_fill_pad_format(imx335, &supported_mode, &fmt); - mutex_lock(&imx335->mutex); __v4l2_ctrl_modify_range(imx335->link_freq_ctrl, 0, __fls(imx335->link_freq_bitmap), ~(imx335->link_freq_bitmap), __ffs(imx335->link_freq_bitmap)); - mutex_unlock(&imx335->mutex); return imx335_set_pad_format(sd, sd_state, &fmt); } @@ -1001,16 +963,17 @@ static void imx335_stop_streaming(struct imx335 *imx335) static int imx335_set_stream(struct v4l2_subdev *sd, int enable) { struct imx335 *imx335 = to_imx335(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&imx335->mutex); + state = v4l2_subdev_lock_and_get_active_state(sd); if (enable) ret = imx335_start_streaming(imx335); else imx335_stop_streaming(imx335); - mutex_unlock(&imx335->mutex); + v4l2_subdev_unlock_state(state); return ret; } @@ -1138,7 +1101,7 @@ static const struct v4l2_subdev_pad_ops imx335_pad_ops = { .enum_frame_size = imx335_enum_frame_size, .get_selection = imx335_get_selection, .set_selection = imx335_get_selection, - .get_fmt = imx335_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx335_set_pad_format, }; @@ -1233,9 +1196,6 @@ static int imx335_init_controls(struct imx335 *imx335) if (ret) return ret; - /* Serialize controls with sensor device */ - ctrl_hdlr->lock = &imx335->mutex; - /* Initialize exposure and gain */ lpfr = mode->vblank + mode->height; imx335->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, @@ -1355,12 +1315,10 @@ static int imx335_probe(struct i2c_client *client) return ret; } - mutex_init(&imx335->mutex); - ret = imx335_power_on(imx335->dev); if (ret) { dev_err(imx335->dev, "failed to power-on the sensor\n"); - goto error_mutex_destroy; + return ret; } /* Check module identity */ @@ -1393,11 +1351,18 @@ static int imx335_probe(struct i2c_client *client) goto error_handler_free; } + imx335->sd.state_lock = imx335->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx335->sd); + if (ret < 0) { + dev_err(imx335->dev, "subdev init error\n"); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx335->sd); if (ret < 0) { dev_err(imx335->dev, "failed to register async subdev: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } pm_runtime_set_active(imx335->dev); @@ -1406,14 +1371,14 @@ static int imx335_probe(struct i2c_client *client) return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx335->sd); error_media_entity: media_entity_cleanup(&imx335->sd.entity); error_handler_free: v4l2_ctrl_handler_free(imx335->sd.ctrl_handler); error_power_off: imx335_power_off(imx335->dev); -error_mutex_destroy: - mutex_destroy(&imx335->mutex); return ret; } @@ -1427,9 +1392,9 @@ static int imx335_probe(struct i2c_client *client) static void imx335_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx335 *imx335 = to_imx335(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); @@ -1437,8 +1402,6 @@ static void imx335_remove(struct i2c_client *client) if (!pm_runtime_status_suspended(&client->dev)) imx335_power_off(&client->dev); pm_runtime_set_suspended(&client->dev); - - mutex_destroy(&imx335->mutex); } static const struct dev_pm_ops imx335_pm_ops = { -- 2.51.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 6/8] media: imx355: Use subdev active state 2025-09-11 8:14 ` [PATCH v2 6/8] media: imx355: Use subdev active state Jai Luthra @ 2025-09-11 14:30 ` Kieran Bingham 0 siblings, 0 replies; 14+ messages in thread From: Kieran Bingham @ 2025-09-11 14:30 UTC (permalink / raw) To: Jai Luthra, Mauro Carvalho Chehab, Sakari Ailus Cc: linux-media, linux-kernel, Tommaso Merciai Quoting Jai Luthra (2025-09-11 09:14:22) > Port the driver to use the subdev active state. This simplifies locking, > and makes it easier to support different crop sizes for binned modes, by > storing the crop rectangle inside the subdev state. > > Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> > --- > drivers/media/i2c/imx335.c | 79 ++++++++++++---------------------------------- > 1 file changed, 21 insertions(+), 58 deletions(-) > > diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c > index 7631f41e6f1e65695fb76a66d9ac5a3588c69658..9b9bbe6473d545bc5618c0fe8191df0b18037cd8 100644 > --- a/drivers/media/i2c/imx335.c > +++ b/drivers/media/i2c/imx335.c > @@ -204,7 +204,6 @@ struct imx335_mode { > * @vblank: Vertical blanking in lines > * @lane_mode: Mode for number of connected data lanes > * @cur_mode: Pointer to current selected sensor mode > - * @mutex: Mutex for serializing sensor controls > * @link_freq_bitmap: Menu bitmap for link_freq_ctrl > * @cur_mbus_code: Currently selected media bus format code > */ > @@ -231,7 +230,6 @@ struct imx335 { > u32 vblank; > u32 lane_mode; > const struct imx335_mode *cur_mode; > - struct mutex mutex; > unsigned long link_freq_bitmap; > u32 cur_mbus_code; > }; > @@ -766,36 +764,6 @@ static void imx335_fill_pad_format(struct imx335 *imx335, > fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; > } > > -/** > - * imx335_get_pad_format() - Get subdevice pad format > - * @sd: pointer to imx335 V4L2 sub-device structure > - * @sd_state: V4L2 sub-device configuration > - * @fmt: V4L2 sub-device format need to be set > - * > - * Return: 0 if successful, error code otherwise. > - */ > -static int imx335_get_pad_format(struct v4l2_subdev *sd, > - struct v4l2_subdev_state *sd_state, > - struct v4l2_subdev_format *fmt) > -{ > - struct imx335 *imx335 = to_imx335(sd); > - > - mutex_lock(&imx335->mutex); > - > - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > - struct v4l2_mbus_framefmt *framefmt; > - > - framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); > - fmt->format = *framefmt; > - } else { > - imx335_fill_pad_format(imx335, imx335->cur_mode, fmt); > - } > - > - mutex_unlock(&imx335->mutex); > - > - return 0; > -} > - > /** > * imx335_set_pad_format() - Set subdevice pad format > * @sd: pointer to imx335 V4L2 sub-device structure > @@ -809,12 +777,12 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, > struct v4l2_subdev_format *fmt) > { > struct imx335 *imx335 = to_imx335(sd); > + struct v4l2_mbus_framefmt *format; > const struct imx335_mode *mode; > int i, ret = 0; > > - mutex_lock(&imx335->mutex); > - > mode = &supported_mode; > + > for (i = 0; i < ARRAY_SIZE(imx335_mbus_codes); i++) { > if (imx335_mbus_codes[i] == fmt->format.code) > imx335->cur_mbus_code = imx335_mbus_codes[i]; > @@ -822,19 +790,15 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, > > imx335_fill_pad_format(imx335, mode, fmt); > > - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > - struct v4l2_mbus_framefmt *framefmt; > + format = v4l2_subdev_state_get_format(sd_state, fmt->pad); > + *format = fmt->format; > > - framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); > - *framefmt = fmt->format; > - } else { It's nice to drop that - and clean up the locking. Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { > ret = imx335_update_controls(imx335, mode); > if (!ret) > imx335->cur_mode = mode; > } > > - mutex_unlock(&imx335->mutex); > - > return ret; > } > > @@ -854,12 +818,10 @@ static int imx335_init_state(struct v4l2_subdev *sd, > fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; > imx335_fill_pad_format(imx335, &supported_mode, &fmt); > > - mutex_lock(&imx335->mutex); > __v4l2_ctrl_modify_range(imx335->link_freq_ctrl, 0, > __fls(imx335->link_freq_bitmap), > ~(imx335->link_freq_bitmap), > __ffs(imx335->link_freq_bitmap)); > - mutex_unlock(&imx335->mutex); > > return imx335_set_pad_format(sd, sd_state, &fmt); > } > @@ -1001,16 +963,17 @@ static void imx335_stop_streaming(struct imx335 *imx335) > static int imx335_set_stream(struct v4l2_subdev *sd, int enable) > { > struct imx335 *imx335 = to_imx335(sd); > + struct v4l2_subdev_state *state; > int ret = 0; > > - mutex_lock(&imx335->mutex); > + state = v4l2_subdev_lock_and_get_active_state(sd); > > if (enable) > ret = imx335_start_streaming(imx335); > else > imx335_stop_streaming(imx335); > > - mutex_unlock(&imx335->mutex); > + v4l2_subdev_unlock_state(state); > > return ret; > } > @@ -1138,7 +1101,7 @@ static const struct v4l2_subdev_pad_ops imx335_pad_ops = { > .enum_frame_size = imx335_enum_frame_size, > .get_selection = imx335_get_selection, > .set_selection = imx335_get_selection, > - .get_fmt = imx335_get_pad_format, > + .get_fmt = v4l2_subdev_get_fmt, > .set_fmt = imx335_set_pad_format, > }; > > @@ -1233,9 +1196,6 @@ static int imx335_init_controls(struct imx335 *imx335) > if (ret) > return ret; > > - /* Serialize controls with sensor device */ > - ctrl_hdlr->lock = &imx335->mutex; > - > /* Initialize exposure and gain */ > lpfr = mode->vblank + mode->height; > imx335->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, > @@ -1355,12 +1315,10 @@ static int imx335_probe(struct i2c_client *client) > return ret; > } > > - mutex_init(&imx335->mutex); > - > ret = imx335_power_on(imx335->dev); > if (ret) { > dev_err(imx335->dev, "failed to power-on the sensor\n"); > - goto error_mutex_destroy; > + return ret; > } > > /* Check module identity */ > @@ -1393,11 +1351,18 @@ static int imx335_probe(struct i2c_client *client) > goto error_handler_free; > } > > + imx335->sd.state_lock = imx335->ctrl_handler.lock; > + ret = v4l2_subdev_init_finalize(&imx335->sd); > + if (ret < 0) { > + dev_err(imx335->dev, "subdev init error\n"); > + goto error_media_entity; > + } > + > ret = v4l2_async_register_subdev_sensor(&imx335->sd); > if (ret < 0) { > dev_err(imx335->dev, > "failed to register async subdev: %d\n", ret); > - goto error_media_entity; > + goto error_subdev_cleanup; > } > > pm_runtime_set_active(imx335->dev); > @@ -1406,14 +1371,14 @@ static int imx335_probe(struct i2c_client *client) > > return 0; > > +error_subdev_cleanup: > + v4l2_subdev_cleanup(&imx335->sd); > error_media_entity: > media_entity_cleanup(&imx335->sd.entity); > error_handler_free: > v4l2_ctrl_handler_free(imx335->sd.ctrl_handler); > error_power_off: > imx335_power_off(imx335->dev); > -error_mutex_destroy: > - mutex_destroy(&imx335->mutex); > > return ret; > } > @@ -1427,9 +1392,9 @@ static int imx335_probe(struct i2c_client *client) > static void imx335_remove(struct i2c_client *client) > { > struct v4l2_subdev *sd = i2c_get_clientdata(client); > - struct imx335 *imx335 = to_imx335(sd); > > v4l2_async_unregister_subdev(sd); > + v4l2_subdev_cleanup(sd); > media_entity_cleanup(&sd->entity); > v4l2_ctrl_handler_free(sd->ctrl_handler); > > @@ -1437,8 +1402,6 @@ static void imx335_remove(struct i2c_client *client) > if (!pm_runtime_status_suspended(&client->dev)) > imx335_power_off(&client->dev); > pm_runtime_set_suspended(&client->dev); > - > - mutex_destroy(&imx335->mutex); > } > > static const struct dev_pm_ops imx335_pm_ops = { > > -- > 2.51.0 > ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 7/8] media: imx335: Support 2x2 binning 2025-09-11 8:14 [PATCH v2 0/8] media: imx335: Vflip, active state and binning support Jai Luthra ` (5 preceding siblings ...) 2025-09-11 8:14 ` [PATCH v2 6/8] media: imx355: Use subdev active state Jai Luthra @ 2025-09-11 8:14 ` Jai Luthra 2025-09-11 11:51 ` Kieran Bingham 2025-09-11 8:14 ` [PATCH v2 8/8] media: imx335: Switch to {enable,disable}_streams Jai Luthra 7 siblings, 1 reply; 14+ messages in thread From: Jai Luthra @ 2025-09-11 8:14 UTC (permalink / raw) To: Kieran Bingham, Sakari Ailus, Mauro Carvalho Chehab Cc: linux-media, linux-kernel, Tommaso Merciai Introduce 2x2 binning mode (1312x972@60fps). Since there are multiple modes now, use v4l2_find_nearest_size() to select the appropriate mode in .set_fmt(). For 2x2 binning the minimum shutter value supported is 17 instead of 9. This effects the maximum allowed exposure time, and if not enforced then the sensor produces very dark frames when the minimum shutter limit is violated. Lastly, update the crop size reported to the userspace. When trying 2x2 binning with the datasheet suggested pixel array size (i.e. 2592 / 2 => 1296), on some platforms (Raspberry Pi 5) artefacts are introduced on the right edge of the output image. Instead, using a higher width of 1312 works fine on all platforms. Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> --- drivers/media/i2c/imx335.c | 275 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 217 insertions(+), 58 deletions(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 9b9bbe6473d545bc5618c0fe8191df0b18037cd8..c985edab7e2a965ccd9427b013cbb3fa8778de2b 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -35,6 +35,7 @@ /* Lines per frame */ #define IMX335_REG_VMAX CCI_REG24_LE(0x3030) +#define IMX335_REG_HMAX CCI_REG16_LE(0x3034) #define IMX335_REG_OPB_SIZE_V CCI_REG8(0x304c) #define IMX335_REG_ADBIT CCI_REG8(0x3050) @@ -42,10 +43,13 @@ #define IMX335_REG_SHUTTER CCI_REG24_LE(0x3058) #define IMX335_EXPOSURE_MIN 1 -#define IMX335_EXPOSURE_OFFSET 9 +#define IMX335_SHUTTER_MIN 9 +#define IMX335_SHUTTER_MIN_BINNED 17 #define IMX335_EXPOSURE_STEP 1 #define IMX335_EXPOSURE_DEFAULT 0x0648 +#define IMX335_REG_AREA2_WIDTH_1 CCI_REG16_LE(0x3072) + #define IMX335_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074) #define IMX335_REG_AREA3_WIDTH_1 CCI_REG16_LE(0x3076) @@ -133,12 +137,11 @@ static const struct v4l2_rect imx335_native_area = { static const struct v4l2_rect imx335_active_area = { .top = 50, - .left = 52, - .width = 2592, + .left = 36, + .width = 2624, .height = 1944, }; - /** * struct imx335_reg_list - imx335 sensor register list * @num_of_regs: Number of registers in the list @@ -155,8 +158,14 @@ static const char * const imx335_supply_name[] = { "dvdd", /* Digital Core (1.2V) supply */ }; +enum imx335_scan_mode { + IMX335_ALL_PIXEL, + IMX335_2_2_BINNING, +}; + /** * struct imx335_mode - imx335 sensor mode structure + * @scan_mode: Configuration scan mode (All pixel / 2x2Binning) * @width: Frame width * @height: Frame height * @code: Format code @@ -170,6 +179,7 @@ static const char * const imx335_supply_name[] = { * @vflip_inverted: Register list vflip (inverted readout) */ struct imx335_mode { + enum imx335_scan_mode scan_mode; u32 width; u32 height; u32 code; @@ -271,12 +281,33 @@ static const struct cci_reg_sequence mode_2592x1944_regs[] = { { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY }, { IMX335_REG_MASTER_MODE, 0x00 }, { IMX335_REG_WINMODE, 0x04 }, + { IMX335_REG_HMAX, 550 }, { IMX335_REG_HTRIMMING_START, 48 }, { IMX335_REG_HNUM, 2592 }, { IMX335_REG_Y_OUT_SIZE, 1944 }, + { IMX335_REG_AREA2_WIDTH_1, 40 }, { IMX335_REG_AREA3_WIDTH_1, 3928 }, { IMX335_REG_OPB_SIZE_V, 0 }, { IMX335_REG_XVS_XHS_DRV, 0x00 }, +}; + +static const struct cci_reg_sequence mode_1312x972_regs[] = { + { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY }, + { IMX335_REG_MASTER_MODE, 0x00 }, + { IMX335_REG_WINMODE, 0x01 }, + { IMX335_REG_HMAX, 275 }, + { IMX335_REG_HTRIMMING_START, 48 }, + { IMX335_REG_HNUM, 2600 }, + { IMX335_REG_Y_OUT_SIZE, 972 }, + { IMX335_REG_AREA2_WIDTH_1, 48 }, + { IMX335_REG_AREA3_WIDTH_1, 3936 }, + { IMX335_REG_OPB_SIZE_V, 0 }, + { IMX335_REG_XVS_XHS_DRV, 0x00 }, + { CCI_REG8(0x3300), 1 }, /* TCYCLE */ + { CCI_REG8(0x3199), 0x30 }, /* HADD/VADD */ +}; + +static const struct cci_reg_sequence imx335_common_regs[] = { { CCI_REG8(0x3288), 0x21 }, { CCI_REG8(0x328a), 0x02 }, { CCI_REG8(0x3414), 0x05 }, @@ -367,16 +398,72 @@ static const struct cci_reg_sequence mode_2592x1944_vflip_inverted[] = { { CCI_REG16_LE(0x3116), 0x002 }, }; -static const struct cci_reg_sequence raw10_framefmt_regs[] = { - { IMX335_REG_ADBIT, 0x00 }, - { IMX335_REG_MDBIT, 0x00 }, - { IMX335_REG_ADBIT1, 0x1ff }, +static const struct cci_reg_sequence mode_1312x972_vflip_normal[] = { + { IMX335_REG_AREA3_ST_ADR_1, 176 }, + + /* Undocumented */ + { CCI_REG8(0x3078), 0x04 }, + { CCI_REG8(0x3079), 0xfd }, + { CCI_REG8(0x307a), 0x04 }, + { CCI_REG8(0x307b), 0xfe }, + { CCI_REG8(0x307c), 0x04 }, + { CCI_REG8(0x307d), 0xfb }, + { CCI_REG8(0x307e), 0x04 }, + { CCI_REG8(0x307f), 0x02 }, + { CCI_REG8(0x3080), 0x04, }, + { CCI_REG8(0x3081), 0xfd, }, + { CCI_REG8(0x3082), 0x04, }, + { CCI_REG8(0x3083), 0xfe, }, + { CCI_REG8(0x3084), 0x04, }, + { CCI_REG8(0x3085), 0xfb, }, + { CCI_REG8(0x3086), 0x04, }, + { CCI_REG8(0x3087), 0x02, }, + { CCI_REG8(0x30a4), 0x77, }, + { CCI_REG8(0x30a8), 0x20, }, + { CCI_REG8(0x30a9), 0x00, }, + { CCI_REG8(0x30ac), 0x08, }, + { CCI_REG8(0x30ad), 0x08, }, + { CCI_REG8(0x30b0), 0x20, }, + { CCI_REG8(0x30b1), 0x00, }, + { CCI_REG8(0x30b4), 0x10, }, + { CCI_REG8(0x30b5), 0x10, }, + { CCI_REG16_LE(0x30b6), 0x00 }, + { CCI_REG16_LE(0x3112), 0x10 }, + { CCI_REG16_LE(0x3116), 0x10 }, }; -static const struct cci_reg_sequence raw12_framefmt_regs[] = { - { IMX335_REG_ADBIT, 0x01 }, - { IMX335_REG_MDBIT, 0x01 }, - { IMX335_REG_ADBIT1, 0x47 }, +static const struct cci_reg_sequence mode_1312x972_vflip_inverted[] = { + { IMX335_REG_AREA3_ST_ADR_1, 4112 }, + + /* Undocumented */ + { CCI_REG8(0x3078), 0x04 }, + { CCI_REG8(0x3079), 0xfd }, + { CCI_REG8(0x307a), 0x04 }, + { CCI_REG8(0x307b), 0xfe }, + { CCI_REG8(0x307c), 0x04 }, + { CCI_REG8(0x307d), 0xfb }, + { CCI_REG8(0x307e), 0x04 }, + { CCI_REG8(0x307f), 0x02 }, + { CCI_REG8(0x3080), 0xfc, }, + { CCI_REG8(0x3081), 0x05, }, + { CCI_REG8(0x3082), 0xfc, }, + { CCI_REG8(0x3083), 0x02, }, + { CCI_REG8(0x3084), 0xfc, }, + { CCI_REG8(0x3085), 0x03, }, + { CCI_REG8(0x3086), 0xfc, }, + { CCI_REG8(0x3087), 0xfe, }, + { CCI_REG8(0x30a4), 0x77, }, + { CCI_REG8(0x30a8), 0x20, }, + { CCI_REG8(0x30a9), 0x00, }, + { CCI_REG8(0x30ac), 0x08, }, + { CCI_REG8(0x30ad), 0x78, }, + { CCI_REG8(0x30b0), 0x20, }, + { CCI_REG8(0x30b1), 0x00, }, + { CCI_REG8(0x30b4), 0x10, }, + { CCI_REG8(0x30b5), 0x70, }, + { CCI_REG16_LE(0x30b6), 0x01f2 }, + { CCI_REG16_LE(0x3112), 0x10 }, + { CCI_REG16_LE(0x3116), 0x02 }, }; static const struct cci_reg_sequence mipi_data_rate_1188Mbps[] = { @@ -441,25 +528,49 @@ static const u32 imx335_mbus_codes[] = { }; /* Supported sensor mode configurations */ -static const struct imx335_mode supported_mode = { - .width = 2592, - .height = 1944, - .hblank = 342, - .vblank = 2556, - .vblank_min = 2556, - .vblank_max = 133060, - .pclk = 396000000, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), - .regs = mode_2592x1944_regs, - }, - .vflip_normal = { - .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_normal), - .regs = mode_2592x1944_vflip_normal, - }, - .vflip_inverted = { - .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_inverted), - .regs = mode_2592x1944_vflip_inverted, +static const struct imx335_mode supported_modes[] = { + { + .scan_mode = IMX335_ALL_PIXEL, + .width = 2592, + .height = 1944, + .hblank = 342, + .vblank = 2556, + .vblank_min = 2556, + .vblank_max = 133060, + .pclk = 396000000, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), + .regs = mode_2592x1944_regs, + }, + .vflip_normal = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_normal), + .regs = mode_2592x1944_vflip_normal, + }, + .vflip_inverted = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_inverted), + .regs = mode_2592x1944_vflip_inverted, + } + }, { + .scan_mode = IMX335_2_2_BINNING, + .width = 1312, + .height = 972, + .hblank = 155, + .vblank = 3528, + .vblank_min = 3528, + .vblank_max = 133060, + .pclk = 396000000, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1312x972_regs), + .regs = mode_1312x972_regs, + }, + .vflip_normal = { + .num_of_regs = ARRAY_SIZE(mode_1312x972_vflip_normal), + .regs = mode_1312x972_vflip_normal, + }, + .vflip_inverted = { + .num_of_regs = ARRAY_SIZE(mode_1312x972_vflip_inverted), + .regs = mode_1312x972_vflip_inverted, + }, }, }; @@ -616,18 +727,22 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) /* Propagate change of current control to all related controls */ if (ctrl->id == V4L2_CID_VBLANK) { + u32 shutter_min = IMX335_SHUTTER_MIN; + u32 lpfr; + imx335->vblank = imx335->vblank_ctrl->val; + lpfr = imx335->vblank + imx335->cur_mode->height; dev_dbg(imx335->dev, "Received vblank %u, new lpfr %u\n", - imx335->vblank, - imx335->vblank + imx335->cur_mode->height); + imx335->vblank, lpfr); + + if (imx335->cur_mode->scan_mode == IMX335_2_2_BINNING) + shutter_min = IMX335_SHUTTER_MIN_BINNED; ret = __v4l2_ctrl_modify_range(imx335->exp_ctrl, IMX335_EXPOSURE_MIN, - imx335->vblank + - imx335->cur_mode->height - - IMX335_EXPOSURE_OFFSET, - 1, IMX335_EXPOSURE_DEFAULT); + lpfr - shutter_min, 1, + IMX335_EXPOSURE_DEFAULT); if (ret) return ret; } @@ -727,17 +842,16 @@ static int imx335_enum_frame_size(struct v4l2_subdev *sd, struct imx335 *imx335 = to_imx335(sd); u32 code; - /* Only a single supported_mode available. */ - if (fsize->index > 0) + if (fsize->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; code = imx335_get_format_code(imx335, fsize->code); if (fsize->code != code) return -EINVAL; - fsize->min_width = supported_mode.width; + fsize->min_width = supported_modes[fsize->index].width; fsize->max_width = fsize->min_width; - fsize->min_height = supported_mode.height; + fsize->min_height = supported_modes[fsize->index].height; fsize->max_height = fsize->min_height; return 0; @@ -779,9 +893,13 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, struct imx335 *imx335 = to_imx335(sd); struct v4l2_mbus_framefmt *format; const struct imx335_mode *mode; + struct v4l2_rect *crop; int i, ret = 0; - mode = &supported_mode; + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); for (i = 0; i < ARRAY_SIZE(imx335_mbus_codes); i++) { if (imx335_mbus_codes[i] == fmt->format.code) @@ -793,6 +911,16 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, format = v4l2_subdev_state_get_format(sd_state, fmt->pad); *format = fmt->format; + crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad); + crop->width = fmt->format.width; + crop->height = fmt->format.height; + if (mode->scan_mode == IMX335_2_2_BINNING) { + crop->width *= 2; + crop->height *= 2; + } + crop->left = (imx335_native_area.width - crop->width) / 2; + crop->top = (imx335_native_area.height - crop->height) / 2; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ret = imx335_update_controls(imx335, mode); if (!ret) @@ -816,7 +944,7 @@ static int imx335_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_format fmt = { 0 }; fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - imx335_fill_pad_format(imx335, &supported_mode, &fmt); + imx335_fill_pad_format(imx335, &supported_modes[0], &fmt); __v4l2_ctrl_modify_range(imx335->link_freq_ctrl, 0, __fls(imx335->link_freq_bitmap), @@ -839,11 +967,15 @@ static int imx335_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); + + return 0; + case V4L2_SEL_TGT_NATIVE_SIZE: sel->r = imx335_native_area; return 0; - case V4L2_SEL_TGT_CROP: case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: sel->r = imx335_active_area; @@ -855,19 +987,35 @@ static int imx335_get_selection(struct v4l2_subdev *sd, static int imx335_set_framefmt(struct imx335 *imx335) { - switch (imx335->cur_mbus_code) { - case MEDIA_BUS_FMT_SRGGB10_1X10: - return cci_multi_reg_write(imx335->cci, raw10_framefmt_regs, - ARRAY_SIZE(raw10_framefmt_regs), - NULL); - - case MEDIA_BUS_FMT_SRGGB12_1X12: - return cci_multi_reg_write(imx335->cci, raw12_framefmt_regs, - ARRAY_SIZE(raw12_framefmt_regs), - NULL); + /* + * In the all-pixel scan mode the AD conversion shall match the output + * bit width requested. + * + * However, when 2/2 binning is enabled, the AD conversion is always + * 10-bit, so we ensure ADBIT is clear and ADBIT1 is assigned 0x1ff. + * That's as much as the documentation gives us... + */ + int ret = 0; + u8 bpp = imx335->cur_mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10 ? 10 : 12; + u8 ad_conv = bpp; + + /* Start with the output mode */ + cci_write(imx335->cci, IMX335_REG_MDBIT, bpp == 12, &ret); + + /* Enforce 10 bit AD on binning modes */ + if (imx335->cur_mode->scan_mode == IMX335_2_2_BINNING) + ad_conv = 10; + + /* AD Conversion configuration */ + if (ad_conv == 10) { + cci_write(imx335->cci, IMX335_REG_ADBIT, 0x00, &ret); + cci_write(imx335->cci, IMX335_REG_ADBIT1, 0x1ff, &ret); + } else { /* 12 bit AD Conversion */ + cci_write(imx335->cci, IMX335_REG_ADBIT, 0x01, &ret); + cci_write(imx335->cci, IMX335_REG_ADBIT1, 0x47, &ret); } - return -EINVAL; + return ret; } /** @@ -903,6 +1051,14 @@ static int imx335_start_streaming(struct imx335 *imx335) goto err_rpm_put; } + /* Write sensor common registers */ + ret = cci_multi_reg_write(imx335->cci, imx335_common_regs, + ARRAY_SIZE(imx335_common_regs), NULL); + if (ret) { + dev_err(imx335->dev, "fail to write initial registers\n"); + goto err_rpm_put; + } + ret = imx335_set_framefmt(imx335); if (ret) { dev_err(imx335->dev, "%s failed to set frame format: %d\n", @@ -1184,7 +1340,7 @@ static int imx335_init_controls(struct imx335 *imx335) struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler; const struct imx335_mode *mode = imx335->cur_mode; struct v4l2_fwnode_device_properties props; - u32 lpfr; + u32 lpfr, shutter_min; int ret; ret = v4l2_fwnode_device_parse(imx335->dev, &props); @@ -1198,11 +1354,14 @@ static int imx335_init_controls(struct imx335 *imx335) /* Initialize exposure and gain */ lpfr = mode->vblank + mode->height; + shutter_min = IMX335_SHUTTER_MIN; + if (mode->scan_mode == IMX335_2_2_BINNING) + shutter_min = IMX335_SHUTTER_MIN_BINNED; imx335->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &imx335_ctrl_ops, V4L2_CID_EXPOSURE, IMX335_EXPOSURE_MIN, - lpfr - IMX335_EXPOSURE_OFFSET, + lpfr - shutter_min, IMX335_EXPOSURE_STEP, IMX335_EXPOSURE_DEFAULT); @@ -1329,7 +1488,7 @@ static int imx335_probe(struct i2c_client *client) } /* Set default mode to max resolution */ - imx335->cur_mode = &supported_mode; + imx335->cur_mode = &supported_modes[0]; imx335->cur_mbus_code = imx335_mbus_codes[0]; imx335->vblank = imx335->cur_mode->vblank; -- 2.51.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 7/8] media: imx335: Support 2x2 binning 2025-09-11 8:14 ` [PATCH v2 7/8] media: imx335: Support 2x2 binning Jai Luthra @ 2025-09-11 11:51 ` Kieran Bingham 0 siblings, 0 replies; 14+ messages in thread From: Kieran Bingham @ 2025-09-11 11:51 UTC (permalink / raw) To: Jai Luthra, Mauro Carvalho Chehab, Sakari Ailus Cc: linux-media, linux-kernel, Tommaso Merciai Quoting Jai Luthra (2025-09-11 09:14:23) > Introduce 2x2 binning mode (1312x972@60fps). Since there are multiple > modes now, use v4l2_find_nearest_size() to select the appropriate mode > in .set_fmt(). > > For 2x2 binning the minimum shutter value supported is 17 instead of 9. > This effects the maximum allowed exposure time, and if not enforced then > the sensor produces very dark frames when the minimum shutter limit is > violated. > > Lastly, update the crop size reported to the userspace. When trying 2x2 > binning with the datasheet suggested pixel array size (i.e. 2592 / 2 => > 1296), on some platforms (Raspberry Pi 5) artefacts are introduced on > the right edge of the output image. Instead, using a higher width of > 1312 works fine on all platforms. > > Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> > --- > drivers/media/i2c/imx335.c | 275 +++++++++++++++++++++++++++++++++++---------- > 1 file changed, 217 insertions(+), 58 deletions(-) > > diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c > index 9b9bbe6473d545bc5618c0fe8191df0b18037cd8..c985edab7e2a965ccd9427b013cbb3fa8778de2b 100644 > --- a/drivers/media/i2c/imx335.c > +++ b/drivers/media/i2c/imx335.c > @@ -35,6 +35,7 @@ > > /* Lines per frame */ > #define IMX335_REG_VMAX CCI_REG24_LE(0x3030) > +#define IMX335_REG_HMAX CCI_REG16_LE(0x3034) > > #define IMX335_REG_OPB_SIZE_V CCI_REG8(0x304c) > #define IMX335_REG_ADBIT CCI_REG8(0x3050) > @@ -42,10 +43,13 @@ > > #define IMX335_REG_SHUTTER CCI_REG24_LE(0x3058) > #define IMX335_EXPOSURE_MIN 1 > -#define IMX335_EXPOSURE_OFFSET 9 > +#define IMX335_SHUTTER_MIN 9 > +#define IMX335_SHUTTER_MIN_BINNED 17 > #define IMX335_EXPOSURE_STEP 1 > #define IMX335_EXPOSURE_DEFAULT 0x0648 > > +#define IMX335_REG_AREA2_WIDTH_1 CCI_REG16_LE(0x3072) > + > #define IMX335_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074) > #define IMX335_REG_AREA3_WIDTH_1 CCI_REG16_LE(0x3076) > > @@ -133,12 +137,11 @@ static const struct v4l2_rect imx335_native_area = { > > static const struct v4l2_rect imx335_active_area = { > .top = 50, > - .left = 52, > - .width = 2592, > + .left = 36, > + .width = 2624, > .height = 1944, > }; > > - > /** > * struct imx335_reg_list - imx335 sensor register list > * @num_of_regs: Number of registers in the list > @@ -155,8 +158,14 @@ static const char * const imx335_supply_name[] = { > "dvdd", /* Digital Core (1.2V) supply */ > }; > > +enum imx335_scan_mode { > + IMX335_ALL_PIXEL, > + IMX335_2_2_BINNING, > +}; > + > /** > * struct imx335_mode - imx335 sensor mode structure > + * @scan_mode: Configuration scan mode (All pixel / 2x2Binning) > * @width: Frame width > * @height: Frame height > * @code: Format code > @@ -170,6 +179,7 @@ static const char * const imx335_supply_name[] = { > * @vflip_inverted: Register list vflip (inverted readout) > */ > struct imx335_mode { > + enum imx335_scan_mode scan_mode; > u32 width; > u32 height; > u32 code; > @@ -271,12 +281,33 @@ static const struct cci_reg_sequence mode_2592x1944_regs[] = { > { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY }, > { IMX335_REG_MASTER_MODE, 0x00 }, > { IMX335_REG_WINMODE, 0x04 }, > + { IMX335_REG_HMAX, 550 }, > { IMX335_REG_HTRIMMING_START, 48 }, > { IMX335_REG_HNUM, 2592 }, > { IMX335_REG_Y_OUT_SIZE, 1944 }, > + { IMX335_REG_AREA2_WIDTH_1, 40 }, > { IMX335_REG_AREA3_WIDTH_1, 3928 }, > { IMX335_REG_OPB_SIZE_V, 0 }, > { IMX335_REG_XVS_XHS_DRV, 0x00 }, > +}; > + > +static const struct cci_reg_sequence mode_1312x972_regs[] = { > + { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY }, > + { IMX335_REG_MASTER_MODE, 0x00 }, > + { IMX335_REG_WINMODE, 0x01 }, > + { IMX335_REG_HMAX, 275 }, > + { IMX335_REG_HTRIMMING_START, 48 }, > + { IMX335_REG_HNUM, 2600 }, > + { IMX335_REG_Y_OUT_SIZE, 972 }, > + { IMX335_REG_AREA2_WIDTH_1, 48 }, > + { IMX335_REG_AREA3_WIDTH_1, 3936 }, > + { IMX335_REG_OPB_SIZE_V, 0 }, > + { IMX335_REG_XVS_XHS_DRV, 0x00 }, > + { CCI_REG8(0x3300), 1 }, /* TCYCLE */ > + { CCI_REG8(0x3199), 0x30 }, /* HADD/VADD */ > +}; > + > +static const struct cci_reg_sequence imx335_common_regs[] = { > { CCI_REG8(0x3288), 0x21 }, > { CCI_REG8(0x328a), 0x02 }, > { CCI_REG8(0x3414), 0x05 }, > @@ -367,16 +398,72 @@ static const struct cci_reg_sequence mode_2592x1944_vflip_inverted[] = { > { CCI_REG16_LE(0x3116), 0x002 }, > }; > > -static const struct cci_reg_sequence raw10_framefmt_regs[] = { > - { IMX335_REG_ADBIT, 0x00 }, > - { IMX335_REG_MDBIT, 0x00 }, > - { IMX335_REG_ADBIT1, 0x1ff }, > +static const struct cci_reg_sequence mode_1312x972_vflip_normal[] = { > + { IMX335_REG_AREA3_ST_ADR_1, 176 }, > + > + /* Undocumented */ > + { CCI_REG8(0x3078), 0x04 }, > + { CCI_REG8(0x3079), 0xfd }, > + { CCI_REG8(0x307a), 0x04 }, > + { CCI_REG8(0x307b), 0xfe }, > + { CCI_REG8(0x307c), 0x04 }, > + { CCI_REG8(0x307d), 0xfb }, > + { CCI_REG8(0x307e), 0x04 }, > + { CCI_REG8(0x307f), 0x02 }, > + { CCI_REG8(0x3080), 0x04, }, > + { CCI_REG8(0x3081), 0xfd, }, > + { CCI_REG8(0x3082), 0x04, }, > + { CCI_REG8(0x3083), 0xfe, }, > + { CCI_REG8(0x3084), 0x04, }, > + { CCI_REG8(0x3085), 0xfb, }, > + { CCI_REG8(0x3086), 0x04, }, > + { CCI_REG8(0x3087), 0x02, }, > + { CCI_REG8(0x30a4), 0x77, }, > + { CCI_REG8(0x30a8), 0x20, }, > + { CCI_REG8(0x30a9), 0x00, }, > + { CCI_REG8(0x30ac), 0x08, }, > + { CCI_REG8(0x30ad), 0x08, }, > + { CCI_REG8(0x30b0), 0x20, }, > + { CCI_REG8(0x30b1), 0x00, }, > + { CCI_REG8(0x30b4), 0x10, }, > + { CCI_REG8(0x30b5), 0x10, }, Something odd happened to that table, I don't think the extra ',' should be at the end of these entries (0x3080->0x030b5). > + { CCI_REG16_LE(0x30b6), 0x00 }, > + { CCI_REG16_LE(0x3112), 0x10 }, > + { CCI_REG16_LE(0x3116), 0x10 }, > }; > > -static const struct cci_reg_sequence raw12_framefmt_regs[] = { > - { IMX335_REG_ADBIT, 0x01 }, > - { IMX335_REG_MDBIT, 0x01 }, > - { IMX335_REG_ADBIT1, 0x47 }, > +static const struct cci_reg_sequence mode_1312x972_vflip_inverted[] = { > + { IMX335_REG_AREA3_ST_ADR_1, 4112 }, > + > + /* Undocumented */ > + { CCI_REG8(0x3078), 0x04 }, > + { CCI_REG8(0x3079), 0xfd }, > + { CCI_REG8(0x307a), 0x04 }, > + { CCI_REG8(0x307b), 0xfe }, > + { CCI_REG8(0x307c), 0x04 }, > + { CCI_REG8(0x307d), 0xfb }, > + { CCI_REG8(0x307e), 0x04 }, > + { CCI_REG8(0x307f), 0x02 }, > + { CCI_REG8(0x3080), 0xfc, }, > + { CCI_REG8(0x3081), 0x05, }, > + { CCI_REG8(0x3082), 0xfc, }, > + { CCI_REG8(0x3083), 0x02, }, > + { CCI_REG8(0x3084), 0xfc, }, > + { CCI_REG8(0x3085), 0x03, }, > + { CCI_REG8(0x3086), 0xfc, }, > + { CCI_REG8(0x3087), 0xfe, }, > + { CCI_REG8(0x30a4), 0x77, }, > + { CCI_REG8(0x30a8), 0x20, }, > + { CCI_REG8(0x30a9), 0x00, }, > + { CCI_REG8(0x30ac), 0x08, }, > + { CCI_REG8(0x30ad), 0x78, }, > + { CCI_REG8(0x30b0), 0x20, }, > + { CCI_REG8(0x30b1), 0x00, }, > + { CCI_REG8(0x30b4), 0x10, }, > + { CCI_REG8(0x30b5), 0x70, }, Same here ... Other than that Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > + { CCI_REG16_LE(0x30b6), 0x01f2 }, > + { CCI_REG16_LE(0x3112), 0x10 }, > + { CCI_REG16_LE(0x3116), 0x02 }, > }; > > static const struct cci_reg_sequence mipi_data_rate_1188Mbps[] = { > @@ -441,25 +528,49 @@ static const u32 imx335_mbus_codes[] = { > }; > > /* Supported sensor mode configurations */ > -static const struct imx335_mode supported_mode = { > - .width = 2592, > - .height = 1944, > - .hblank = 342, > - .vblank = 2556, > - .vblank_min = 2556, > - .vblank_max = 133060, > - .pclk = 396000000, > - .reg_list = { > - .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), > - .regs = mode_2592x1944_regs, > - }, > - .vflip_normal = { > - .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_normal), > - .regs = mode_2592x1944_vflip_normal, > - }, > - .vflip_inverted = { > - .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_inverted), > - .regs = mode_2592x1944_vflip_inverted, > +static const struct imx335_mode supported_modes[] = { > + { > + .scan_mode = IMX335_ALL_PIXEL, > + .width = 2592, > + .height = 1944, > + .hblank = 342, > + .vblank = 2556, > + .vblank_min = 2556, > + .vblank_max = 133060, > + .pclk = 396000000, > + .reg_list = { > + .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), > + .regs = mode_2592x1944_regs, > + }, > + .vflip_normal = { > + .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_normal), > + .regs = mode_2592x1944_vflip_normal, > + }, > + .vflip_inverted = { > + .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_inverted), > + .regs = mode_2592x1944_vflip_inverted, > + } > + }, { > + .scan_mode = IMX335_2_2_BINNING, > + .width = 1312, > + .height = 972, > + .hblank = 155, > + .vblank = 3528, > + .vblank_min = 3528, > + .vblank_max = 133060, > + .pclk = 396000000, > + .reg_list = { > + .num_of_regs = ARRAY_SIZE(mode_1312x972_regs), > + .regs = mode_1312x972_regs, > + }, > + .vflip_normal = { > + .num_of_regs = ARRAY_SIZE(mode_1312x972_vflip_normal), > + .regs = mode_1312x972_vflip_normal, > + }, > + .vflip_inverted = { > + .num_of_regs = ARRAY_SIZE(mode_1312x972_vflip_inverted), > + .regs = mode_1312x972_vflip_inverted, > + }, > }, > }; > > @@ -616,18 +727,22 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) > > /* Propagate change of current control to all related controls */ > if (ctrl->id == V4L2_CID_VBLANK) { > + u32 shutter_min = IMX335_SHUTTER_MIN; > + u32 lpfr; > + > imx335->vblank = imx335->vblank_ctrl->val; > + lpfr = imx335->vblank + imx335->cur_mode->height; > > dev_dbg(imx335->dev, "Received vblank %u, new lpfr %u\n", > - imx335->vblank, > - imx335->vblank + imx335->cur_mode->height); > + imx335->vblank, lpfr); > + > + if (imx335->cur_mode->scan_mode == IMX335_2_2_BINNING) > + shutter_min = IMX335_SHUTTER_MIN_BINNED; > > ret = __v4l2_ctrl_modify_range(imx335->exp_ctrl, > IMX335_EXPOSURE_MIN, > - imx335->vblank + > - imx335->cur_mode->height - > - IMX335_EXPOSURE_OFFSET, > - 1, IMX335_EXPOSURE_DEFAULT); > + lpfr - shutter_min, 1, > + IMX335_EXPOSURE_DEFAULT); > if (ret) > return ret; > } > @@ -727,17 +842,16 @@ static int imx335_enum_frame_size(struct v4l2_subdev *sd, > struct imx335 *imx335 = to_imx335(sd); > u32 code; > > - /* Only a single supported_mode available. */ > - if (fsize->index > 0) > + if (fsize->index >= ARRAY_SIZE(supported_modes)) > return -EINVAL; > > code = imx335_get_format_code(imx335, fsize->code); > if (fsize->code != code) > return -EINVAL; > > - fsize->min_width = supported_mode.width; > + fsize->min_width = supported_modes[fsize->index].width; > fsize->max_width = fsize->min_width; > - fsize->min_height = supported_mode.height; > + fsize->min_height = supported_modes[fsize->index].height; > fsize->max_height = fsize->min_height; > > return 0; > @@ -779,9 +893,13 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, > struct imx335 *imx335 = to_imx335(sd); > struct v4l2_mbus_framefmt *format; > const struct imx335_mode *mode; > + struct v4l2_rect *crop; > int i, ret = 0; > > - mode = &supported_mode; > + mode = v4l2_find_nearest_size(supported_modes, > + ARRAY_SIZE(supported_modes), > + width, height, > + fmt->format.width, fmt->format.height); > > for (i = 0; i < ARRAY_SIZE(imx335_mbus_codes); i++) { > if (imx335_mbus_codes[i] == fmt->format.code) > @@ -793,6 +911,16 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, > format = v4l2_subdev_state_get_format(sd_state, fmt->pad); > *format = fmt->format; > > + crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad); > + crop->width = fmt->format.width; > + crop->height = fmt->format.height; > + if (mode->scan_mode == IMX335_2_2_BINNING) { > + crop->width *= 2; > + crop->height *= 2; > + } > + crop->left = (imx335_native_area.width - crop->width) / 2; > + crop->top = (imx335_native_area.height - crop->height) / 2; > + > if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { > ret = imx335_update_controls(imx335, mode); > if (!ret) > @@ -816,7 +944,7 @@ static int imx335_init_state(struct v4l2_subdev *sd, > struct v4l2_subdev_format fmt = { 0 }; > > fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; > - imx335_fill_pad_format(imx335, &supported_mode, &fmt); > + imx335_fill_pad_format(imx335, &supported_modes[0], &fmt); > > __v4l2_ctrl_modify_range(imx335->link_freq_ctrl, 0, > __fls(imx335->link_freq_bitmap), > @@ -839,11 +967,15 @@ static int imx335_get_selection(struct v4l2_subdev *sd, > struct v4l2_subdev_selection *sel) > { > switch (sel->target) { > + case V4L2_SEL_TGT_CROP: > + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); > + > + return 0; > + > case V4L2_SEL_TGT_NATIVE_SIZE: > sel->r = imx335_native_area; > return 0; > > - case V4L2_SEL_TGT_CROP: > case V4L2_SEL_TGT_CROP_DEFAULT: > case V4L2_SEL_TGT_CROP_BOUNDS: > sel->r = imx335_active_area; > @@ -855,19 +987,35 @@ static int imx335_get_selection(struct v4l2_subdev *sd, > > static int imx335_set_framefmt(struct imx335 *imx335) > { > - switch (imx335->cur_mbus_code) { > - case MEDIA_BUS_FMT_SRGGB10_1X10: > - return cci_multi_reg_write(imx335->cci, raw10_framefmt_regs, > - ARRAY_SIZE(raw10_framefmt_regs), > - NULL); > - > - case MEDIA_BUS_FMT_SRGGB12_1X12: > - return cci_multi_reg_write(imx335->cci, raw12_framefmt_regs, > - ARRAY_SIZE(raw12_framefmt_regs), > - NULL); > + /* > + * In the all-pixel scan mode the AD conversion shall match the output > + * bit width requested. > + * > + * However, when 2/2 binning is enabled, the AD conversion is always > + * 10-bit, so we ensure ADBIT is clear and ADBIT1 is assigned 0x1ff. > + * That's as much as the documentation gives us... > + */ > + int ret = 0; > + u8 bpp = imx335->cur_mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10 ? 10 : 12; > + u8 ad_conv = bpp; > + > + /* Start with the output mode */ > + cci_write(imx335->cci, IMX335_REG_MDBIT, bpp == 12, &ret); > + > + /* Enforce 10 bit AD on binning modes */ > + if (imx335->cur_mode->scan_mode == IMX335_2_2_BINNING) > + ad_conv = 10; > + > + /* AD Conversion configuration */ > + if (ad_conv == 10) { > + cci_write(imx335->cci, IMX335_REG_ADBIT, 0x00, &ret); > + cci_write(imx335->cci, IMX335_REG_ADBIT1, 0x1ff, &ret); > + } else { /* 12 bit AD Conversion */ > + cci_write(imx335->cci, IMX335_REG_ADBIT, 0x01, &ret); > + cci_write(imx335->cci, IMX335_REG_ADBIT1, 0x47, &ret); > } > > - return -EINVAL; > + return ret; > } > > /** > @@ -903,6 +1051,14 @@ static int imx335_start_streaming(struct imx335 *imx335) > goto err_rpm_put; > } > > + /* Write sensor common registers */ > + ret = cci_multi_reg_write(imx335->cci, imx335_common_regs, > + ARRAY_SIZE(imx335_common_regs), NULL); > + if (ret) { > + dev_err(imx335->dev, "fail to write initial registers\n"); > + goto err_rpm_put; > + } > + > ret = imx335_set_framefmt(imx335); > if (ret) { > dev_err(imx335->dev, "%s failed to set frame format: %d\n", > @@ -1184,7 +1340,7 @@ static int imx335_init_controls(struct imx335 *imx335) > struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler; > const struct imx335_mode *mode = imx335->cur_mode; > struct v4l2_fwnode_device_properties props; > - u32 lpfr; > + u32 lpfr, shutter_min; > int ret; > > ret = v4l2_fwnode_device_parse(imx335->dev, &props); > @@ -1198,11 +1354,14 @@ static int imx335_init_controls(struct imx335 *imx335) > > /* Initialize exposure and gain */ > lpfr = mode->vblank + mode->height; > + shutter_min = IMX335_SHUTTER_MIN; > + if (mode->scan_mode == IMX335_2_2_BINNING) > + shutter_min = IMX335_SHUTTER_MIN_BINNED; > imx335->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, > &imx335_ctrl_ops, > V4L2_CID_EXPOSURE, > IMX335_EXPOSURE_MIN, > - lpfr - IMX335_EXPOSURE_OFFSET, > + lpfr - shutter_min, > IMX335_EXPOSURE_STEP, > IMX335_EXPOSURE_DEFAULT); > > @@ -1329,7 +1488,7 @@ static int imx335_probe(struct i2c_client *client) > } > > /* Set default mode to max resolution */ > - imx335->cur_mode = &supported_mode; > + imx335->cur_mode = &supported_modes[0]; > imx335->cur_mbus_code = imx335_mbus_codes[0]; > imx335->vblank = imx335->cur_mode->vblank; > > > -- > 2.51.0 > ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 8/8] media: imx335: Switch to {enable,disable}_streams 2025-09-11 8:14 [PATCH v2 0/8] media: imx335: Vflip, active state and binning support Jai Luthra ` (6 preceding siblings ...) 2025-09-11 8:14 ` [PATCH v2 7/8] media: imx335: Support 2x2 binning Jai Luthra @ 2025-09-11 8:14 ` Jai Luthra 2025-09-11 11:46 ` Kieran Bingham 7 siblings, 1 reply; 14+ messages in thread From: Jai Luthra @ 2025-09-11 8:14 UTC (permalink / raw) To: Kieran Bingham, Sakari Ailus, Mauro Carvalho Chehab Cc: linux-media, linux-kernel, Tommaso Merciai Switch from s_stream to enable_streams and disable_streams callbacks. Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> --- drivers/media/i2c/imx335.c | 53 +++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index c985edab7e2a965ccd9427b013cbb3fa8778de2b..a1af70d050bca79255ac9303e8508de919bc4fdc 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -1019,13 +1019,19 @@ static int imx335_set_framefmt(struct imx335 *imx335) } /** - * imx335_start_streaming() - Start sensor stream - * @imx335: pointer to imx335 device + * imx335_enable_streams() - Enable sensor streams + * @sd: V4L2 subdevice + * @state: V4L2 subdevice state + * @pad: The pad to enable + * @streams_mask: Bitmask of streams to enable * * Return: 0 if successful, error code otherwise. */ -static int imx335_start_streaming(struct imx335 *imx335) +static int imx335_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct imx335 *imx335 = to_imx335(sd); const struct imx335_reg_list *reg_list; int ret; @@ -1099,37 +1105,24 @@ static int imx335_start_streaming(struct imx335 *imx335) } /** - * imx335_stop_streaming() - Stop sensor stream - * @imx335: pointer to imx335 device - */ -static void imx335_stop_streaming(struct imx335 *imx335) -{ - cci_write(imx335->cci, IMX335_REG_MODE_SELECT, - IMX335_MODE_STANDBY, NULL); - pm_runtime_put(imx335->dev); -} - -/** - * imx335_set_stream() - Enable sensor streaming - * @sd: pointer to imx335 subdevice - * @enable: set to enable sensor streaming + * imx335_disable_streams() - Disable sensor streams + * @sd: V4L2 subdevice + * @state: V4L2 subdevice state + * @pad: The pad to disable + * @streams_mask: Bitmask of streams to disable * * Return: 0 if successful, error code otherwise. */ -static int imx335_set_stream(struct v4l2_subdev *sd, int enable) +static int imx335_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx335 *imx335 = to_imx335(sd); - struct v4l2_subdev_state *state; - int ret = 0; - - state = v4l2_subdev_lock_and_get_active_state(sd); - - if (enable) - ret = imx335_start_streaming(imx335); - else - imx335_stop_streaming(imx335); + int ret; - v4l2_subdev_unlock_state(state); + ret = cci_write(imx335->cci, IMX335_REG_MODE_SELECT, + IMX335_MODE_STANDBY, NULL); + pm_runtime_put(imx335->dev); return ret; } @@ -1249,7 +1242,7 @@ static int imx335_parse_hw_config(struct imx335 *imx335) /* V4l2 subdevice ops */ static const struct v4l2_subdev_video_ops imx335_video_ops = { - .s_stream = imx335_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops imx335_pad_ops = { @@ -1259,6 +1252,8 @@ static const struct v4l2_subdev_pad_ops imx335_pad_ops = { .set_selection = imx335_get_selection, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx335_set_pad_format, + .enable_streams = imx335_enable_streams, + .disable_streams = imx335_disable_streams, }; static const struct v4l2_subdev_ops imx335_subdev_ops = { -- 2.51.0 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 8/8] media: imx335: Switch to {enable,disable}_streams 2025-09-11 8:14 ` [PATCH v2 8/8] media: imx335: Switch to {enable,disable}_streams Jai Luthra @ 2025-09-11 11:46 ` Kieran Bingham 0 siblings, 0 replies; 14+ messages in thread From: Kieran Bingham @ 2025-09-11 11:46 UTC (permalink / raw) To: Jai Luthra, Mauro Carvalho Chehab, Sakari Ailus Cc: linux-media, linux-kernel, Tommaso Merciai Quoting Jai Luthra (2025-09-11 09:14:24) > Switch from s_stream to enable_streams and disable_streams callbacks. > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> > --- > drivers/media/i2c/imx335.c | 53 +++++++++++++++++++++------------------------- > 1 file changed, 24 insertions(+), 29 deletions(-) > > diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c > index c985edab7e2a965ccd9427b013cbb3fa8778de2b..a1af70d050bca79255ac9303e8508de919bc4fdc 100644 > --- a/drivers/media/i2c/imx335.c > +++ b/drivers/media/i2c/imx335.c > @@ -1019,13 +1019,19 @@ static int imx335_set_framefmt(struct imx335 *imx335) > } > > /** > - * imx335_start_streaming() - Start sensor stream > - * @imx335: pointer to imx335 device > + * imx335_enable_streams() - Enable sensor streams > + * @sd: V4L2 subdevice > + * @state: V4L2 subdevice state > + * @pad: The pad to enable > + * @streams_mask: Bitmask of streams to enable > * > * Return: 0 if successful, error code otherwise. > */ > -static int imx335_start_streaming(struct imx335 *imx335) > +static int imx335_enable_streams(struct v4l2_subdev *sd, > + struct v4l2_subdev_state *state, u32 pad, > + u64 streams_mask) > { > + struct imx335 *imx335 = to_imx335(sd); > const struct imx335_reg_list *reg_list; > int ret; > > @@ -1099,37 +1105,24 @@ static int imx335_start_streaming(struct imx335 *imx335) > } > > /** > - * imx335_stop_streaming() - Stop sensor stream > - * @imx335: pointer to imx335 device > - */ > -static void imx335_stop_streaming(struct imx335 *imx335) > -{ > - cci_write(imx335->cci, IMX335_REG_MODE_SELECT, > - IMX335_MODE_STANDBY, NULL); > - pm_runtime_put(imx335->dev); > -} > - > -/** > - * imx335_set_stream() - Enable sensor streaming > - * @sd: pointer to imx335 subdevice > - * @enable: set to enable sensor streaming > + * imx335_disable_streams() - Disable sensor streams > + * @sd: V4L2 subdevice > + * @state: V4L2 subdevice state > + * @pad: The pad to disable > + * @streams_mask: Bitmask of streams to disable > * > * Return: 0 if successful, error code otherwise. > */ > -static int imx335_set_stream(struct v4l2_subdev *sd, int enable) > +static int imx335_disable_streams(struct v4l2_subdev *sd, > + struct v4l2_subdev_state *state, u32 pad, > + u64 streams_mask) > { > struct imx335 *imx335 = to_imx335(sd); > - struct v4l2_subdev_state *state; > - int ret = 0; > - > - state = v4l2_subdev_lock_and_get_active_state(sd); > - > - if (enable) > - ret = imx335_start_streaming(imx335); > - else > - imx335_stop_streaming(imx335); > + int ret; > > - v4l2_subdev_unlock_state(state); > + ret = cci_write(imx335->cci, IMX335_REG_MODE_SELECT, > + IMX335_MODE_STANDBY, NULL); > + pm_runtime_put(imx335->dev); > > return ret; > } > @@ -1249,7 +1242,7 @@ static int imx335_parse_hw_config(struct imx335 *imx335) > > /* V4l2 subdevice ops */ > static const struct v4l2_subdev_video_ops imx335_video_ops = { > - .s_stream = imx335_set_stream, > + .s_stream = v4l2_subdev_s_stream_helper, > }; > > static const struct v4l2_subdev_pad_ops imx335_pad_ops = { > @@ -1259,6 +1252,8 @@ static const struct v4l2_subdev_pad_ops imx335_pad_ops = { > .set_selection = imx335_get_selection, > .get_fmt = v4l2_subdev_get_fmt, > .set_fmt = imx335_set_pad_format, > + .enable_streams = imx335_enable_streams, > + .disable_streams = imx335_disable_streams, > }; > > static const struct v4l2_subdev_ops imx335_subdev_ops = { > > -- > 2.51.0 > ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2025-09-15 6:29 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-11 8:14 [PATCH v2 0/8] media: imx335: Vflip, active state and binning support Jai Luthra
2025-09-11 8:14 ` [PATCH v2 1/8] media: imx335: Rectify name of mode struct Jai Luthra
2025-09-11 8:14 ` [PATCH v2 2/8] media: imx335: Support vertical flip Jai Luthra
2025-09-11 8:14 ` [PATCH v2 3/8] media: imx335: Update the native pixel array width Jai Luthra
2025-09-11 8:14 ` [PATCH v2 4/8] media: imx335: Update HBLANK range on mode change Jai Luthra
2025-09-11 8:14 ` [PATCH v2 5/8] media: imx335: Handle runtime PM in leaf functions Jai Luthra
2025-09-11 11:14 ` Kieran Bingham
2025-09-15 6:28 ` Jai Luthra
2025-09-11 8:14 ` [PATCH v2 6/8] media: imx355: Use subdev active state Jai Luthra
2025-09-11 14:30 ` Kieran Bingham
2025-09-11 8:14 ` [PATCH v2 7/8] media: imx335: Support 2x2 binning Jai Luthra
2025-09-11 11:51 ` Kieran Bingham
2025-09-11 8:14 ` [PATCH v2 8/8] media: imx335: Switch to {enable,disable}_streams Jai Luthra
2025-09-11 11:46 ` Kieran Bingham
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®