From 3a7bd2d405cd663ef8b2ba4e9518f59ce17dee32 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 10:00:32 +0100 Subject: [PATCH 01/20] media: imx355: Avoid calling imx355_power_off twice in error path If v4l2_async_register_subdev_sensor failed, then the sensor had already been powered down by pm_runtime_idle, but the error path then also explicitly called imx355_power_off as well. That left an imbalance in the regulator and clock calls. Call pm_runtime_idle only after v4l2_async_register_subdev_sensor succeeds to avoid this. Fixes: efa5fe19c0a9 ("media: imx355: Enable runtime PM before registering async sub-device") Cc: stable@vger.kernel.org Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 27a5c212a527fa..ac59908f57aae6 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1822,12 +1822,13 @@ static int imx355_probe(struct i2c_client *client) */ pm_runtime_set_active(imx355->dev); pm_runtime_enable(imx355->dev); - pm_runtime_idle(imx355->dev); ret = v4l2_async_register_subdev_sensor(&imx355->sd); if (ret < 0) goto error_media_entity_runtime_pm; + pm_runtime_idle(imx355->dev); + return 0; error_media_entity_runtime_pm: From 0e3d4c1b3ac2aca64c2cb348ca6b357a7ecc1a69 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Tue, 14 Apr 2026 12:12:10 +0200 Subject: [PATCH 02/20] media: i2c: imx355: Add support for 24 MHz external clock The IMX355 sensor supports multiple external clock frequencies, including 19.2 MHz and 24 MHz. The driver currently supports only fixed 19.2 MHz input clock. Refactor the clock handling to make the PLL configuration dependent on the external clock frequency and add support for 24 MHz. Introduce a table of clock parameter sets and program the corresponding EXTCLK frequency and PLL multipliers to maintain consistent internal VCO frequencies across supported inputs. The PLL settings are adjusted so that OP VCO remains at 720 MHz This preserves existing timing characteristics while allowing systems using a 24 MHz clock to operate correctly. No functional change for existing 19.2 MHz users. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Heidelberg Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 77 ++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index ac59908f57aae6..a6ee7532018fca 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -25,6 +25,10 @@ #define IMX355_REG_CHIP_ID 0x0016 #define IMX355_CHIP_ID 0x0355 +/* PLL registers that depend on the external clock frequency */ +#define IMX355_REG_EXTCLK_FREQ 0x0136 +#define IMX355_REG_PLL_OP_MUL 0x030e + /* V_TIMING internal */ #define IMX355_REG_FLL 0x0340 #define IMX355_FLL_MAX 0xffff @@ -63,7 +67,6 @@ /* default link frequency and external clock */ #define IMX355_LINK_FREQ_DEFAULT 360000000LL -#define IMX355_EXT_CLK 19200000 #define IMX355_LINK_FREQ_INDEX 0 /* number of data lanes */ @@ -100,6 +103,29 @@ struct imx355_mode { struct imx355_reg_list reg_list; }; +struct imx355_clk_params { + u32 ext_clk; + u16 extclk_freq; /* External clock (MHz) in 8.8 fixed point) */ + u16 pll_op_mpy; /* OP system PLL multiplier */ +}; + +/* + * The clock tree is in single PLL mode, so PREDIV_VT and MPY_IVT do nothing. + * All modes use the same PLL setup for OP, with IOPCK being 720MHz. + */ +static const struct imx355_clk_params imx355_clk_params[] = { + { + .ext_clk = 19200000, + .extclk_freq = 0x1333, /* 19.2 MHz */ + .pll_op_mpy = 75, /* 19.2 / 2 * 75 = 720 MHz */ + }, + { + .ext_clk = 24000000, + .extclk_freq = 0x1800, /* 24.0 MHz */ + .pll_op_mpy = 60, /* 24.0 / 2 * 60 = 720 MHz */ + }, +}; + struct imx355_hwcfg { unsigned long link_freq_bitmap; }; @@ -125,6 +151,7 @@ struct imx355 { const struct imx355_mode *cur_mode; struct imx355_hwcfg *hwcfg; + const struct imx355_clk_params *clk_params; /* * Mutex for serialized access: @@ -144,8 +171,6 @@ static const struct regulator_bulk_data imx355_supplies[] = { }; static const struct imx355_reg imx355_global_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, { 0x304e, 0x03 }, { 0x4348, 0x16 }, { 0x4350, 0x19 }, @@ -235,8 +260,6 @@ static const struct imx355_reg mode_3268x2448_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -284,8 +307,6 @@ static const struct imx355_reg mode_3264x2448_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -333,8 +354,6 @@ static const struct imx355_reg mode_3280x2464_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -382,8 +401,6 @@ static const struct imx355_reg mode_1940x1096_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -431,8 +448,6 @@ static const struct imx355_reg mode_1936x1096_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -480,8 +495,6 @@ static const struct imx355_reg mode_1924x1080_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -529,8 +542,6 @@ static const struct imx355_reg mode_1920x1080_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -578,8 +589,6 @@ static const struct imx355_reg mode_1640x1232_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -627,8 +636,6 @@ static const struct imx355_reg mode_1640x922_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -676,8 +683,6 @@ static const struct imx355_reg mode_1300x736_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -725,8 +730,6 @@ static const struct imx355_reg mode_1296x736_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -774,8 +777,6 @@ static const struct imx355_reg mode_1284x720_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -823,8 +824,6 @@ static const struct imx355_reg mode_1280x720_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -872,8 +871,6 @@ static const struct imx355_reg mode_820x616_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x02 }, { 0x0701, 0x78 }, @@ -1422,6 +1419,16 @@ static int imx355_start_streaming(struct imx355 *imx355) return ret; } + /* Set PLL registers for the external clock frequency */ + ret = imx355_write_reg(imx355, IMX355_REG_EXTCLK_FREQ, 2, + imx355->clk_params->extclk_freq); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_PLL_OP_MUL, 2, + imx355->clk_params->pll_op_mpy); + if (ret) + return ret; + /* set digital gain control to all color mode */ ret = imx355_write_reg(imx355, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, 1); if (ret) @@ -1749,7 +1756,13 @@ static int imx355_probe(struct i2c_client *client) "failed to get clock\n"); freq = clk_get_rate(imx355->clk); - if (freq != IMX355_EXT_CLK) + for (unsigned int i = 0; i < ARRAY_SIZE(imx355_clk_params); i++) { + if (freq == imx355_clk_params[i].ext_clk) { + imx355->clk_params = &imx355_clk_params[i]; + break; + } + } + if (!imx355->clk_params) return dev_err_probe(imx355->dev, -EINVAL, "external clock %lu is not supported\n", freq); From 5e36e9b576e7d2172ace100d55c40495912484ea Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Apr 2026 15:07:59 +0100 Subject: [PATCH 03/20] media: imx355: Remove duplicated registers from the mode tables A large number of registers are identical within all the modes. Move those to imx355_global_regs. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi --- drivers/media/i2c/imx355.c | 345 +++---------------------------------- 1 file changed, 23 insertions(+), 322 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index a6ee7532018fca..01d6c28a2656e5 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -221,6 +221,29 @@ static const struct imx355_reg imx355_global_regs[] = { { 0x68b0, 0x00 }, { 0x3058, 0x00 }, { 0x305a, 0x00 }, + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x0310, 0x00 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, }; static const struct imx355_reg_list imx355_global_setting = { @@ -229,9 +252,6 @@ static const struct imx355_reg_list imx355_global_setting = { }; static const struct imx355_reg mode_3268x2448_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x0a }, @@ -244,8 +264,6 @@ static const struct imx355_reg mode_3268x2448_regs[] = { { 0x0349, 0xcb }, { 0x034a, 0x09 }, { 0x034b, 0x97 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -253,32 +271,11 @@ static const struct imx355_reg mode_3268x2448_regs[] = { { 0x034d, 0xc4 }, { 0x034e, 0x09 }, { 0x034f, 0x90 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_3264x2448_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x0a }, @@ -291,8 +288,6 @@ static const struct imx355_reg mode_3264x2448_regs[] = { { 0x0349, 0xc7 }, { 0x034a, 0x09 }, { 0x034b, 0x97 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -300,32 +295,11 @@ static const struct imx355_reg mode_3264x2448_regs[] = { { 0x034d, 0xc0 }, { 0x034e, 0x09 }, { 0x034f, 0x90 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_3280x2464_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x0a }, @@ -338,8 +312,6 @@ static const struct imx355_reg mode_3280x2464_regs[] = { { 0x0349, 0xcf }, { 0x034a, 0x09 }, { 0x034b, 0x9f }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -347,32 +319,11 @@ static const struct imx355_reg mode_3280x2464_regs[] = { { 0x034d, 0xd0 }, { 0x034e, 0x09 }, { 0x034f, 0xa0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1940x1096_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x05 }, @@ -385,8 +336,6 @@ static const struct imx355_reg mode_1940x1096_regs[] = { { 0x0349, 0x33 }, { 0x034a, 0x06 }, { 0x034b, 0xf3 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -394,32 +343,11 @@ static const struct imx355_reg mode_1940x1096_regs[] = { { 0x034d, 0x94 }, { 0x034e, 0x04 }, { 0x034f, 0x48 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1936x1096_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x05 }, @@ -432,8 +360,6 @@ static const struct imx355_reg mode_1936x1096_regs[] = { { 0x0349, 0x2f }, { 0x034a, 0x06 }, { 0x034b, 0xf3 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -441,32 +367,11 @@ static const struct imx355_reg mode_1936x1096_regs[] = { { 0x034d, 0x90 }, { 0x034e, 0x04 }, { 0x034f, 0x48 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1924x1080_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x05 }, @@ -479,8 +384,6 @@ static const struct imx355_reg mode_1924x1080_regs[] = { { 0x0349, 0x2b }, { 0x034a, 0x06 }, { 0x034b, 0xeb }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -488,32 +391,11 @@ static const struct imx355_reg mode_1924x1080_regs[] = { { 0x034d, 0x84 }, { 0x034e, 0x04 }, { 0x034f, 0x38 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1920x1080_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x05 }, @@ -526,8 +408,6 @@ static const struct imx355_reg mode_1920x1080_regs[] = { { 0x0349, 0x27 }, { 0x034a, 0x06 }, { 0x034b, 0xeb }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -535,32 +415,11 @@ static const struct imx355_reg mode_1920x1080_regs[] = { { 0x034d, 0x80 }, { 0x034e, 0x04 }, { 0x034f, 0x38 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1640x1232_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -573,8 +432,6 @@ static const struct imx355_reg mode_1640x1232_regs[] = { { 0x0349, 0xcf }, { 0x034a, 0x09 }, { 0x034b, 0x9f }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -582,32 +439,11 @@ static const struct imx355_reg mode_1640x1232_regs[] = { { 0x034d, 0x68 }, { 0x034e, 0x04 }, { 0x034f, 0xd0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1640x922_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -620,8 +456,6 @@ static const struct imx355_reg mode_1640x922_regs[] = { { 0x0349, 0xcf }, { 0x034a, 0x08 }, { 0x034b, 0x63 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -629,32 +463,11 @@ static const struct imx355_reg mode_1640x922_regs[] = { { 0x034d, 0x68 }, { 0x034e, 0x03 }, { 0x034f, 0x9a }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1300x736_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -667,8 +480,6 @@ static const struct imx355_reg mode_1300x736_regs[] = { { 0x0349, 0x7f }, { 0x034a, 0x07 }, { 0x034b, 0xaf }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -676,32 +487,11 @@ static const struct imx355_reg mode_1300x736_regs[] = { { 0x034d, 0x14 }, { 0x034e, 0x02 }, { 0x034f, 0xe0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1296x736_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -714,8 +504,6 @@ static const struct imx355_reg mode_1296x736_regs[] = { { 0x0349, 0x77 }, { 0x034a, 0x07 }, { 0x034b, 0xaf }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -723,32 +511,11 @@ static const struct imx355_reg mode_1296x736_regs[] = { { 0x034d, 0x10 }, { 0x034e, 0x02 }, { 0x034f, 0xe0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1284x720_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -761,8 +528,6 @@ static const struct imx355_reg mode_1284x720_regs[] = { { 0x0349, 0x6f }, { 0x034a, 0x07 }, { 0x034b, 0x9f }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -770,32 +535,11 @@ static const struct imx355_reg mode_1284x720_regs[] = { { 0x034d, 0x04 }, { 0x034e, 0x02 }, { 0x034f, 0xd0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1280x720_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -808,8 +552,6 @@ static const struct imx355_reg mode_1280x720_regs[] = { { 0x0349, 0x67 }, { 0x034a, 0x07 }, { 0x034b, 0x9f }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -817,32 +559,11 @@ static const struct imx355_reg mode_1280x720_regs[] = { { 0x034d, 0x00 }, { 0x034e, 0x02 }, { 0x034f, 0xd0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_820x616_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x02 }, @@ -855,8 +576,6 @@ static const struct imx355_reg mode_820x616_regs[] = { { 0x0349, 0xcf }, { 0x034a, 0x09 }, { 0x034b, 0x9f }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x44 }, { 0x0902, 0x00 }, @@ -864,26 +583,8 @@ static const struct imx355_reg mode_820x616_regs[] = { { 0x034d, 0x34 }, { 0x034e, 0x02 }, { 0x034f, 0x68 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x02 }, { 0x0701, 0x78 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const char * const imx355_test_pattern_menu[] = { From 78960bc602ba654c1a39abb541245c67dc827516 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Apr 2026 15:27:07 +0100 Subject: [PATCH 04/20] media: imx355: Remove setting FRM_LENGTH_LINES in the mode regs Registers 0x0340 and 0x0341 (FRM_LENGTH_LINES) are already written from the set_ctrl(V4L2_CID_VBLANK) handler, so don't write them from the mode register list. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi --- drivers/media/i2c/imx355.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 01d6c28a2656e5..b40d89d86156ec 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -254,8 +254,6 @@ static const struct imx355_reg_list imx355_global_setting = { static const struct imx355_reg mode_3268x2448_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x0a }, - { 0x0341, 0x37 }, { 0x0344, 0x00 }, { 0x0345, 0x08 }, { 0x0346, 0x00 }, @@ -278,8 +276,6 @@ static const struct imx355_reg mode_3268x2448_regs[] = { static const struct imx355_reg mode_3264x2448_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x0a }, - { 0x0341, 0x37 }, { 0x0344, 0x00 }, { 0x0345, 0x08 }, { 0x0346, 0x00 }, @@ -302,8 +298,6 @@ static const struct imx355_reg mode_3264x2448_regs[] = { static const struct imx355_reg mode_3280x2464_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x0a }, - { 0x0341, 0x37 }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x00 }, @@ -326,8 +320,6 @@ static const struct imx355_reg mode_3280x2464_regs[] = { static const struct imx355_reg mode_1940x1096_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x02 }, { 0x0345, 0xa0 }, { 0x0346, 0x02 }, @@ -350,8 +342,6 @@ static const struct imx355_reg mode_1940x1096_regs[] = { static const struct imx355_reg mode_1936x1096_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x02 }, { 0x0345, 0xa0 }, { 0x0346, 0x02 }, @@ -374,8 +364,6 @@ static const struct imx355_reg mode_1936x1096_regs[] = { static const struct imx355_reg mode_1924x1080_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x02 }, { 0x0345, 0xa8 }, { 0x0346, 0x02 }, @@ -398,8 +386,6 @@ static const struct imx355_reg mode_1924x1080_regs[] = { static const struct imx355_reg mode_1920x1080_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x02 }, { 0x0345, 0xa8 }, { 0x0346, 0x02 }, @@ -422,8 +408,6 @@ static const struct imx355_reg mode_1920x1080_regs[] = { static const struct imx355_reg mode_1640x1232_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x00 }, @@ -446,8 +430,6 @@ static const struct imx355_reg mode_1640x1232_regs[] = { static const struct imx355_reg mode_1640x922_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x01 }, @@ -470,8 +452,6 @@ static const struct imx355_reg mode_1640x922_regs[] = { static const struct imx355_reg mode_1300x736_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x01 }, { 0x0345, 0x58 }, { 0x0346, 0x01 }, @@ -494,8 +474,6 @@ static const struct imx355_reg mode_1300x736_regs[] = { static const struct imx355_reg mode_1296x736_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x01 }, { 0x0345, 0x58 }, { 0x0346, 0x01 }, @@ -518,8 +496,6 @@ static const struct imx355_reg mode_1296x736_regs[] = { static const struct imx355_reg mode_1284x720_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x01 }, { 0x0345, 0x68 }, { 0x0346, 0x02 }, @@ -542,8 +518,6 @@ static const struct imx355_reg mode_1284x720_regs[] = { static const struct imx355_reg mode_1280x720_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x01 }, { 0x0345, 0x68 }, { 0x0346, 0x02 }, @@ -566,8 +540,6 @@ static const struct imx355_reg mode_1280x720_regs[] = { static const struct imx355_reg mode_820x616_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x02 }, - { 0x0341, 0x8c }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x00 }, From 60e9e2ff9320d8fe2bc87799f9ce49255b11d7d3 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Apr 2026 15:53:10 +0100 Subject: [PATCH 05/20] media: imx355: Programmatically set the crop parameters for each mode Currently the cropping is set via register entries in the per mode register lists. Add the crop information to the mode structure as a v4l2_rect, and set the registers programmatically. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 290 ++++++++++++++++--------------------- 1 file changed, 121 insertions(+), 169 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index b40d89d86156ec..123ceed26d0531 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -33,6 +33,13 @@ #define IMX355_REG_FLL 0x0340 #define IMX355_FLL_MAX 0xffff +#define IMX355_REG_X_ADD_START 0x0344 +#define IMX355_REG_Y_ADD_START 0x0346 +#define IMX355_REG_X_ADD_END 0x0348 +#define IMX355_REG_Y_ADD_END 0x034a +#define IMX355_REG_X_OUT_SIZE 0x034c +#define IMX355_REG_Y_OUT_SIZE 0x034e + /* Exposure control */ #define IMX355_REG_EXPOSURE 0x0202 #define IMX355_EXPOSURE_MIN 1 @@ -88,6 +95,7 @@ struct imx355_mode { u32 width; /* Frame height */ u32 height; + struct v4l2_rect crop; /* V-timing */ u32 fll_def; @@ -254,21 +262,9 @@ static const struct imx355_reg_list imx355_global_setting = { static const struct imx355_reg mode_3268x2448_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x00 }, - { 0x0345, 0x08 }, - { 0x0346, 0x00 }, - { 0x0347, 0x08 }, - { 0x0348, 0x0c }, - { 0x0349, 0xcb }, - { 0x034a, 0x09 }, - { 0x034b, 0x97 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x0c }, - { 0x034d, 0xc4 }, - { 0x034e, 0x09 }, - { 0x034f, 0x90 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -276,21 +272,9 @@ static const struct imx355_reg mode_3268x2448_regs[] = { static const struct imx355_reg mode_3264x2448_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x00 }, - { 0x0345, 0x08 }, - { 0x0346, 0x00 }, - { 0x0347, 0x08 }, - { 0x0348, 0x0c }, - { 0x0349, 0xc7 }, - { 0x034a, 0x09 }, - { 0x034b, 0x97 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x0c }, - { 0x034d, 0xc0 }, - { 0x034e, 0x09 }, - { 0x034f, 0x90 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -298,21 +282,9 @@ static const struct imx355_reg mode_3264x2448_regs[] = { static const struct imx355_reg mode_3280x2464_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x0c }, - { 0x0349, 0xcf }, - { 0x034a, 0x09 }, - { 0x034b, 0x9f }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x0c }, - { 0x034d, 0xd0 }, - { 0x034e, 0x09 }, - { 0x034f, 0xa0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -320,21 +292,9 @@ static const struct imx355_reg mode_3280x2464_regs[] = { static const struct imx355_reg mode_1940x1096_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x02 }, - { 0x0345, 0xa0 }, - { 0x0346, 0x02 }, - { 0x0347, 0xac }, - { 0x0348, 0x0a }, - { 0x0349, 0x33 }, - { 0x034a, 0x06 }, - { 0x034b, 0xf3 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x07 }, - { 0x034d, 0x94 }, - { 0x034e, 0x04 }, - { 0x034f, 0x48 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -342,21 +302,9 @@ static const struct imx355_reg mode_1940x1096_regs[] = { static const struct imx355_reg mode_1936x1096_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x02 }, - { 0x0345, 0xa0 }, - { 0x0346, 0x02 }, - { 0x0347, 0xac }, - { 0x0348, 0x0a }, - { 0x0349, 0x2f }, - { 0x034a, 0x06 }, - { 0x034b, 0xf3 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x07 }, - { 0x034d, 0x90 }, - { 0x034e, 0x04 }, - { 0x034f, 0x48 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -364,21 +312,9 @@ static const struct imx355_reg mode_1936x1096_regs[] = { static const struct imx355_reg mode_1924x1080_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x02 }, - { 0x0345, 0xa8 }, - { 0x0346, 0x02 }, - { 0x0347, 0xb4 }, - { 0x0348, 0x0a }, - { 0x0349, 0x2b }, - { 0x034a, 0x06 }, - { 0x034b, 0xeb }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x07 }, - { 0x034d, 0x84 }, - { 0x034e, 0x04 }, - { 0x034f, 0x38 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -386,21 +322,9 @@ static const struct imx355_reg mode_1924x1080_regs[] = { static const struct imx355_reg mode_1920x1080_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x02 }, - { 0x0345, 0xa8 }, - { 0x0346, 0x02 }, - { 0x0347, 0xb4 }, - { 0x0348, 0x0a }, - { 0x0349, 0x27 }, - { 0x034a, 0x06 }, - { 0x034b, 0xeb }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x07 }, - { 0x034d, 0x80 }, - { 0x034e, 0x04 }, - { 0x034f, 0x38 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -408,21 +332,9 @@ static const struct imx355_reg mode_1920x1080_regs[] = { static const struct imx355_reg mode_1640x1232_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x0c }, - { 0x0349, 0xcf }, - { 0x034a, 0x09 }, - { 0x034b, 0x9f }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x06 }, - { 0x034d, 0x68 }, - { 0x034e, 0x04 }, - { 0x034f, 0xd0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -430,21 +342,9 @@ static const struct imx355_reg mode_1640x1232_regs[] = { static const struct imx355_reg mode_1640x922_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x01 }, - { 0x0347, 0x30 }, - { 0x0348, 0x0c }, - { 0x0349, 0xcf }, - { 0x034a, 0x08 }, - { 0x034b, 0x63 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x06 }, - { 0x034d, 0x68 }, - { 0x034e, 0x03 }, - { 0x034f, 0x9a }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -452,21 +352,9 @@ static const struct imx355_reg mode_1640x922_regs[] = { static const struct imx355_reg mode_1300x736_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x01 }, - { 0x0345, 0x58 }, - { 0x0346, 0x01 }, - { 0x0347, 0xf0 }, - { 0x0348, 0x0b }, - { 0x0349, 0x7f }, - { 0x034a, 0x07 }, - { 0x034b, 0xaf }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x05 }, - { 0x034d, 0x14 }, - { 0x034e, 0x02 }, - { 0x034f, 0xe0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -474,21 +362,9 @@ static const struct imx355_reg mode_1300x736_regs[] = { static const struct imx355_reg mode_1296x736_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x01 }, - { 0x0345, 0x58 }, - { 0x0346, 0x01 }, - { 0x0347, 0xf0 }, - { 0x0348, 0x0b }, - { 0x0349, 0x77 }, - { 0x034a, 0x07 }, - { 0x034b, 0xaf }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x05 }, - { 0x034d, 0x10 }, - { 0x034e, 0x02 }, - { 0x034f, 0xe0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -496,21 +372,9 @@ static const struct imx355_reg mode_1296x736_regs[] = { static const struct imx355_reg mode_1284x720_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x01 }, - { 0x0345, 0x68 }, - { 0x0346, 0x02 }, - { 0x0347, 0x00 }, - { 0x0348, 0x0b }, - { 0x0349, 0x6f }, - { 0x034a, 0x07 }, - { 0x034b, 0x9f }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x05 }, - { 0x034d, 0x04 }, - { 0x034e, 0x02 }, - { 0x034f, 0xd0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -518,21 +382,9 @@ static const struct imx355_reg mode_1284x720_regs[] = { static const struct imx355_reg mode_1280x720_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x01 }, - { 0x0345, 0x68 }, - { 0x0346, 0x02 }, - { 0x0347, 0x00 }, - { 0x0348, 0x0b }, - { 0x0349, 0x67 }, - { 0x034a, 0x07 }, - { 0x034b, 0x9f }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x05 }, - { 0x034d, 0x00 }, - { 0x034e, 0x02 }, - { 0x034f, 0xd0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -540,21 +392,9 @@ static const struct imx355_reg mode_1280x720_regs[] = { static const struct imx355_reg mode_820x616_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x0c }, - { 0x0349, 0xcf }, - { 0x034a, 0x09 }, - { 0x034b, 0x9f }, { 0x0900, 0x01 }, { 0x0901, 0x44 }, { 0x0902, 0x00 }, - { 0x034c, 0x03 }, - { 0x034d, 0x34 }, - { 0x034e, 0x02 }, - { 0x034f, 0x68 }, { 0x0700, 0x02 }, { 0x0701, 0x78 }, }; @@ -580,6 +420,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 3280, .height = 2464, + .crop = { + .width = 3280, + .height = 2464, + .left = 0, + .top = 0, + }, .fll_def = 2615, .fll_min = 2615, .llp = 3672, @@ -592,6 +438,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 3268, .height = 2448, + .crop = { + .width = 3268, + .height = 2448, + .left = 8, + .top = 8, + }, .fll_def = 2615, .fll_min = 2615, .llp = 3672, @@ -604,6 +456,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 3264, .height = 2448, + .crop = { + .width = 3264, + .height = 2448, + .left = 8, + .top = 8, + }, .fll_def = 2615, .fll_min = 2615, .llp = 3672, @@ -616,6 +474,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1940, .height = 1096, + .crop = { + .width = 1940, + .height = 1096, + .left = 672, + .top = 684, + }, .fll_def = 1306, .fll_min = 1306, .llp = 3672, @@ -628,6 +492,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1936, .height = 1096, + .crop = { + .width = 1936, + .height = 1096, + .left = 672, + .top = 684, + }, .fll_def = 1306, .fll_min = 1306, .llp = 3672, @@ -640,6 +510,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1924, .height = 1080, + .crop = { + .width = 1924, + .height = 1080, + .left = 680, + .top = 692, + }, .fll_def = 1306, .fll_min = 1306, .llp = 3672, @@ -652,6 +528,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1920, .height = 1080, + .crop = { + .width = 1920, + .height = 1080, + .left = 680, + .top = 692, + }, .fll_def = 1306, .fll_min = 1306, .llp = 3672, @@ -664,6 +546,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1640, .height = 1232, + .crop = { + .width = 3280, + .height = 2464, + .left = 0, + .top = 0, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -676,6 +564,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1640, .height = 922, + .crop = { + .width = 3280, + .height = 1844, + .left = 0, + .top = 304, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -688,6 +582,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1300, .height = 736, + .crop = { + .width = 2600, + .height = 1472, + .left = 344, + .top = 496, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -700,6 +600,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1296, .height = 736, + .crop = { + .width = 2592, + .height = 1472, + .left = 344, + .top = 496, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -712,6 +618,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1284, .height = 720, + .crop = { + .width = 2568, + .height = 1440, + .left = 360, + .top = 512, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -724,6 +636,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1280, .height = 720, + .crop = { + .width = 2560, + .height = 1440, + .left = 360, + .top = 512, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -736,6 +654,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 820, .height = 616, + .crop = { + .width = 3280, + .height = 2464, + .left = 0, + .top = 0, + }, .fll_def = 652, .fll_min = 652, .llp = 3672, @@ -1074,6 +998,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, static int imx355_start_streaming(struct imx355 *imx355) { const struct imx355_reg_list *reg_list; + const struct imx355_mode *mode; int ret; /* Global Setting */ @@ -1085,13 +1010,40 @@ static int imx355_start_streaming(struct imx355 *imx355) } /* Apply default values of current mode */ - reg_list = &imx355->cur_mode->reg_list; + mode = imx355->cur_mode; + reg_list = &mode->reg_list; ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(imx355->dev, "failed to set mode"); return ret; } + /* Set readout crop and size registers */ + ret = imx355_write_reg(imx355, IMX355_REG_X_ADD_START, 2, + mode->crop.left); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_Y_ADD_START, 2, + mode->crop.top); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_X_ADD_END, 2, + mode->crop.width + mode->crop.left - 1); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_Y_ADD_END, 2, + mode->crop.height + mode->crop.top - 1); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_X_OUT_SIZE, 2, + mode->width); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_Y_OUT_SIZE, 2, + mode->height); + if (ret) + return ret; + /* Set PLL registers for the external clock frequency */ ret = imx355_write_reg(imx355, IMX355_REG_EXTCLK_FREQ, 2, imx355->clk_params->extclk_freq); From ec7162a9ca9dbe8b3be4cf692dc48e6a9bb6c772 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Apr 2026 16:06:21 +0100 Subject: [PATCH 06/20] media: imx355: Set register LINE_LENGTH_PCK programmatically The driver already has the LLP value stored in the mode structure, but also had the same value set via register writes in the mode's register list. Remove this duplication. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi --- drivers/media/i2c/imx355.c | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 123ceed26d0531..e9f4bea67d3a74 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -33,6 +33,9 @@ #define IMX355_REG_FLL 0x0340 #define IMX355_FLL_MAX 0xffff +#define IMX355_REG_LLP 0x0342 +#define IMX355_LLP_MAX 0xffff + #define IMX355_REG_X_ADD_START 0x0344 #define IMX355_REG_Y_ADD_START 0x0346 #define IMX355_REG_X_ADD_END 0x0348 @@ -260,8 +263,6 @@ static const struct imx355_reg_list imx355_global_setting = { }; static const struct imx355_reg mode_3268x2448_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -270,8 +271,6 @@ static const struct imx355_reg mode_3268x2448_regs[] = { }; static const struct imx355_reg mode_3264x2448_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -280,8 +279,6 @@ static const struct imx355_reg mode_3264x2448_regs[] = { }; static const struct imx355_reg mode_3280x2464_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -290,8 +287,6 @@ static const struct imx355_reg mode_3280x2464_regs[] = { }; static const struct imx355_reg mode_1940x1096_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -300,8 +295,6 @@ static const struct imx355_reg mode_1940x1096_regs[] = { }; static const struct imx355_reg mode_1936x1096_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -310,8 +303,6 @@ static const struct imx355_reg mode_1936x1096_regs[] = { }; static const struct imx355_reg mode_1924x1080_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -320,8 +311,6 @@ static const struct imx355_reg mode_1924x1080_regs[] = { }; static const struct imx355_reg mode_1920x1080_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -330,8 +319,6 @@ static const struct imx355_reg mode_1920x1080_regs[] = { }; static const struct imx355_reg mode_1640x1232_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -340,8 +327,6 @@ static const struct imx355_reg mode_1640x1232_regs[] = { }; static const struct imx355_reg mode_1640x922_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -350,8 +335,6 @@ static const struct imx355_reg mode_1640x922_regs[] = { }; static const struct imx355_reg mode_1300x736_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -360,8 +343,6 @@ static const struct imx355_reg mode_1300x736_regs[] = { }; static const struct imx355_reg mode_1296x736_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -370,8 +351,6 @@ static const struct imx355_reg mode_1296x736_regs[] = { }; static const struct imx355_reg mode_1284x720_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -380,8 +359,6 @@ static const struct imx355_reg mode_1284x720_regs[] = { }; static const struct imx355_reg mode_1280x720_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -390,8 +367,6 @@ static const struct imx355_reg mode_1280x720_regs[] = { }; static const struct imx355_reg mode_820x616_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x01 }, { 0x0901, 0x44 }, { 0x0902, 0x00 }, @@ -1059,6 +1034,12 @@ static int imx355_start_streaming(struct imx355 *imx355) if (ret) return ret; + /* set line length */ + ret = imx355_write_reg(imx355, IMX355_REG_LLP, 2, + imx355->hblank->val + imx355->cur_mode->width); + if (ret) + return ret; + /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); if (ret) From c13b86a40237c370a1233e93302ed7508a0f3325 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Apr 2026 16:13:53 +0100 Subject: [PATCH 07/20] media: imx355: Set binning mode registers programmatically Compute the binning registers based on the difference between the mode width/height vs the crop width/height. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 61 ++++++++++++-------------------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index e9f4bea67d3a74..95487b12939def 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -72,6 +72,10 @@ #define IMX355_TEST_PATTERN_GRAY_COLOR_BARS 3 #define IMX355_TEST_PATTERN_PN9 4 +#define IMX355_REG_BINNING_MODE 0x0900 +#define IMX355_REG_BINNING_TYPE 0x0901 +#define IMX355_REG_BINNING_WEIGHTING 0x0902 + /* Flip Control */ #define IMX355_REG_ORIENTATION 0x0101 @@ -263,113 +267,71 @@ static const struct imx355_reg_list imx355_global_setting = { }; static const struct imx355_reg mode_3268x2448_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_3264x2448_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_3280x2464_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1940x1096_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1936x1096_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1924x1080_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1920x1080_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1640x1232_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1640x922_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1300x736_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1296x736_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1284x720_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1280x720_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_820x616_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x44 }, - { 0x0902, 0x00 }, { 0x0700, 0x02 }, { 0x0701, 0x78 }, }; @@ -974,6 +936,7 @@ static int imx355_start_streaming(struct imx355 *imx355) { const struct imx355_reg_list *reg_list; const struct imx355_mode *mode; + u8 binning_mode; int ret; /* Global Setting */ @@ -1019,6 +982,20 @@ static int imx355_start_streaming(struct imx355 *imx355) if (ret) return ret; + binning_mode = ((mode->crop.width / mode->width) << 4) | + (mode->crop.height / mode->height); + ret = imx355_write_reg(imx355, IMX355_REG_BINNING_MODE, 1, + binning_mode == 0x11 ? 0x00 : 0x01); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_BINNING_TYPE, 1, + binning_mode); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_BINNING_WEIGHTING, 1, 0x00); + if (ret) + return ret; + /* Set PLL registers for the external clock frequency */ ret = imx355_write_reg(imx355, IMX355_REG_EXTCLK_FREQ, 2, imx355->clk_params->extclk_freq); From 3829ecdcd2b4578c9d5fcc1abba936eb05f73327 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 29 Apr 2026 14:46:06 +0100 Subject: [PATCH 08/20] media: imx355: Remove link_freq_index from each mode as ununsed The link_freq_index value in imx355_mode is unused, so remove it. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi --- drivers/media/i2c/imx355.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 95487b12939def..b329da85ddc093 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -81,7 +81,6 @@ /* default link frequency and external clock */ #define IMX355_LINK_FREQ_DEFAULT 360000000LL -#define IMX355_LINK_FREQ_INDEX 0 /* number of data lanes */ #define IMX355_DATA_LANES 4 @@ -111,9 +110,6 @@ struct imx355_mode { /* H-timing */ u32 llp; - /* index of link frequency */ - u32 link_freq_index; - /* Default register values */ struct imx355_reg_list reg_list; }; @@ -366,7 +362,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 2615, .fll_min = 2615, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), .regs = mode_3280x2464_regs, @@ -384,7 +379,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 2615, .fll_min = 2615, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3268x2448_regs), .regs = mode_3268x2448_regs, @@ -402,7 +396,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 2615, .fll_min = 2615, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), .regs = mode_3264x2448_regs, @@ -420,7 +413,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1940x1096_regs), .regs = mode_1940x1096_regs, @@ -438,7 +430,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1936x1096_regs), .regs = mode_1936x1096_regs, @@ -456,7 +447,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1924x1080_regs), .regs = mode_1924x1080_regs, @@ -474,7 +464,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), .regs = mode_1920x1080_regs, @@ -492,7 +481,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), .regs = mode_1640x1232_regs, @@ -510,7 +498,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640x922_regs), .regs = mode_1640x922_regs, @@ -528,7 +515,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1300x736_regs), .regs = mode_1300x736_regs, @@ -546,7 +532,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1296x736_regs), .regs = mode_1296x736_regs, @@ -564,7 +549,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1284x720_regs), .regs = mode_1284x720_regs, @@ -582,7 +566,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), .regs = mode_1280x720_regs, @@ -600,7 +583,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 652, .fll_min = 652, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_820x616_regs), .regs = mode_820x616_regs, From c76fdf7f23b39562914b948e0166197341602bd6 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 29 Apr 2026 14:49:19 +0100 Subject: [PATCH 09/20] media: imx355: pixel_rate never changes, so don't recompute The pixel rate is always the same, so there is no need to try and recompute it in imx355_set_pad_format, and then no need to have the pointer to it stored. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi --- drivers/media/i2c/imx355.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index b329da85ddc093..879e98b8a8efdd 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -151,7 +151,6 @@ struct imx355 { struct v4l2_ctrl_handler ctrl_handler; /* V4L2 Controls */ struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *vblank; struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; @@ -867,7 +866,6 @@ imx355_set_pad_format(struct v4l2_subdev *sd, s32 vblank_def; s32 vblank_min; s64 h_blank; - u64 pixel_rate; u32 height; mutex_lock(&imx355->mutex); @@ -888,9 +886,6 @@ imx355_set_pad_format(struct v4l2_subdev *sd, *framefmt = fmt->format; } else { imx355->cur_mode = mode; - pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; - do_div(pixel_rate, 10); - __v4l2_ctrl_s_ctrl_int64(imx355->pixel_rate, pixel_rate); /* Update limits and set FPS to default */ height = imx355->cur_mode->height; vblank_def = imx355->cur_mode->fll_def - height; @@ -1180,9 +1175,8 @@ static int imx355_init_controls(struct imx355 *imx355) pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; do_div(pixel_rate, 10); /* By default, PIXEL_RATE is read only */ - imx355->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, - V4L2_CID_PIXEL_RATE, pixel_rate, - pixel_rate, 1, pixel_rate); + v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_PIXEL_RATE, + pixel_rate, pixel_rate, 1, pixel_rate); /* Initialize vblank/hblank/exposure parameters based on current mode */ mode = imx355->cur_mode; From 5dc3b08a166f0fe522a339e47b7bd858d1cae1b1 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 29 Apr 2026 15:23:04 +0100 Subject: [PATCH 10/20] media: imx355: Remove redundant fll_min, and implement fixed offset fll_min (Frame Length Lines) is set to the same value as fll_def for all modes, which makes it redundant. The actual value is also erroneous as the sensor works in all the defined modes with FLL set at the mode height + 20 lines, so set the vblank control minimum to 20 rather than varying it. This also improves the maximum frame rate achievable. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi --- drivers/media/i2c/imx355.c | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 879e98b8a8efdd..649a6fc9abe9ae 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -32,6 +32,7 @@ /* V_TIMING internal */ #define IMX355_REG_FLL 0x0340 #define IMX355_FLL_MAX 0xffff +#define IMX355_VBLANK_MIN 20 #define IMX355_REG_LLP 0x0342 #define IMX355_LLP_MAX 0xffff @@ -105,7 +106,6 @@ struct imx355_mode { /* V-timing */ u32 fll_def; - u32 fll_min; /* H-timing */ u32 llp; @@ -359,7 +359,6 @@ static const struct imx355_mode supported_modes[] = { .top = 0, }, .fll_def = 2615, - .fll_min = 2615, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), @@ -376,7 +375,6 @@ static const struct imx355_mode supported_modes[] = { .top = 8, }, .fll_def = 2615, - .fll_min = 2615, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3268x2448_regs), @@ -393,7 +391,6 @@ static const struct imx355_mode supported_modes[] = { .top = 8, }, .fll_def = 2615, - .fll_min = 2615, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), @@ -410,7 +407,6 @@ static const struct imx355_mode supported_modes[] = { .top = 684, }, .fll_def = 1306, - .fll_min = 1306, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1940x1096_regs), @@ -427,7 +423,6 @@ static const struct imx355_mode supported_modes[] = { .top = 684, }, .fll_def = 1306, - .fll_min = 1306, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1936x1096_regs), @@ -444,7 +439,6 @@ static const struct imx355_mode supported_modes[] = { .top = 692, }, .fll_def = 1306, - .fll_min = 1306, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1924x1080_regs), @@ -461,7 +455,6 @@ static const struct imx355_mode supported_modes[] = { .top = 692, }, .fll_def = 1306, - .fll_min = 1306, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), @@ -478,7 +471,6 @@ static const struct imx355_mode supported_modes[] = { .top = 0, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), @@ -495,7 +487,6 @@ static const struct imx355_mode supported_modes[] = { .top = 304, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640x922_regs), @@ -512,7 +503,6 @@ static const struct imx355_mode supported_modes[] = { .top = 496, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1300x736_regs), @@ -529,7 +519,6 @@ static const struct imx355_mode supported_modes[] = { .top = 496, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1296x736_regs), @@ -546,7 +535,6 @@ static const struct imx355_mode supported_modes[] = { .top = 512, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1284x720_regs), @@ -563,7 +551,6 @@ static const struct imx355_mode supported_modes[] = { .top = 512, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), @@ -580,7 +567,6 @@ static const struct imx355_mode supported_modes[] = { .top = 0, }, .fll_def = 652, - .fll_min = 652, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_820x616_regs), @@ -864,7 +850,6 @@ imx355_set_pad_format(struct v4l2_subdev *sd, const struct imx355_mode *mode; struct v4l2_mbus_framefmt *framefmt; s32 vblank_def; - s32 vblank_min; s64 h_blank; u32 height; @@ -889,10 +874,9 @@ imx355_set_pad_format(struct v4l2_subdev *sd, /* Update limits and set FPS to default */ height = imx355->cur_mode->height; vblank_def = imx355->cur_mode->fll_def - height; - vblank_min = imx355->cur_mode->fll_min - height; height = IMX355_FLL_MAX - height; - __v4l2_ctrl_modify_range(imx355->vblank, vblank_min, height, 1, - vblank_def); + __v4l2_ctrl_modify_range(imx355->vblank, IMX355_VBLANK_MIN, + height, 1, vblank_def); __v4l2_ctrl_s_ctrl(imx355->vblank, vblank_def); h_blank = mode->llp - imx355->cur_mode->width; /* @@ -1151,7 +1135,6 @@ static int imx355_init_controls(struct imx355 *imx355) struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max; s64 vblank_def; - s64 vblank_min; s64 hblank; u64 pixel_rate; const struct imx355_mode *mode; @@ -1181,9 +1164,8 @@ static int imx355_init_controls(struct imx355 *imx355) /* Initialize vblank/hblank/exposure parameters based on current mode */ mode = imx355->cur_mode; vblank_def = mode->fll_def - mode->height; - vblank_min = mode->fll_min - mode->height; imx355->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, - V4L2_CID_VBLANK, vblank_min, + V4L2_CID_VBLANK, IMX355_VBLANK_MIN, IMX355_FLL_MAX - mode->height, 1, vblank_def); From 0757564924513c4dc2efe07e7e6332ad8330e181 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Apr 2026 19:05:29 +0100 Subject: [PATCH 11/20] media: imx355: Add support for get_selection Provide all the cropping information via get_selection. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 649a6fc9abe9ae..120eda46d98209 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -86,6 +86,11 @@ /* number of data lanes */ #define IMX355_DATA_LANES 4 +#define IMX355_PIXEL_ARRAY_TOP 0 +#define IMX355_PIXEL_ARRAY_LEFT 0 +#define IMX355_PIXEL_ARRAY_WIDTH 3280 +#define IMX355_PIXEL_ARRAY_HEIGHT 2464 + struct imx355_reg { u16 address; u8 val; @@ -677,6 +682,7 @@ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) struct imx355 *imx355 = to_imx355(sd); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(fh->state, 0); mutex_lock(&imx355->mutex); @@ -686,6 +692,8 @@ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt->code = imx355_get_format_code(imx355); try_fmt->field = V4L2_FIELD_NONE; + *crop = imx355->cur_mode->crop; + mutex_unlock(&imx355->mutex); return 0; @@ -849,6 +857,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, struct imx355 *imx355 = to_imx355(sd); const struct imx355_mode *mode; struct v4l2_mbus_framefmt *framefmt; + struct v4l2_rect *crop; s32 vblank_def; s64 h_blank; u32 height; @@ -869,6 +878,12 @@ imx355_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; + + crop = v4l2_subdev_state_get_crop(sd_state, 0); + crop->width = mode->crop.width; + crop->height = mode->crop.height; + crop->left = mode->crop.left; + crop->top = mode->crop.top; } else { imx355->cur_mode = mode; /* Update limits and set FPS to default */ @@ -892,6 +907,50 @@ imx355_set_pad_format(struct v4l2_subdev *sd, return 0; } +static void +__imx355_get_pad_crop(struct imx355 *imx355, + struct v4l2_subdev_state *sd_state, unsigned int pad, + enum v4l2_subdev_format_whence which, struct v4l2_rect *r) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + *r = *v4l2_subdev_state_get_crop(sd_state, pad); + break; + case V4L2_SUBDEV_FORMAT_ACTIVE: + *r = imx355->cur_mode->crop; + break; + } +} + +static int imx355_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct imx355 *imx355 = to_imx355(sd); + + mutex_lock(&imx355->mutex); + __imx355_get_pad_crop(imx355, sd_state, sel->pad, sel->which, + &sel->r); + mutex_unlock(&imx355->mutex); + + return 0; + } + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = IMX355_PIXEL_ARRAY_TOP; + sel->r.left = IMX355_PIXEL_ARRAY_LEFT; + sel->r.width = IMX355_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX355_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + /* Start streaming */ static int imx355_start_streaming(struct imx355 *imx355) { @@ -1066,6 +1125,7 @@ static const struct v4l2_subdev_pad_ops imx355_pad_ops = { .get_fmt = imx355_get_pad_format, .set_fmt = imx355_set_pad_format, .enum_frame_size = imx355_enum_frame_size, + .get_selection = imx355_get_selection, }; static const struct v4l2_subdev_ops imx355_subdev_ops = { From 1857a3825d7cddc891712dbf5ce19c5e29f0bc88 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Apr 2026 19:06:56 +0100 Subject: [PATCH 12/20] media: imx355: Use pm_runtime autosuspend_delay Avoid powering the sensor up and down unnecessarily by using pm_runtime's autosuspend_delay feature. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi --- drivers/media/i2c/imx355.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 120eda46d98209..17a2acc1152df1 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1074,7 +1074,7 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) goto err_rpm_put; } else { imx355_stop_streaming(imx355); - pm_runtime_put(imx355->dev); + pm_runtime_put_autosuspend(imx355->dev); } /* vflip and hflip cannot change during streaming */ @@ -1086,7 +1086,7 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) return ret; err_rpm_put: - pm_runtime_put(imx355->dev); + pm_runtime_put_autosuspend(imx355->dev); err_unlock: mutex_unlock(&imx355->mutex); @@ -1436,6 +1436,8 @@ static int imx355_probe(struct i2c_client *client) */ pm_runtime_set_active(imx355->dev); pm_runtime_enable(imx355->dev); + pm_runtime_set_autosuspend_delay(imx355->dev, 1000); + pm_runtime_use_autosuspend(imx355->dev); ret = v4l2_async_register_subdev_sensor(&imx355->sd); if (ret < 0) @@ -1448,6 +1450,7 @@ static int imx355_probe(struct i2c_client *client) error_media_entity_runtime_pm: pm_runtime_disable(imx355->dev); pm_runtime_set_suspended(imx355->dev); + pm_runtime_dont_use_autosuspend(imx355->dev); media_entity_cleanup(&imx355->sd.entity); error_handler_free: @@ -1478,6 +1481,8 @@ static void imx355_remove(struct i2c_client *client) pm_runtime_set_suspended(imx355->dev); } + pm_runtime_dont_use_autosuspend(imx355->dev); + mutex_destroy(&imx355->mutex); } From 08d620ac8cbd23b51e40b1ba120e94577e12539d Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 29 Apr 2026 12:26:21 +0100 Subject: [PATCH 13/20] media: imx355: Convert to new CCI register access helpers Use the new comon CCI register access helpers to replace the private register access helpers in the imx355 driver. Signed-off-by: Dave Stevenson --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/imx355.c | 500 +++++++++++++++---------------------- 2 files changed, 196 insertions(+), 305 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 275f2eaef70e00..32f0c5793e2ab3 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -281,6 +281,7 @@ config VIDEO_IMX335 config VIDEO_IMX355 tristate "Sony IMX355 sensor support" + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX355 camera. diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 17a2acc1152df1..52bdfe63623107 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -9,76 +9,78 @@ #include #include #include +#include #include #include +#include #include #include #include #include -#define IMX355_REG_MODE_SELECT 0x0100 +#define IMX355_REG_MODE_SELECT CCI_REG8(0x0100) #define IMX355_MODE_STANDBY 0x00 #define IMX355_MODE_STREAMING 0x01 /* Chip ID */ -#define IMX355_REG_CHIP_ID 0x0016 +#define IMX355_REG_CHIP_ID CCI_REG16(0x0016) #define IMX355_CHIP_ID 0x0355 /* PLL registers that depend on the external clock frequency */ -#define IMX355_REG_EXTCLK_FREQ 0x0136 -#define IMX355_REG_PLL_OP_MUL 0x030e +#define IMX355_REG_EXTCLK_FREQ CCI_REG16(0x0136) +#define IMX355_REG_PLL_OP_MUL CCI_REG16(0x030e) /* V_TIMING internal */ -#define IMX355_REG_FLL 0x0340 +#define IMX355_REG_FLL CCI_REG16(0x0340) #define IMX355_FLL_MAX 0xffff #define IMX355_VBLANK_MIN 20 -#define IMX355_REG_LLP 0x0342 +#define IMX355_REG_LLP CCI_REG16(0x0342) #define IMX355_LLP_MAX 0xffff -#define IMX355_REG_X_ADD_START 0x0344 -#define IMX355_REG_Y_ADD_START 0x0346 -#define IMX355_REG_X_ADD_END 0x0348 -#define IMX355_REG_Y_ADD_END 0x034a -#define IMX355_REG_X_OUT_SIZE 0x034c -#define IMX355_REG_Y_OUT_SIZE 0x034e +#define IMX355_REG_X_ADD_START CCI_REG16(0x0344) +#define IMX355_REG_Y_ADD_START CCI_REG16(0x0346) +#define IMX355_REG_X_ADD_END CCI_REG16(0x0348) +#define IMX355_REG_Y_ADD_END CCI_REG16(0x034a) +#define IMX355_REG_X_OUT_SIZE CCI_REG16(0x034c) +#define IMX355_REG_Y_OUT_SIZE CCI_REG16(0x034e) /* Exposure control */ -#define IMX355_REG_EXPOSURE 0x0202 +#define IMX355_REG_EXPOSURE CCI_REG16(0x0202) #define IMX355_EXPOSURE_MIN 1 #define IMX355_EXPOSURE_STEP 1 #define IMX355_EXPOSURE_DEFAULT 0x0282 /* Analog gain control */ -#define IMX355_REG_ANALOG_GAIN 0x0204 +#define IMX355_REG_ANALOG_GAIN CCI_REG16(0x0204) #define IMX355_ANA_GAIN_MIN 0 #define IMX355_ANA_GAIN_MAX 960 #define IMX355_ANA_GAIN_STEP 1 #define IMX355_ANA_GAIN_DEFAULT 0 /* Digital gain control */ -#define IMX355_REG_DPGA_USE_GLOBAL_GAIN 0x3070 -#define IMX355_REG_DIG_GAIN_GLOBAL 0x020e +#define IMX355_REG_DPGA_USE_GLOBAL_GAIN CCI_REG8(0x3070) +#define IMX355_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e) #define IMX355_DGTL_GAIN_MIN 256 #define IMX355_DGTL_GAIN_MAX 4095 #define IMX355_DGTL_GAIN_STEP 1 #define IMX355_DGTL_GAIN_DEFAULT 256 /* Test Pattern Control */ -#define IMX355_REG_TEST_PATTERN 0x0600 +#define IMX355_REG_TEST_PATTERN CCI_REG16(0x0600) #define IMX355_TEST_PATTERN_DISABLED 0 #define IMX355_TEST_PATTERN_SOLID_COLOR 1 #define IMX355_TEST_PATTERN_COLOR_BARS 2 #define IMX355_TEST_PATTERN_GRAY_COLOR_BARS 3 #define IMX355_TEST_PATTERN_PN9 4 -#define IMX355_REG_BINNING_MODE 0x0900 -#define IMX355_REG_BINNING_TYPE 0x0901 -#define IMX355_REG_BINNING_WEIGHTING 0x0902 +#define IMX355_REG_BINNING_MODE CCI_REG8(0x0900) +#define IMX355_REG_BINNING_TYPE CCI_REG8(0x0901) +#define IMX355_REG_BINNING_WEIGHTING CCI_REG8(0x0902) /* Flip Control */ -#define IMX355_REG_ORIENTATION 0x0101 +#define IMX355_REG_ORIENTATION CCI_REG8(0x0101) /* default link frequency and external clock */ #define IMX355_LINK_FREQ_DEFAULT 360000000LL @@ -91,14 +93,9 @@ #define IMX355_PIXEL_ARRAY_WIDTH 3280 #define IMX355_PIXEL_ARRAY_HEIGHT 2464 -struct imx355_reg { - u16 address; - u8 val; -}; - struct imx355_reg_list { u32 num_of_regs; - const struct imx355_reg *regs; + const struct cci_reg_sequence *regs; }; /* Mode : resolution and related config&values */ @@ -149,6 +146,7 @@ struct imx355_hwcfg { struct imx355 { struct device *dev; struct clk *clk; + struct regmap *regmap; struct v4l2_subdev sd; struct media_pad pad; @@ -185,155 +183,150 @@ static const struct regulator_bulk_data imx355_supplies[] = { { .supply = "dovdd" }, }; -static const struct imx355_reg imx355_global_regs[] = { - { 0x304e, 0x03 }, - { 0x4348, 0x16 }, - { 0x4350, 0x19 }, - { 0x4408, 0x0a }, - { 0x440c, 0x0b }, - { 0x4411, 0x5f }, - { 0x4412, 0x2c }, - { 0x4623, 0x00 }, - { 0x462c, 0x0f }, - { 0x462d, 0x00 }, - { 0x462e, 0x00 }, - { 0x4684, 0x54 }, - { 0x480a, 0x07 }, - { 0x4908, 0x07 }, - { 0x4909, 0x07 }, - { 0x490d, 0x0a }, - { 0x491e, 0x0f }, - { 0x4921, 0x06 }, - { 0x4923, 0x28 }, - { 0x4924, 0x28 }, - { 0x4925, 0x29 }, - { 0x4926, 0x29 }, - { 0x4927, 0x1f }, - { 0x4928, 0x20 }, - { 0x4929, 0x20 }, - { 0x492a, 0x20 }, - { 0x492c, 0x05 }, - { 0x492d, 0x06 }, - { 0x492e, 0x06 }, - { 0x492f, 0x06 }, - { 0x4930, 0x03 }, - { 0x4931, 0x04 }, - { 0x4932, 0x04 }, - { 0x4933, 0x05 }, - { 0x595e, 0x01 }, - { 0x5963, 0x01 }, - { 0x3030, 0x01 }, - { 0x3031, 0x01 }, - { 0x3045, 0x01 }, - { 0x4010, 0x00 }, - { 0x4011, 0x00 }, - { 0x4012, 0x00 }, - { 0x4013, 0x01 }, - { 0x68a8, 0xfe }, - { 0x68a9, 0xff }, - { 0x6888, 0x00 }, - { 0x6889, 0x00 }, - { 0x68b0, 0x00 }, - { 0x3058, 0x00 }, - { 0x305a, 0x00 }, - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, +static const struct cci_reg_sequence imx355_global_regs[] = { + { CCI_REG8(0x304e), 0x03 }, + { CCI_REG8(0x4348), 0x16 }, + { CCI_REG8(0x4350), 0x19 }, + { CCI_REG8(0x4408), 0x0a }, + { CCI_REG8(0x440c), 0x0b }, + { CCI_REG8(0x4411), 0x5f }, + { CCI_REG8(0x4412), 0x2c }, + { CCI_REG8(0x4623), 0x00 }, + { CCI_REG8(0x462c), 0x0f }, + { CCI_REG8(0x462d), 0x00 }, + { CCI_REG8(0x462e), 0x00 }, + { CCI_REG8(0x4684), 0x54 }, + { CCI_REG8(0x480a), 0x07 }, + { CCI_REG8(0x4908), 0x07 }, + { CCI_REG8(0x4909), 0x07 }, + { CCI_REG8(0x490d), 0x0a }, + { CCI_REG8(0x491e), 0x0f }, + { CCI_REG8(0x4921), 0x06 }, + { CCI_REG8(0x4923), 0x28 }, + { CCI_REG8(0x4924), 0x28 }, + { CCI_REG8(0x4925), 0x29 }, + { CCI_REG8(0x4926), 0x29 }, + { CCI_REG8(0x4927), 0x1f }, + { CCI_REG8(0x4928), 0x20 }, + { CCI_REG8(0x4929), 0x20 }, + { CCI_REG8(0x492a), 0x20 }, + { CCI_REG8(0x492c), 0x05 }, + { CCI_REG8(0x492d), 0x06 }, + { CCI_REG8(0x492e), 0x06 }, + { CCI_REG8(0x492f), 0x06 }, + { CCI_REG8(0x4930), 0x03 }, + { CCI_REG8(0x4931), 0x04 }, + { CCI_REG8(0x4932), 0x04 }, + { CCI_REG8(0x4933), 0x05 }, + { CCI_REG8(0x595e), 0x01 }, + { CCI_REG8(0x5963), 0x01 }, + { CCI_REG8(0x3030), 0x01 }, + { CCI_REG8(0x3031), 0x01 }, + { CCI_REG8(0x3045), 0x01 }, + { CCI_REG8(0x4010), 0x00 }, + { CCI_REG8(0x4011), 0x00 }, + { CCI_REG8(0x4012), 0x00 }, + { CCI_REG8(0x4013), 0x01 }, + { CCI_REG8(0x68a8), 0xfe }, + { CCI_REG8(0x68a9), 0xff }, + { CCI_REG8(0x6888), 0x00 }, + { CCI_REG8(0x6889), 0x00 }, + { CCI_REG8(0x68b0), 0x00 }, + { CCI_REG8(0x3058), 0x00 }, + { CCI_REG8(0x305a), 0x00 }, + { CCI_REG8(0x0112), 0x0a }, + { CCI_REG8(0x0113), 0x0a }, + { CCI_REG8(0x0114), 0x03 }, + { CCI_REG8(0x0301), 0x05 }, + { CCI_REG8(0x0303), 0x01 }, + { CCI_REG8(0x0305), 0x02 }, + { CCI_REG8(0x0306), 0x00 }, + { CCI_REG8(0x0307), 0x78 }, + { CCI_REG8(0x030b), 0x01 }, + { CCI_REG8(0x030d), 0x02 }, + { CCI_REG8(0x0310), 0x00 }, + { CCI_REG8(0x0220), 0x00 }, + { CCI_REG8(0x0222), 0x01 }, + { CCI_REG8(0x0820), 0x0b }, + { CCI_REG8(0x0821), 0x40 }, + { CCI_REG8(0x3088), 0x04 }, + { CCI_REG8(0x6813), 0x02 }, + { CCI_REG8(0x6835), 0x07 }, + { CCI_REG8(0x6836), 0x01 }, + { CCI_REG8(0x6837), 0x04 }, + { CCI_REG8(0x684d), 0x07 }, + { CCI_REG8(0x684e), 0x01 }, + { CCI_REG8(0x684f), 0x04 }, }; -static const struct imx355_reg_list imx355_global_setting = { - .num_of_regs = ARRAY_SIZE(imx355_global_regs), - .regs = imx355_global_regs, +static const struct cci_reg_sequence mode_3268x2448_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_3268x2448_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_3264x2448_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_3264x2448_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_3280x2464_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_3280x2464_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1940x1096_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1940x1096_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1936x1096_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1936x1096_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1924x1080_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1924x1080_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1920x1080_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1920x1080_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1640x1232_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1640x1232_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1640x922_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1640x922_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1300x736_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1300x736_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1296x736_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1296x736_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1284x720_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1284x720_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1280x720_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1280x720_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, -}; - -static const struct imx355_reg mode_820x616_regs[] = { - { 0x0700, 0x02 }, - { 0x0701, 0x78 }, +static const struct cci_reg_sequence mode_820x616_regs[] = { + { CCI_REG8(0x0700), 0x02 }, + { CCI_REG8(0x0701), 0x78 }, }; static const char * const imx355_test_pattern_menu[] = { @@ -604,78 +597,6 @@ static u32 imx355_get_format_code(struct imx355 *imx355) return code; } -/* Read registers up to 4 at a time */ -static int imx355_read_reg(struct imx355 *imx355, u16 reg, u32 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); - struct i2c_msg msgs[2]; - u8 addr_buf[2]; - u8 data_buf[4] = { 0 }; - int ret; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, addr_buf); - /* Write register address */ - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; - - /* Read data from register */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - return -EIO; - - *val = get_unaligned_be32(data_buf); - - return 0; -} - -/* Write registers up to 4 at a time */ -static int imx355_write_reg(struct imx355 *imx355, u16 reg, u32 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); - u8 buf[6]; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; -} - -/* Write a list of registers */ -static int imx355_write_regs(struct imx355 *imx355, - const struct imx355_reg *regs, u32 len) -{ - int ret; - u32 i; - - for (i = 0; i < len; i++) { - ret = imx355_write_reg(imx355, regs[i].address, 1, regs[i].val); - if (ret) { - dev_err_ratelimited(imx355->dev, - "write reg 0x%4.4x return err %d", - regs[i].address, ret); - - return ret; - } - } - - return 0; -} - /* Open sub-device */ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { @@ -727,31 +648,31 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: /* Analog gain = 1024/(1024 - ctrl->val) times */ - ret = imx355_write_reg(imx355, IMX355_REG_ANALOG_GAIN, 2, - ctrl->val); + ret = cci_write(imx355->regmap, IMX355_REG_ANALOG_GAIN, + ctrl->val, NULL); break; case V4L2_CID_DIGITAL_GAIN: - ret = imx355_write_reg(imx355, IMX355_REG_DIG_GAIN_GLOBAL, 2, - ctrl->val); + ret = cci_write(imx355->regmap, IMX355_REG_DIG_GAIN_GLOBAL, + ctrl->val, NULL); break; case V4L2_CID_EXPOSURE: - ret = imx355_write_reg(imx355, IMX355_REG_EXPOSURE, 2, - ctrl->val); + ret = cci_write(imx355->regmap, IMX355_REG_EXPOSURE, + ctrl->val, NULL); break; case V4L2_CID_VBLANK: /* Update FLL that meets expected vertical blanking */ - ret = imx355_write_reg(imx355, IMX355_REG_FLL, 2, - imx355->cur_mode->height + ctrl->val); + ret = cci_write(imx355->regmap, IMX355_REG_FLL, + imx355->cur_mode->height + ctrl->val, NULL); break; case V4L2_CID_TEST_PATTERN: - ret = imx355_write_reg(imx355, IMX355_REG_TEST_PATTERN, - 2, ctrl->val); + ret = cci_write(imx355->regmap, IMX355_REG_TEST_PATTERN, + ctrl->val, NULL); break; case V4L2_CID_HFLIP: case V4L2_CID_VFLIP: - ret = imx355_write_reg(imx355, IMX355_REG_ORIENTATION, 1, - imx355->hflip->val | - imx355->vflip->val << 1); + ret = cci_write(imx355->regmap, IMX355_REG_ORIENTATION, + imx355->hflip->val | imx355->vflip->val << 1, + NULL); break; default: ret = -EINVAL; @@ -957,100 +878,64 @@ static int imx355_start_streaming(struct imx355 *imx355) const struct imx355_reg_list *reg_list; const struct imx355_mode *mode; u8 binning_mode; - int ret; + int ret = 0; /* Global Setting */ - reg_list = &imx355_global_setting; - ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); - if (ret) { - dev_err(imx355->dev, "failed to set global settings"); - return ret; - } + cci_multi_reg_write(imx355->regmap, imx355_global_regs, + ARRAY_SIZE(imx355_global_regs), &ret); /* Apply default values of current mode */ mode = imx355->cur_mode; reg_list = &mode->reg_list; - ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); - if (ret) { - dev_err(imx355->dev, "failed to set mode"); - return ret; - } + cci_multi_reg_write(imx355->regmap, reg_list->regs, + reg_list->num_of_regs, &ret); /* Set readout crop and size registers */ - ret = imx355_write_reg(imx355, IMX355_REG_X_ADD_START, 2, - mode->crop.left); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_Y_ADD_START, 2, - mode->crop.top); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_X_ADD_END, 2, - mode->crop.width + mode->crop.left - 1); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_Y_ADD_END, 2, - mode->crop.height + mode->crop.top - 1); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_X_OUT_SIZE, 2, - mode->width); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_Y_OUT_SIZE, 2, - mode->height); - if (ret) - return ret; + cci_write(imx355->regmap, IMX355_REG_X_ADD_START, mode->crop.left, + &ret); + cci_write(imx355->regmap, IMX355_REG_Y_ADD_START, mode->crop.top, &ret); + cci_write(imx355->regmap, IMX355_REG_X_ADD_END, + mode->crop.width + mode->crop.left - 1, &ret); + cci_write(imx355->regmap, IMX355_REG_Y_ADD_END, + mode->crop.height + mode->crop.top - 1, &ret); + cci_write(imx355->regmap, IMX355_REG_X_OUT_SIZE, mode->width, &ret); + cci_write(imx355->regmap, IMX355_REG_Y_OUT_SIZE, mode->height, &ret); binning_mode = ((mode->crop.width / mode->width) << 4) | (mode->crop.height / mode->height); - ret = imx355_write_reg(imx355, IMX355_REG_BINNING_MODE, 1, - binning_mode == 0x11 ? 0x00 : 0x01); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_BINNING_TYPE, 1, - binning_mode); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_BINNING_WEIGHTING, 1, 0x00); - if (ret) - return ret; + cci_write(imx355->regmap, IMX355_REG_BINNING_MODE, + binning_mode == 0x11 ? 0x00 : 0x01, &ret); + cci_write(imx355->regmap, IMX355_REG_BINNING_TYPE, binning_mode, &ret); + cci_write(imx355->regmap, IMX355_REG_BINNING_WEIGHTING, 0x00, &ret); /* Set PLL registers for the external clock frequency */ - ret = imx355_write_reg(imx355, IMX355_REG_EXTCLK_FREQ, 2, - imx355->clk_params->extclk_freq); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_PLL_OP_MUL, 2, - imx355->clk_params->pll_op_mpy); - if (ret) - return ret; + cci_write(imx355->regmap, IMX355_REG_EXTCLK_FREQ, + imx355->clk_params->extclk_freq, &ret); + cci_write(imx355->regmap, IMX355_REG_PLL_OP_MUL, + imx355->clk_params->pll_op_mpy, &ret); /* set digital gain control to all color mode */ - ret = imx355_write_reg(imx355, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, 1); - if (ret) - return ret; + cci_write(imx355->regmap, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, &ret); /* set line length */ - ret = imx355_write_reg(imx355, IMX355_REG_LLP, 2, - imx355->hblank->val + imx355->cur_mode->width); - if (ret) - return ret; + cci_write(imx355->regmap, IMX355_REG_LLP, + imx355->hblank->val + imx355->cur_mode->width, &ret); /* Apply customized values from user */ - ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); - if (ret) - return ret; + if (!ret) + ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); + + cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, IMX355_MODE_STREAMING, + &ret); - return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, - 1, IMX355_MODE_STREAMING); + return ret; } /* Stop streaming */ static int imx355_stop_streaming(struct imx355 *imx355) { - return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, - 1, IMX355_MODE_STANDBY); + return cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, + IMX355_MODE_STANDBY, NULL); } static int imx355_set_stream(struct v4l2_subdev *sd, int enable) @@ -1097,14 +982,14 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) static int imx355_identify_module(struct imx355 *imx355) { int ret; - u32 val; + u64 val; - ret = imx355_read_reg(imx355, IMX355_REG_CHIP_ID, 2, &val); + ret = cci_read(imx355->regmap, IMX355_REG_CHIP_ID, &val, NULL); if (ret) return ret; if (val != IMX355_CHIP_ID) { - dev_err(imx355->dev, "chip id mismatch: %x!=%x", + dev_err(imx355->dev, "chip id mismatch: %x!=%llx", IMX355_CHIP_ID, val); return -EIO; } @@ -1351,6 +1236,11 @@ static int imx355_probe(struct i2c_client *client) mutex_init(&imx355->mutex); + imx355->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx355->regmap)) + return dev_err_probe(imx355->dev, PTR_ERR(imx355->regmap), + "Unable to initialize I2C\n"); + imx355->clk = devm_v4l2_sensor_clk_get(imx355->dev, NULL); if (IS_ERR(imx355->clk)) return dev_err_probe(imx355->dev, PTR_ERR(imx355->clk), From 3e2c65a08f87cc4b764f03430e05969a13b49f7a Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 11 May 2026 19:01:59 +0100 Subject: [PATCH 14/20] media: imx355: Set the colorspace in the format The colorspace fields were left set to 0 when they should be set appropriately for a raw image sensor. Add those values. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 52bdfe63623107..fa8a6851b44efb 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -612,6 +612,10 @@ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt->height = imx355->cur_mode->height; try_fmt->code = imx355_get_format_code(imx355); try_fmt->field = V4L2_FIELD_NONE; + try_fmt->colorspace = V4L2_COLORSPACE_RAW; + try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + try_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + try_fmt->xfer_func = V4L2_XFER_FUNC_NONE; *crop = imx355->cur_mode->crop; @@ -738,6 +742,10 @@ static void imx355_update_pad_format(struct imx355 *imx355, fmt->format.height = mode->height; fmt->format.code = imx355_get_format_code(imx355); fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } static int imx355_do_get_pad_format(struct imx355 *imx355, From 5cc8266170658abdb0d612c9084488c8aa7bc870 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 12 May 2026 17:00:22 +0100 Subject: [PATCH 15/20] media: imx355: Define the exposure offset, and use that define The difference between frame length and max exposure time in lines is 10. That had been used as a bare value in calculations. Move it to a define, and use that define. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index fa8a6851b44efb..cc27a193cd8156 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -51,6 +51,7 @@ #define IMX355_EXPOSURE_MIN 1 #define IMX355_EXPOSURE_STEP 1 #define IMX355_EXPOSURE_DEFAULT 0x0282 +#define IMX355_EXPOSURE_OFFSET 10 /* Analog gain control */ #define IMX355_REG_ANALOG_GAIN CCI_REG16(0x0204) @@ -635,7 +636,7 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - max = imx355->cur_mode->height + ctrl->val - 10; + max = imx355->cur_mode->height + ctrl->val - IMX355_EXPOSURE_OFFSET; __v4l2_ctrl_modify_range(imx355->exposure, imx355->exposure->minimum, max, imx355->exposure->step, max); @@ -1130,7 +1131,7 @@ static int imx355_init_controls(struct imx355 *imx355) imx355->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* fll >= exposure time + adjust parameter (default value is 10) */ - exposure_max = mode->fll_def - 10; + exposure_max = mode->fll_def - IMX355_EXPOSURE_OFFSET; imx355->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_EXPOSURE, IMX355_EXPOSURE_MIN, exposure_max, From 4a791e157533c0052f8f742180b11f40b12673ef Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 17 Jun 2026 12:17:50 +0100 Subject: [PATCH 16/20] media: imx355: Use NULL ctrl_ops for HBLANK as it is a read-only control This avoids the set_ctrl handler being called under any circumstances, as it will return an error for the unhandled ctrl. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index cc27a193cd8156..3b3bf41485c291 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1124,9 +1124,8 @@ static int imx355_init_controls(struct imx355 *imx355) 1, vblank_def); hblank = mode->llp - mode->width; - imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, - V4L2_CID_HBLANK, hblank, hblank, - 1, hblank); + imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_HBLANK, + hblank, hblank, 1, hblank); if (imx355->hblank) imx355->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; From 052663272844483808b56b923119da4f0a780a62 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 30 Jun 2026 17:17:04 +0100 Subject: [PATCH 17/20] media: imx355: Compute link frequency from PLL setup In preparation for additional options in the PLL setup, compute the link frequency rather than using a hardcoded value. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 39 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 3b3bf41485c291..d8f8efc0c927aa 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -29,7 +29,11 @@ /* PLL registers that depend on the external clock frequency */ #define IMX355_REG_EXTCLK_FREQ CCI_REG16(0x0136) +#define IMX355_REG_PLL_OP_PREDIV CCI_REG8(0x030d) #define IMX355_REG_PLL_OP_MUL CCI_REG16(0x030e) +#define IMX355_REG_PLL_IVT_PCK_DIV CCI_REG8(0x0301) +#define IMX355_PLL_OP_PREDIV 2 +#define IMX355_PLL_IVT_PCK_DIV 5 /* V_TIMING internal */ #define IMX355_REG_FLL CCI_REG16(0x0340) @@ -83,9 +87,6 @@ /* Flip Control */ #define IMX355_REG_ORIENTATION CCI_REG8(0x0101) -/* default link frequency and external clock */ -#define IMX355_LINK_FREQ_DEFAULT 360000000LL - /* number of data lanes */ #define IMX355_DATA_LANES 4 @@ -141,6 +142,7 @@ static const struct imx355_clk_params imx355_clk_params[] = { }; struct imx355_hwcfg { + s64 link_freq_menu; unsigned long link_freq_bitmap; }; @@ -238,13 +240,13 @@ static const struct cci_reg_sequence imx355_global_regs[] = { { CCI_REG8(0x0112), 0x0a }, { CCI_REG8(0x0113), 0x0a }, { CCI_REG8(0x0114), 0x03 }, - { CCI_REG8(0x0301), 0x05 }, + { IMX355_REG_PLL_IVT_PCK_DIV, IMX355_PLL_IVT_PCK_DIV }, { CCI_REG8(0x0303), 0x01 }, { CCI_REG8(0x0305), 0x02 }, { CCI_REG8(0x0306), 0x00 }, { CCI_REG8(0x0307), 0x78 }, { CCI_REG8(0x030b), 0x01 }, - { CCI_REG8(0x030d), 0x02 }, + { IMX355_REG_PLL_OP_PREDIV, IMX355_PLL_OP_PREDIV }, { CCI_REG8(0x0310), 0x00 }, { CCI_REG8(0x0220), 0x00 }, { CCI_REG8(0x0222), 0x01 }, @@ -338,14 +340,6 @@ static const char * const imx355_test_pattern_menu[] = { "Pseudorandom Sequence (PN9)", }; -/* - * When adding more than the one below, make sure the disallowed ones will - * actually be disabled in the LINK_FREQ control. - */ -static const s64 link_freq_menu_items[] = { - IMX355_LINK_FREQ_DEFAULT, -}; - /* Mode configs */ static const struct imx355_mode supported_modes[] = { { @@ -1092,7 +1086,6 @@ static int imx355_init_controls(struct imx355 *imx355) s64 hblank; u64 pixel_rate; const struct imx355_mode *mode; - u32 max; int ret; ctrl_hdlr = &imx355->ctrl_handler; @@ -1101,15 +1094,14 @@ static int imx355_init_controls(struct imx355 *imx355) return ret; ctrl_hdlr->lock = &imx355->mutex; - max = ARRAY_SIZE(link_freq_menu_items) - 1; imx355->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx355_ctrl_ops, - V4L2_CID_LINK_FREQ, max, 0, - link_freq_menu_items); + V4L2_CID_LINK_FREQ, 0, 0, + &imx355->hwcfg->link_freq_menu); if (imx355->link_freq) imx355->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; + pixel_rate = imx355->hwcfg->link_freq_menu * 2 * 4; do_div(pixel_rate, 10); /* By default, PIXEL_RATE is read only */ v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1184,12 +1176,14 @@ static int imx355_init_controls(struct imx355 *imx355) return ret; } -static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) +static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) { + struct device *dev = imx355->dev; struct imx355_hwcfg *cfg; struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + const struct imx355_clk_params *clk = imx355->clk_params; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); int ret; @@ -1212,10 +1206,11 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX355_DATA_LANES) goto out_err; + cfg->link_freq_menu = (clk->ext_clk * clk->pll_op_mpy) / + (IMX355_PLL_OP_PREDIV * 2); ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, bus_cfg.nr_of_link_frequencies, - link_freq_menu_items, - ARRAY_SIZE(link_freq_menu_items), + &cfg->link_freq_menu, 1, &cfg->link_freq_bitmap); if (ret) goto out_err; @@ -1286,7 +1281,7 @@ static int imx355_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); - imx355->hwcfg = imx355_get_hwcfg(imx355->dev); + imx355->hwcfg = imx355_get_hwcfg(imx355); if (!imx355->hwcfg) { dev_err(imx355->dev, "failed to get hwcfg"); ret = -ENODEV; From 14862282feefaa69df95e18c2c6e9b7a1daabfe7 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Apr 2026 16:22:08 +0100 Subject: [PATCH 18/20] media: imx355: Support 2 lane readout The sensor supports 2 or 4 lane readout, but the driver only allowed for 4 lanes. Add 2 lane support. The clock tree only supports single PLL mode to feed both IOP (MIPI) and IVT (Pixel array). 2 lane mode supports a MIPI link frequency of up to 445MHz (890Mbit/s) cf 360MHz (720Mbit/s) for 4lane. Update clock setup and pixel rates to match. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 67 +++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index d8f8efc0c927aa..0462e0feee3622 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -27,11 +27,14 @@ #define IMX355_REG_CHIP_ID CCI_REG16(0x0016) #define IMX355_CHIP_ID 0x0355 +#define IMX355_REG_LANE_SEL CCI_REG8(0x0114) + /* PLL registers that depend on the external clock frequency */ #define IMX355_REG_EXTCLK_FREQ CCI_REG16(0x0136) #define IMX355_REG_PLL_OP_PREDIV CCI_REG8(0x030d) #define IMX355_REG_PLL_OP_MUL CCI_REG16(0x030e) #define IMX355_REG_PLL_IVT_PCK_DIV CCI_REG8(0x0301) +#define IMX355_REG_PLL_IVT_SYSCK_DIV CCI_REG8(0x0303) #define IMX355_PLL_OP_PREDIV 2 #define IMX355_PLL_IVT_PCK_DIV 5 @@ -80,6 +83,8 @@ #define IMX355_TEST_PATTERN_GRAY_COLOR_BARS 3 #define IMX355_TEST_PATTERN_PN9 4 +#define IMX355_REG_REQ_LINK_BIT_RATE CCI_REG16(0x0820) + #define IMX355_REG_BINNING_MODE CCI_REG8(0x0900) #define IMX355_REG_BINNING_TYPE CCI_REG8(0x0901) #define IMX355_REG_BINNING_WEIGHTING CCI_REG8(0x0902) @@ -87,9 +92,6 @@ /* Flip Control */ #define IMX355_REG_ORIENTATION CCI_REG8(0x0101) -/* number of data lanes */ -#define IMX355_DATA_LANES 4 - #define IMX355_PIXEL_ARRAY_TOP 0 #define IMX355_PIXEL_ARRAY_LEFT 0 #define IMX355_PIXEL_ARRAY_WIDTH 3280 @@ -120,30 +122,38 @@ struct imx355_mode { struct imx355_clk_params { u32 ext_clk; - u16 extclk_freq; /* External clock (MHz) in 8.8 fixed point) */ - u16 pll_op_mpy; /* OP system PLL multiplier */ + u16 extclk_freq; /* External clock (MHz) in 8.8 fixed point) */ + u16 pll_op_mpy[2]; /* OP system PLL multiplier */ + u8 pll_op_prediv[2]; /* OP system pre PLL d */ }; /* * The clock tree is in single PLL mode, so PREDIV_VT and MPY_IVT do nothing. - * All modes use the same PLL setup for OP, with IOPCK being 720MHz. + * In 4 lane mode the MIPI rate is 360Mhz (720Mbit/s) and pixel rate is + * 288MPix/s. + * In 2 lane mode the MIPI rate is 444MHz (888Mbit/s) and pixel rate + * 177.6MPix/s with a 24MHz clock, and 441.6MHz (883.2Mbit/s) and 176.6MPix/s + * with a 19.2MHz clock. */ static const struct imx355_clk_params imx355_clk_params[] = { { .ext_clk = 19200000, - .extclk_freq = 0x1333, /* 19.2 MHz */ - .pll_op_mpy = 75, /* 19.2 / 2 * 75 = 720 MHz */ + .extclk_freq = 0x1333, + .pll_op_mpy = { 75, 92 }, + .pll_op_prediv = { 2, 2 } }, { .ext_clk = 24000000, - .extclk_freq = 0x1800, /* 24.0 MHz */ - .pll_op_mpy = 60, /* 24.0 / 2 * 60 = 720 MHz */ + .extclk_freq = 0x1800, + .pll_op_mpy = { 60, 111 }, + .pll_op_prediv = { 2, 3 } }, }; struct imx355_hwcfg { s64 link_freq_menu; unsigned long link_freq_bitmap; + unsigned int num_lanes; }; struct imx355 { @@ -239,7 +249,6 @@ static const struct cci_reg_sequence imx355_global_regs[] = { { CCI_REG8(0x305a), 0x00 }, { CCI_REG8(0x0112), 0x0a }, { CCI_REG8(0x0113), 0x0a }, - { CCI_REG8(0x0114), 0x03 }, { IMX355_REG_PLL_IVT_PCK_DIV, IMX355_PLL_IVT_PCK_DIV }, { CCI_REG8(0x0303), 0x01 }, { CCI_REG8(0x0305), 0x02 }, @@ -250,8 +259,6 @@ static const struct cci_reg_sequence imx355_global_regs[] = { { CCI_REG8(0x0310), 0x00 }, { CCI_REG8(0x0220), 0x00 }, { CCI_REG8(0x0222), 0x01 }, - { CCI_REG8(0x0820), 0x0b }, - { CCI_REG8(0x0821), 0x40 }, { CCI_REG8(0x3088), 0x04 }, { CCI_REG8(0x6813), 0x02 }, { CCI_REG8(0x6835), 0x07 }, @@ -817,6 +824,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, __v4l2_ctrl_modify_range(imx355->vblank, IMX355_VBLANK_MIN, height, 1, vblank_def); __v4l2_ctrl_s_ctrl(imx355->vblank, vblank_def); + h_blank = mode->llp - imx355->cur_mode->width; /* * Currently hblank is not changeable. @@ -880,6 +888,8 @@ static int imx355_start_streaming(struct imx355 *imx355) { const struct imx355_reg_list *reg_list; const struct imx355_mode *mode; + int lane_idx = imx355->hwcfg->num_lanes == 4 ? 0 : 1; + u64 link_bitrate; u8 binning_mode; int ret = 0; @@ -915,7 +925,21 @@ static int imx355_start_streaming(struct imx355 *imx355) cci_write(imx355->regmap, IMX355_REG_EXTCLK_FREQ, imx355->clk_params->extclk_freq, &ret); cci_write(imx355->regmap, IMX355_REG_PLL_OP_MUL, - imx355->clk_params->pll_op_mpy, &ret); + imx355->clk_params->pll_op_mpy[lane_idx], &ret); + cci_write(imx355->regmap, IMX355_REG_PLL_OP_PREDIV, + imx355->clk_params->pll_op_prediv[lane_idx], &ret); + cci_write(imx355->regmap, IMX355_REG_PLL_IVT_SYSCK_DIV, + lane_idx ? 2 : 1, &ret); + + /* Set MIPI configuration */ + cci_write(imx355->regmap, IMX355_REG_LANE_SEL, + imx355->hwcfg->num_lanes - 1, &ret); + + link_bitrate = imx355->link_freq->qmenu_int[imx355->link_freq->val] * + imx355->hwcfg->num_lanes * 2; + do_div(link_bitrate, 1000000); + cci_write(imx355->regmap, IMX355_REG_REQ_LINK_BIT_RATE, link_bitrate, + &ret); /* set digital gain control to all color mode */ cci_write(imx355->regmap, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, &ret); @@ -1101,9 +1125,9 @@ static int imx355_init_controls(struct imx355 *imx355) imx355->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - pixel_rate = imx355->hwcfg->link_freq_menu * 2 * 4; + pixel_rate = imx355->hwcfg->link_freq_menu * 2 * imx355->hwcfg->num_lanes; do_div(pixel_rate, 10); - /* By default, PIXEL_RATE is read only */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_PIXEL_RATE, pixel_rate, pixel_rate, 1, pixel_rate); @@ -1186,6 +1210,7 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) const struct imx355_clk_params *clk = imx355->clk_params; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); + int lane_idx; int ret; if (!fwnode) @@ -1203,11 +1228,15 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) if (!cfg) goto out_err; - if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX355_DATA_LANES) + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 && + bus_cfg.bus.mipi_csi2.num_data_lanes != 4) goto out_err; - cfg->link_freq_menu = (clk->ext_clk * clk->pll_op_mpy) / - (IMX355_PLL_OP_PREDIV * 2); + cfg->num_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + + lane_idx = cfg->num_lanes == 4 ? 0 : 1; + cfg->link_freq_menu = (clk->ext_clk * clk->pll_op_mpy[lane_idx]) / + (clk->pll_op_prediv[lane_idx] * 2); ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, bus_cfg.nr_of_link_frequencies, &cfg->link_freq_menu, 1, From a781ab08492196e32e7ae55d6060b5269d523729 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 11 May 2026 18:13:20 +0100 Subject: [PATCH 19/20] media: imx355: Switch to using the subdev state The subdev state is now preferred rather than handling crop and format within the driver state, so switch the driver to using it. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 183 +++++++++++-------------------------- 1 file changed, 55 insertions(+), 128 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 0462e0feee3622..97f4594f7ed9a9 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -179,13 +179,6 @@ struct imx355 { struct imx355_hwcfg *hwcfg; const struct imx355_clk_params *clk_params; - /* - * Mutex for serialized access: - * Protect sensor set pad format and start/stop streaming safely. - * Protect access to sensor v4l2 controls. - */ - struct mutex mutex; - struct gpio_desc *reset_gpio; struct regulator_bulk_data *supplies; }; @@ -593,46 +586,23 @@ static u32 imx355_get_format_code(struct imx355 *imx355) { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, }, }; - lockdep_assert_held(&imx355->mutex); code = codes[imx355->vflip->val][imx355->hflip->val]; return code; } -/* Open sub-device */ -static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx355 *imx355 = to_imx355(sd); - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_state_get_format(fh->state, 0); - struct v4l2_rect *crop = v4l2_subdev_state_get_crop(fh->state, 0); - - mutex_lock(&imx355->mutex); - - /* Initialize try_fmt */ - try_fmt->width = imx355->cur_mode->width; - try_fmt->height = imx355->cur_mode->height; - try_fmt->code = imx355_get_format_code(imx355); - try_fmt->field = V4L2_FIELD_NONE; - try_fmt->colorspace = V4L2_COLORSPACE_RAW; - try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - try_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - try_fmt->xfer_func = V4L2_XFER_FUNC_NONE; - - *crop = imx355->cur_mode->crop; - - mutex_unlock(&imx355->mutex); - - return 0; -} - static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx355 *imx355 = container_of(ctrl->handler, struct imx355, ctrl_handler); + const struct v4l2_mbus_framefmt *format = NULL; + struct v4l2_subdev_state *state; s64 max; int ret; + state = v4l2_subdev_get_locked_active_state(&imx355->sd); + format = v4l2_subdev_state_get_format(state, 0); + /* Propagate change of current control to all related controls */ switch (ctrl->id) { case V4L2_CID_VBLANK: @@ -705,9 +675,7 @@ static int imx355_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - mutex_lock(&imx355->mutex); code->code = imx355_get_format_code(imx355); - mutex_unlock(&imx355->mutex); return 0; } @@ -721,12 +689,9 @@ static int imx355_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - mutex_lock(&imx355->mutex); if (fse->code != imx355_get_format_code(imx355)) { - mutex_unlock(&imx355->mutex); return -EINVAL; } - mutex_unlock(&imx355->mutex); fse->min_width = supported_modes[fse->index].width; fse->max_width = fse->min_width; @@ -750,36 +715,6 @@ static void imx355_update_pad_format(struct imx355 *imx355, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -static int imx355_do_get_pad_format(struct imx355 *imx355, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct v4l2_mbus_framefmt *framefmt; - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); - fmt->format = *framefmt; - } else { - imx355_update_pad_format(imx355, imx355->cur_mode, fmt); - } - - return 0; -} - -static int imx355_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx355 *imx355 = to_imx355(sd); - int ret; - - mutex_lock(&imx355->mutex); - ret = imx355_do_get_pad_format(imx355, sd_state, fmt); - mutex_unlock(&imx355->mutex); - - return ret; -} - static int imx355_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, @@ -793,8 +728,6 @@ imx355_set_pad_format(struct v4l2_subdev *sd, s64 h_blank; u32 height; - mutex_lock(&imx355->mutex); - /* * Only one bayer order is supported. * It depends on the flip settings. @@ -806,16 +739,17 @@ imx355_set_pad_format(struct v4l2_subdev *sd, width, height, fmt->format.width, fmt->format.height); imx355_update_pad_format(imx355, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); - *framefmt = fmt->format; - - crop = v4l2_subdev_state_get_crop(sd_state, 0); - crop->width = mode->crop.width; - crop->height = mode->crop.height; - crop->left = mode->crop.left; - crop->top = mode->crop.top; - } else { + framefmt = v4l2_subdev_state_get_format(sd_state, 0); + + *framefmt = fmt->format; + + crop = v4l2_subdev_state_get_crop(sd_state, 0); + crop->width = mode->crop.width; + crop->height = mode->crop.height; + crop->left = mode->crop.left; + crop->top = mode->crop.top; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { imx355->cur_mode = mode; /* Update limits and set FPS to default */ height = imx355->cur_mode->height; @@ -834,41 +768,17 @@ imx355_set_pad_format(struct v4l2_subdev *sd, h_blank, 1, h_blank); } - mutex_unlock(&imx355->mutex); - return 0; } -static void -__imx355_get_pad_crop(struct imx355 *imx355, - struct v4l2_subdev_state *sd_state, unsigned int pad, - enum v4l2_subdev_format_whence which, struct v4l2_rect *r) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - *r = *v4l2_subdev_state_get_crop(sd_state, pad); - break; - case V4L2_SUBDEV_FORMAT_ACTIVE: - *r = imx355->cur_mode->crop; - break; - } -} - static int imx355_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - struct imx355 *imx355 = to_imx355(sd); - - mutex_lock(&imx355->mutex); - __imx355_get_pad_crop(imx355, sd_state, sel->pad, sel->which, - &sel->r); - mutex_unlock(&imx355->mutex); - + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); return 0; - } case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_NATIVE_SIZE: @@ -883,6 +793,21 @@ static int imx355_get_selection(struct v4l2_subdev *sd, return -EINVAL; } +static int imx355_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { }; + + fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt.format.width = supported_modes[0].width; + fmt.format.height = supported_modes[0].height; + + imx355_set_pad_format(subdev, sd_state, &fmt); + + return 0; +} + /* Start streaming */ static int imx355_start_streaming(struct imx355 *imx355) { @@ -968,9 +893,10 @@ static int imx355_stop_streaming(struct imx355 *imx355) static int imx355_set_stream(struct v4l2_subdev *sd, int enable) { struct imx355 *imx355 = to_imx355(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&imx355->mutex); + state = v4l2_subdev_lock_and_get_active_state(sd); if (enable) { ret = pm_runtime_resume_and_get(imx355->dev); @@ -993,14 +919,14 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) __v4l2_ctrl_grab(imx355->vflip, enable); __v4l2_ctrl_grab(imx355->hflip, enable); - mutex_unlock(&imx355->mutex); + v4l2_subdev_unlock_state(state); return ret; err_rpm_put: pm_runtime_put_autosuspend(imx355->dev); err_unlock: - mutex_unlock(&imx355->mutex); + v4l2_subdev_unlock_state(state); return ret; } @@ -1034,7 +960,7 @@ static const struct v4l2_subdev_video_ops imx355_video_ops = { static const struct v4l2_subdev_pad_ops imx355_pad_ops = { .enum_mbus_code = imx355_enum_mbus_code, - .get_fmt = imx355_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx355_set_pad_format, .enum_frame_size = imx355_enum_frame_size, .get_selection = imx355_get_selection, @@ -1051,7 +977,7 @@ static const struct media_entity_operations imx355_subdev_entity_ops = { }; static const struct v4l2_subdev_internal_ops imx355_internal_ops = { - .open = imx355_open, + .init_state = imx355_entity_init_state, }; static int imx355_power_off(struct device *dev) @@ -1117,7 +1043,6 @@ static int imx355_init_controls(struct imx355 *imx355) if (ret) return ret; - ctrl_hdlr->lock = &imx355->mutex; imx355->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_LINK_FREQ, 0, 0, &imx355->hwcfg->link_freq_menu); @@ -1266,8 +1191,6 @@ static int imx355_probe(struct i2c_client *client) imx355->dev = &client->dev; - mutex_init(&imx355->mutex); - imx355->regmap = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(imx355->regmap)) return dev_err_probe(imx355->dev, PTR_ERR(imx355->regmap), @@ -1296,7 +1219,7 @@ static int imx355_probe(struct i2c_client *client) &imx355->supplies); if (ret) { dev_err_probe(imx355->dev, ret, "could not get regulators"); - goto error_probe; + return ret; } imx355->reset_gpio = devm_gpiod_get_optional(imx355->dev, "reset", @@ -1304,7 +1227,7 @@ static int imx355_probe(struct i2c_client *client) if (IS_ERR(imx355->reset_gpio)) { ret = dev_err_probe(imx355->dev, PTR_ERR(imx355->reset_gpio), "failed to get gpios"); - goto error_probe; + return ret; } /* Initialize subdev */ @@ -1313,13 +1236,12 @@ static int imx355_probe(struct i2c_client *client) imx355->hwcfg = imx355_get_hwcfg(imx355); if (!imx355->hwcfg) { dev_err(imx355->dev, "failed to get hwcfg"); - ret = -ENODEV; - goto error_probe; + return -ENODEV; } ret = imx355_power_on(imx355->dev); if (ret) - goto error_probe; + return ret; /* Check module identity */ ret = imx355_identify_module(imx355); @@ -1352,6 +1274,13 @@ static int imx355_probe(struct i2c_client *client) goto error_handler_free; } + imx355->sd.state_lock = imx355->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx355->sd); + if (ret < 0) { + dev_err_probe(imx355->dev, ret, "subdev init error\n"); + goto error_media_entity_free; + } + /* * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. @@ -1363,16 +1292,18 @@ static int imx355_probe(struct i2c_client *client) ret = v4l2_async_register_subdev_sensor(&imx355->sd); if (ret < 0) - goto error_media_entity_runtime_pm; + goto error_subdev_cleanup_runtime_pm; pm_runtime_idle(imx355->dev); return 0; -error_media_entity_runtime_pm: +error_subdev_cleanup_runtime_pm: pm_runtime_disable(imx355->dev); pm_runtime_set_suspended(imx355->dev); pm_runtime_dont_use_autosuspend(imx355->dev); + v4l2_subdev_cleanup(&imx355->sd); +error_media_entity_free: media_entity_cleanup(&imx355->sd.entity); error_handler_free: @@ -1381,9 +1312,6 @@ static int imx355_probe(struct i2c_client *client) error_power_off: imx355_power_off(imx355->dev); -error_probe: - mutex_destroy(&imx355->mutex); - return ret; } @@ -1393,6 +1321,7 @@ static void imx355_remove(struct i2c_client *client) struct imx355 *imx355 = to_imx355(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); @@ -1404,8 +1333,6 @@ static void imx355_remove(struct i2c_client *client) } pm_runtime_dont_use_autosuspend(imx355->dev); - - mutex_destroy(&imx355->mutex); } static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = { From c4cb4f34f93826f8d48f7e705d7683b666e3aeb1 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 11 May 2026 18:58:48 +0100 Subject: [PATCH 20/20] media: imx355: Remove storing cur_mode in the state All the information for the mode is now stored within the subdev state, so configure the sensor based on that. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 63 ++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 97f4594f7ed9a9..8eb8588cb71bb0 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -173,9 +173,6 @@ struct imx355 { struct v4l2_ctrl *vflip; struct v4l2_ctrl *hflip; - /* Current mode */ - const struct imx355_mode *cur_mode; - struct imx355_hwcfg *hwcfg; const struct imx355_clk_params *clk_params; @@ -607,7 +604,7 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - max = imx355->cur_mode->height + ctrl->val - IMX355_EXPOSURE_OFFSET; + max = format->height + ctrl->val - IMX355_EXPOSURE_OFFSET; __v4l2_ctrl_modify_range(imx355->exposure, imx355->exposure->minimum, max, imx355->exposure->step, max); @@ -638,7 +635,7 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VBLANK: /* Update FLL that meets expected vertical blanking */ ret = cci_write(imx355->regmap, IMX355_REG_FLL, - imx355->cur_mode->height + ctrl->val, NULL); + format->height + ctrl->val, NULL); break; case V4L2_CID_TEST_PATTERN: ret = cci_write(imx355->regmap, IMX355_REG_TEST_PATTERN, @@ -724,9 +721,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, const struct imx355_mode *mode; struct v4l2_mbus_framefmt *framefmt; struct v4l2_rect *crop; - s32 vblank_def; s64 h_blank; - u32 height; /* * Only one bayer order is supported. @@ -750,16 +745,14 @@ imx355_set_pad_format(struct v4l2_subdev *sd, crop->top = mode->crop.top; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - imx355->cur_mode = mode; /* Update limits and set FPS to default */ - height = imx355->cur_mode->height; - vblank_def = imx355->cur_mode->fll_def - height; - height = IMX355_FLL_MAX - height; __v4l2_ctrl_modify_range(imx355->vblank, IMX355_VBLANK_MIN, - height, 1, vblank_def); - __v4l2_ctrl_s_ctrl(imx355->vblank, vblank_def); + IMX355_FLL_MAX - mode->height, 1, + mode->fll_def - mode->height); + __v4l2_ctrl_s_ctrl(imx355->vblank, mode->fll_def - mode->height); + + h_blank = mode->llp - mode->width; - h_blank = mode->llp - imx355->cur_mode->width; /* * Currently hblank is not changeable. * So FPS control is done only by vblank. @@ -811,9 +804,11 @@ static int imx355_entity_init_state(struct v4l2_subdev *subdev, /* Start streaming */ static int imx355_start_streaming(struct imx355 *imx355) { - const struct imx355_reg_list *reg_list; + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; const struct imx355_mode *mode; int lane_idx = imx355->hwcfg->num_lanes == 4 ? 0 : 1; + struct v4l2_rect *crop; u64 link_bitrate; u8 binning_mode; int ret = 0; @@ -822,25 +817,29 @@ static int imx355_start_streaming(struct imx355 *imx355) cci_multi_reg_write(imx355->regmap, imx355_global_regs, ARRAY_SIZE(imx355_global_regs), &ret); - /* Apply default values of current mode */ - mode = imx355->cur_mode; - reg_list = &mode->reg_list; - cci_multi_reg_write(imx355->regmap, reg_list->regs, - reg_list->num_of_regs, &ret); + /* Apply values of current mode */ + state = v4l2_subdev_get_locked_active_state(&imx355->sd); + fmt = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, fmt->width, fmt->height); + cci_multi_reg_write(imx355->regmap, mode->reg_list.regs, + mode->reg_list.num_of_regs, &ret); /* Set readout crop and size registers */ - cci_write(imx355->regmap, IMX355_REG_X_ADD_START, mode->crop.left, + cci_write(imx355->regmap, IMX355_REG_X_ADD_START, crop->left, &ret); - cci_write(imx355->regmap, IMX355_REG_Y_ADD_START, mode->crop.top, &ret); + cci_write(imx355->regmap, IMX355_REG_Y_ADD_START, crop->top, &ret); cci_write(imx355->regmap, IMX355_REG_X_ADD_END, - mode->crop.width + mode->crop.left - 1, &ret); + crop->width + crop->left - 1, &ret); cci_write(imx355->regmap, IMX355_REG_Y_ADD_END, - mode->crop.height + mode->crop.top - 1, &ret); - cci_write(imx355->regmap, IMX355_REG_X_OUT_SIZE, mode->width, &ret); - cci_write(imx355->regmap, IMX355_REG_Y_OUT_SIZE, mode->height, &ret); + crop->height + crop->top - 1, &ret); + cci_write(imx355->regmap, IMX355_REG_X_OUT_SIZE, fmt->width, &ret); + cci_write(imx355->regmap, IMX355_REG_Y_OUT_SIZE, fmt->height, &ret); - binning_mode = ((mode->crop.width / mode->width) << 4) | - (mode->crop.height / mode->height); + binning_mode = ((crop->width / fmt->width) << 4) | + (crop->height / fmt->height); cci_write(imx355->regmap, IMX355_REG_BINNING_MODE, binning_mode == 0x11 ? 0x00 : 0x01, &ret); cci_write(imx355->regmap, IMX355_REG_BINNING_TYPE, binning_mode, &ret); @@ -871,7 +870,7 @@ static int imx355_start_streaming(struct imx355 *imx355) /* set line length */ cci_write(imx355->regmap, IMX355_REG_LLP, - imx355->hblank->val + imx355->cur_mode->width, &ret); + imx355->hblank->val + fmt->width, &ret); /* Apply customized values from user */ if (!ret) @@ -1031,11 +1030,11 @@ static int imx355_init_controls(struct imx355 *imx355) { struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; + const struct imx355_mode *mode = &supported_modes[0]; s64 exposure_max; s64 vblank_def; s64 hblank; u64 pixel_rate; - const struct imx355_mode *mode; int ret; ctrl_hdlr = &imx355->ctrl_handler; @@ -1057,7 +1056,6 @@ static int imx355_init_controls(struct imx355 *imx355) pixel_rate, pixel_rate, 1, pixel_rate); /* Initialize vblank/hblank/exposure parameters based on current mode */ - mode = imx355->cur_mode; vblank_def = mode->fll_def - mode->height; imx355->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_VBLANK, IMX355_VBLANK_MIN, @@ -1250,9 +1248,6 @@ static int imx355_probe(struct i2c_client *client) goto error_power_off; } - /* Set default mode to max resolution */ - imx355->cur_mode = &supported_modes[0]; - ret = imx355_init_controls(imx355); if (ret) { dev_err(imx355->dev, "failed to init controls: %d", ret);