From 56a1b0145ea0c0d61c1fa896e3b30f6e885d0c77 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 15 Sep 2026 14:46:25 +0100 Subject: [PATCH 01/30] media: imx355: Reset to upstream We applied versions of the patches prior to them being merged upstream, and further changes were required. Reset to upstream. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 1619 +++++++++++++++++++++++------------- 1 file changed, 1033 insertions(+), 586 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 8e7110d5855ec..96f368ef44f68 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -3,103 +3,73 @@ #include #include -#include -#include #include #include -#include #include -#include -#include #include -#include #include #include #include #include -#define IMX355_REG_MODE_SELECT CCI_REG8(0x0100) +#define IMX355_REG_MODE_SELECT 0x0100 #define IMX355_MODE_STANDBY 0x00 #define IMX355_MODE_STREAMING 0x01 /* Chip ID */ -#define IMX355_REG_CHIP_ID CCI_REG16(0x0016) +#define IMX355_REG_CHIP_ID 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 - /* V_TIMING internal */ -#define IMX355_REG_FLL CCI_REG16(0x0340) +#define IMX355_REG_FLL 0x0340 #define IMX355_FLL_MAX 0xffff -#define IMX355_VBLANK_MIN 20 - -#define IMX355_REG_LLP CCI_REG16(0x0342) -#define IMX355_LLP_MAX 0xffff - -#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 CCI_REG16(0x0202) +#define IMX355_REG_EXPOSURE 0x0202 #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) +#define IMX355_REG_ANALOG_GAIN 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 CCI_REG8(0x3070) -#define IMX355_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e) +#define IMX355_REG_DPGA_USE_GLOBAL_GAIN 0x3070 +#define IMX355_REG_DIG_GAIN_GLOBAL 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 CCI_REG8(0x0600) +#define IMX355_REG_TEST_PATTERN 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_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) - /* Flip Control */ -#define IMX355_REG_ORIENTATION CCI_REG8(0x0101) +#define IMX355_REG_ORIENTATION 0x0101 -#define IMX355_PIXEL_ARRAY_TOP 0 -#define IMX355_PIXEL_ARRAY_LEFT 0 -#define IMX355_PIXEL_ARRAY_WIDTH 3280 -#define IMX355_PIXEL_ARRAY_HEIGHT 2464 +/* default link frequency and external clock */ +#define IMX355_LINK_FREQ_DEFAULT 360000000LL +#define IMX355_EXT_CLK 19200000 +#define IMX355_LINK_FREQ_INDEX 0 + +struct imx355_reg { + u16 address; + u8 val; +}; struct imx355_reg_list { u32 num_of_regs; - const struct cci_reg_sequence *regs; + const struct imx355_reg *regs; }; /* Mode : resolution and related config&values */ @@ -108,58 +78,28 @@ struct imx355_mode { u32 width; /* Frame height */ u32 height; - struct v4l2_rect crop; /* V-timing */ u32 fll_def; + u32 fll_min; /* H-timing */ u32 llp; + /* index of link frequency */ + u32 link_freq_index; + /* Default register values */ 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[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. - * 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, - .pll_op_mpy = { 75, 92 }, - .pll_op_prediv = { 2, 2 } - }, - { - .ext_clk = 24000000, - .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 { struct device *dev; struct clk *clk; - struct regmap *regmap; struct v4l2_subdev sd; struct media_pad pad; @@ -167,165 +107,770 @@ 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; 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; - struct gpio_desc *reset_gpio; - struct regulator_bulk_data *supplies; + /* + * Mutex for serialized access: + * Protect sensor set pad format and start/stop streaming safely. + * Protect access to sensor v4l2 controls. + */ + struct mutex mutex; }; -static const struct regulator_bulk_data imx355_supplies[] = { - { .supply = "avdd" }, - { .supply = "dvdd" }, - { .supply = "dovdd" }, +static const struct imx355_reg imx355_global_regs[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 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 }, }; -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 }, - { IMX355_REG_PLL_IVT_PCK_DIV, IMX355_PLL_IVT_PCK_DIV }, - { CCI_REG8(0x0303), 0x01 }, - { CCI_REG8(0x0305), 0x02 }, - { CCI_REG8(0x0306), 0x78 }, - { CCI_REG8(0x0307), 0x02 }, - { IMX355_REG_PLL_OP_PREDIV, IMX355_PLL_OP_PREDIV }, - { CCI_REG8(0x0310), 0x00 }, - { CCI_REG8(0x0220), 0x00 }, - { CCI_REG8(0x0222), 0x01 }, - { 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[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x0a }, + { 0x0341, 0x37 }, + { 0x0344, 0x00 }, + { 0x0345, 0x08 }, + { 0x0346, 0x00 }, + { 0x0347, 0x08 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcb }, + { 0x034a, 0x09 }, + { 0x034b, 0x97 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x0c }, + { 0x034d, 0xc4 }, + { 0x034e, 0x09 }, + { 0x034f, 0x90 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_3264x2448_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_3264x2448_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x0a }, + { 0x0341, 0x37 }, + { 0x0344, 0x00 }, + { 0x0345, 0x08 }, + { 0x0346, 0x00 }, + { 0x0347, 0x08 }, + { 0x0348, 0x0c }, + { 0x0349, 0xc7 }, + { 0x034a, 0x09 }, + { 0x034b, 0x97 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x0c }, + { 0x034d, 0xc0 }, + { 0x034e, 0x09 }, + { 0x034f, 0x90 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_3280x2464_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_3280x2464_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x0a }, + { 0x0341, 0x37 }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x09 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x0c }, + { 0x034d, 0xd0 }, + { 0x034e, 0x09 }, + { 0x034f, 0xa0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_1940x1096_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_1940x1096_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x02 }, + { 0x0345, 0xa0 }, + { 0x0346, 0x02 }, + { 0x0347, 0xac }, + { 0x0348, 0x0a }, + { 0x0349, 0x33 }, + { 0x034a, 0x06 }, + { 0x034b, 0xf3 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x07 }, + { 0x034d, 0x94 }, + { 0x034e, 0x04 }, + { 0x034f, 0x48 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_1936x1096_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_1936x1096_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x02 }, + { 0x0345, 0xa0 }, + { 0x0346, 0x02 }, + { 0x0347, 0xac }, + { 0x0348, 0x0a }, + { 0x0349, 0x2f }, + { 0x034a, 0x06 }, + { 0x034b, 0xf3 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x07 }, + { 0x034d, 0x90 }, + { 0x034e, 0x04 }, + { 0x034f, 0x48 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_1924x1080_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_1924x1080_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x02 }, + { 0x0345, 0xa8 }, + { 0x0346, 0x02 }, + { 0x0347, 0xb4 }, + { 0x0348, 0x0a }, + { 0x0349, 0x2b }, + { 0x034a, 0x06 }, + { 0x034b, 0xeb }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x07 }, + { 0x034d, 0x84 }, + { 0x034e, 0x04 }, + { 0x034f, 0x38 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_1920x1080_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_1920x1080_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x02 }, + { 0x0345, 0xa8 }, + { 0x0346, 0x02 }, + { 0x0347, 0xb4 }, + { 0x0348, 0x0a }, + { 0x0349, 0x27 }, + { 0x034a, 0x06 }, + { 0x034b, 0xeb }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x07 }, + { 0x034d, 0x80 }, + { 0x034e, 0x04 }, + { 0x034f, 0x38 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_1640x1232_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_1640x1232_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x09 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x06 }, + { 0x034d, 0x68 }, + { 0x034e, 0x04 }, + { 0x034f, 0xd0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_1640x922_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_1640x922_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x01 }, + { 0x0347, 0x30 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x08 }, + { 0x034b, 0x63 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x06 }, + { 0x034d, 0x68 }, + { 0x034e, 0x03 }, + { 0x034f, 0x9a }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_1300x736_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_1300x736_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x01 }, + { 0x0345, 0x58 }, + { 0x0346, 0x01 }, + { 0x0347, 0xf0 }, + { 0x0348, 0x0b }, + { 0x0349, 0x7f }, + { 0x034a, 0x07 }, + { 0x034b, 0xaf }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x05 }, + { 0x034d, 0x14 }, + { 0x034e, 0x02 }, + { 0x034f, 0xe0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_1296x736_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_1296x736_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x01 }, + { 0x0345, 0x58 }, + { 0x0346, 0x01 }, + { 0x0347, 0xf0 }, + { 0x0348, 0x0b }, + { 0x0349, 0x77 }, + { 0x034a, 0x07 }, + { 0x034b, 0xaf }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x05 }, + { 0x034d, 0x10 }, + { 0x034e, 0x02 }, + { 0x034f, 0xe0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_1284x720_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_1284x720_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x01 }, + { 0x0345, 0x68 }, + { 0x0346, 0x02 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0b }, + { 0x0349, 0x6f }, + { 0x034a, 0x07 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x05 }, + { 0x034d, 0x04 }, + { 0x034e, 0x02 }, + { 0x034f, 0xd0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_1280x720_regs[] = { - { CCI_REG8(0x0700), 0x00 }, - { CCI_REG8(0x0701), 0x10 }, +static const struct imx355_reg mode_1280x720_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x01 }, + { 0x0345, 0x68 }, + { 0x0346, 0x02 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0b }, + { 0x0349, 0x67 }, + { 0x034a, 0x07 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x05 }, + { 0x034d, 0x00 }, + { 0x034e, 0x02 }, + { 0x034f, 0xd0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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 cci_reg_sequence mode_820x616_regs[] = { - { CCI_REG8(0x0700), 0x02 }, - { CCI_REG8(0x0701), 0x78 }, +static const struct imx355_reg mode_820x616_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x02 }, + { 0x0341, 0x8c }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x09 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x44 }, + { 0x0902, 0x00 }, + { 0x034c, 0x03 }, + { 0x034d, 0x34 }, + { 0x034e, 0x02 }, + { 0x034f, 0x68 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 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[] = { @@ -336,19 +881,23 @@ 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[] = { { .width = 3280, .height = 2464, - .crop = { - .width = 3280, - .height = 2464, - .left = 0, - .top = 0, - }, .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, @@ -357,14 +906,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3268x2448_regs), .regs = mode_3268x2448_regs, @@ -373,14 +918,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), .regs = mode_3264x2448_regs, @@ -389,14 +930,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1940x1096_regs), .regs = mode_1940x1096_regs, @@ -405,14 +942,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1936x1096_regs), .regs = mode_1936x1096_regs, @@ -421,14 +954,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1924x1080_regs), .regs = mode_1924x1080_regs, @@ -437,14 +966,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), .regs = mode_1920x1080_regs, @@ -453,14 +978,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), .regs = mode_1640x1232_regs, @@ -469,14 +990,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640x922_regs), .regs = mode_1640x922_regs, @@ -485,14 +1002,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1300x736_regs), .regs = mode_1300x736_regs, @@ -501,14 +1014,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1296x736_regs), .regs = mode_1296x736_regs, @@ -517,14 +1026,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1284x720_regs), .regs = mode_1284x720_regs, @@ -533,14 +1038,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), .regs = mode_1280x720_regs, @@ -549,14 +1050,10 @@ 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, + .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_820x616_regs), .regs = mode_820x616_regs, @@ -582,28 +1079,116 @@ 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; } +/* 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) +{ + struct imx355 *imx355 = to_imx355(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_state_get_format(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; + + 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: /* Update max exposure while meeting expected vblanking */ - max = format->height + ctrl->val - IMX355_EXPOSURE_OFFSET; + max = imx355->cur_mode->height + ctrl->val - 10; __v4l2_ctrl_modify_range(imx355->exposure, imx355->exposure->minimum, max, imx355->exposure->step, max); @@ -620,36 +1205,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 = cci_write(imx355->regmap, IMX355_REG_ANALOG_GAIN, - ctrl->val, NULL); + ret = imx355_write_reg(imx355, IMX355_REG_ANALOG_GAIN, 2, + ctrl->val); break; case V4L2_CID_DIGITAL_GAIN: - ret = cci_write(imx355->regmap, IMX355_REG_DIG_GAIN_GLOBAL, - ctrl->val, NULL); + ret = imx355_write_reg(imx355, IMX355_REG_DIG_GAIN_GLOBAL, 2, + ctrl->val); break; case V4L2_CID_EXPOSURE: - ret = cci_write(imx355->regmap, IMX355_REG_EXPOSURE, - ctrl->val, NULL); + ret = imx355_write_reg(imx355, IMX355_REG_EXPOSURE, 2, + ctrl->val); break; case V4L2_CID_VBLANK: /* Update FLL that meets expected vertical blanking */ - ret = cci_write(imx355->regmap, IMX355_REG_FLL, - format->height + ctrl->val, NULL); - break; - case V4L2_CID_HBLANK: - ret = cci_write(imx355->regmap, IMX355_REG_LLP, - format->width + ctrl->val, - NULL); + ret = imx355_write_reg(imx355, IMX355_REG_FLL, 2, + imx355->cur_mode->height + ctrl->val); break; case V4L2_CID_TEST_PATTERN: - ret = cci_write(imx355->regmap, IMX355_REG_TEST_PATTERN, - ctrl->val, NULL); + ret = imx355_write_reg(imx355, IMX355_REG_TEST_PATTERN, + 2, ctrl->val); break; case V4L2_CID_HFLIP: case V4L2_CID_VFLIP: - ret = cci_write(imx355->regmap, IMX355_REG_ORIENTATION, - imx355->hflip->val | imx355->vflip->val << 1, - NULL); + ret = imx355_write_reg(imx355, IMX355_REG_ORIENTATION, 1, + imx355->hflip->val | + imx355->vflip->val << 1); break; default: ret = -EINVAL; @@ -676,7 +1256,9 @@ 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; } @@ -690,9 +1272,12 @@ 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; @@ -710,10 +1295,36 @@ 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, + 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 @@ -724,8 +1335,13 @@ 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; + s32 vblank_min; s64 h_blank; + u64 pixel_rate; + u32 height; + + mutex_lock(&imx355->mutex); /* * Only one bayer order is supported. @@ -738,68 +1354,32 @@ imx355_set_pad_format(struct v4l2_subdev *sd, width, height, fmt->format.width, fmt->format.height); imx355_update_pad_format(imx355, mode, fmt); - - 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) { + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); + *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 */ - __v4l2_ctrl_modify_range(imx355->vblank, IMX355_VBLANK_MIN, - 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; - + 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_s_ctrl(imx355->vblank, vblank_def); + h_blank = mode->llp - imx355->cur_mode->width; + /* + * Currently hblank is not changeable. + * So FPS control is done only by vblank. + */ __v4l2_ctrl_modify_range(imx355->hblank, h_blank, - IMX355_LLP_MAX - mode->width, 1, - h_blank); - __v4l2_ctrl_s_ctrl(imx355->hblank, h_blank); - } - - return 0; -} - -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: - 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: - 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; + h_blank, 1, h_blank); } - 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); + mutex_unlock(&imx355->mutex); return 0; } @@ -807,85 +1387,44 @@ static int imx355_entity_init_state(struct v4l2_subdev *subdev, /* Start streaming */ static int imx355_start_streaming(struct imx355 *imx355) { - 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; + const struct imx355_reg_list *reg_list; + int ret; /* Global Setting */ - cci_multi_reg_write(imx355->regmap, imx355_global_regs, - ARRAY_SIZE(imx355_global_regs), &ret); + 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; + } - /* 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, crop->left, - &ret); - cci_write(imx355->regmap, IMX355_REG_Y_ADD_START, crop->top, &ret); - cci_write(imx355->regmap, IMX355_REG_X_ADD_END, - crop->width + crop->left - 1, &ret); - cci_write(imx355->regmap, IMX355_REG_Y_ADD_END, - 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 = ((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); - cci_write(imx355->regmap, IMX355_REG_BINNING_WEIGHTING, 0x00, &ret); - - /* Set PLL registers for the external clock frequency */ - 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[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); + /* Apply default values of current mode */ + reg_list = &imx355->cur_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 digital gain control to all color mode */ - cci_write(imx355->regmap, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, &ret); + ret = imx355_write_reg(imx355, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, 1); + if (ret) + return ret; /* Apply customized values from user */ - if (!ret) - ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); - - cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, IMX355_MODE_STREAMING, - &ret); + ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); + if (ret) + return ret; - return ret; + return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, + 1, IMX355_MODE_STREAMING); } /* Stop streaming */ static int imx355_stop_streaming(struct imx355 *imx355) { - return cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, - IMX355_MODE_STANDBY, NULL); + return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, + 1, IMX355_MODE_STANDBY); } static int imx355_set_stream(struct v4l2_subdev *sd, int enable) @@ -893,10 +1432,12 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) struct imx355 *imx355 = to_imx355(sd); int ret = 0; + mutex_lock(&imx355->mutex); + if (enable) { ret = pm_runtime_resume_and_get(imx355->dev); if (ret < 0) - return ret; + goto err_unlock; /* * Apply default & customized values @@ -907,17 +1448,21 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) goto err_rpm_put; } else { imx355_stop_streaming(imx355); - pm_runtime_put_autosuspend(imx355->dev); + pm_runtime_put(imx355->dev); } /* vflip and hflip cannot change during streaming */ __v4l2_ctrl_grab(imx355->vflip, enable); __v4l2_ctrl_grab(imx355->hflip, enable); + mutex_unlock(&imx355->mutex); + return ret; err_rpm_put: - pm_runtime_put_autosuspend(imx355->dev); + pm_runtime_put(imx355->dev); +err_unlock: + mutex_unlock(&imx355->mutex); return ret; } @@ -926,14 +1471,14 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) static int imx355_identify_module(struct imx355 *imx355) { int ret; - u64 val; + u32 val; - ret = cci_read(imx355->regmap, IMX355_REG_CHIP_ID, &val, NULL); + ret = imx355_read_reg(imx355, IMX355_REG_CHIP_ID, 2, &val); if (ret) return ret; if (val != IMX355_CHIP_ID) { - dev_err(imx355->dev, "chip id mismatch: %x!=%llx", + dev_err(imx355->dev, "chip id mismatch: %x!=%x", IMX355_CHIP_ID, val); return -EIO; } @@ -951,10 +1496,9 @@ 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 = v4l2_subdev_get_fmt, + .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 = { @@ -968,65 +1512,21 @@ static const struct media_entity_operations imx355_subdev_entity_ops = { }; static const struct v4l2_subdev_internal_ops imx355_internal_ops = { - .init_state = imx355_entity_init_state, + .open = imx355_open, }; -static int imx355_power_off(struct device *dev) -{ - struct i2c_client *client = container_of(dev, struct i2c_client, dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx355 *imx355 = to_imx355(sd); - - gpiod_set_value_cansleep(imx355->reset_gpio, 1); - - regulator_bulk_disable(ARRAY_SIZE(imx355_supplies), imx355->supplies); - clk_disable_unprepare(imx355->clk); - - return 0; -} - -static int imx355_power_on(struct device *dev) -{ - struct i2c_client *client = container_of(dev, struct i2c_client, dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx355 *imx355 = to_imx355(sd); - int ret; - - ret = clk_prepare_enable(imx355->clk); - if (ret) - return dev_err_probe(dev, ret, "failed to enable clocks"); - - ret = regulator_bulk_enable(ARRAY_SIZE(imx355_supplies), - imx355->supplies); - if (ret) { - dev_err_probe(dev, ret, "failed to enable regulators"); - goto error_disable_clocks; - } - - usleep_range(1000, 2000); - gpiod_set_value_cansleep(imx355->reset_gpio, 0); - usleep_range(10000, 11000); - - return 0; - -error_disable_clocks: - clk_disable_unprepare(imx355->clk); - return ret; -} - -static DEFINE_RUNTIME_DEV_PM_OPS(imx355_pm_ops, imx355_power_off, - imx355_power_on, NULL); - /* Initialize control handlers */ 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 vblank_min; s64 hblank; u64 pixel_rate; + const struct imx355_mode *mode; + u32 max; int ret; ctrl_hdlr = &imx355->ctrl_handler; @@ -1034,34 +1534,40 @@ static int imx355_init_controls(struct imx355 *imx355) if (ret) 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, 0, 0, - &imx355->hwcfg->link_freq_menu); + V4L2_CID_LINK_FREQ, max, 0, + link_freq_menu_items); 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->hwcfg->link_freq_menu * 2 * imx355->hwcfg->num_lanes; + pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; do_div(pixel_rate, 10); - - v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_PIXEL_RATE, - pixel_rate, pixel_rate, 1, pixel_rate); + /* 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); /* 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, IMX355_VBLANK_MIN, + V4L2_CID_VBLANK, vblank_min, IMX355_FLL_MAX - mode->height, 1, vblank_def); hblank = mode->llp - mode->width; imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, - V4L2_CID_HBLANK, hblank, - IMX355_LLP_MAX - mode->width, 1, - hblank); + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + if (imx355->hblank) + imx355->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* fll >= exposure time + adjust parameter (default value is 10) */ - exposure_max = mode->fll_def - IMX355_EXPOSURE_OFFSET; + exposure_max = mode->fll_def - 10; imx355->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_EXPOSURE, IMX355_EXPOSURE_MIN, exposure_max, @@ -1115,17 +1621,14 @@ static int imx355_init_controls(struct imx355 *imx355) return ret; } -static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) +static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) { - 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 lane_idx; int ret; if (!fwnode) @@ -1143,18 +1646,10 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) if (!cfg) goto out_err; - if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 && - bus_cfg.bus.mipi_csi2.num_data_lanes != 4) - goto out_err; - - 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, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), &cfg->link_freq_bitmap); if (ret) goto out_err; @@ -1181,11 +1676,7 @@ static int imx355_probe(struct i2c_client *client) imx355->dev = &client->dev; - imx355->regmap = devm_cci_regmap_init_i2c(client, 16); - if (IS_ERR(imx355->regmap)) { - dev_err(imx355->dev, "Unable to initialize I2C\n"); - return -ENODEV; - } + mutex_init(&imx355->mutex); imx355->clk = devm_v4l2_sensor_clk_get(imx355->dev, NULL); if (IS_ERR(imx355->clk)) @@ -1193,58 +1684,35 @@ static int imx355_probe(struct i2c_client *client) "failed to get clock\n"); freq = clk_get_rate(imx355->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) + if (freq != IMX355_EXT_CLK) return dev_err_probe(imx355->dev, -EINVAL, "external clock %lu is not supported\n", freq); - ret = devm_regulator_bulk_get_const(imx355->dev, - ARRAY_SIZE(imx355_supplies), - imx355_supplies, - &imx355->supplies); - if (ret) { - dev_err_probe(imx355->dev, ret, "could not get regulators"); - return ret; - } - - imx355->reset_gpio = devm_gpiod_get_optional(imx355->dev, "reset", - GPIOD_OUT_HIGH); - if (IS_ERR(imx355->reset_gpio)) { - ret = dev_err_probe(imx355->dev, PTR_ERR(imx355->reset_gpio), - "failed to get gpios"); - return ret; - } - /* Initialize subdev */ v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); - imx355->hwcfg = imx355_get_hwcfg(imx355); - if (!imx355->hwcfg) { - dev_err(imx355->dev, "failed to get hwcfg"); - return -ENODEV; - } - - ret = imx355_power_on(imx355->dev); - if (ret) - return ret; - /* Check module identity */ ret = imx355_identify_module(imx355); if (ret) { dev_err(imx355->dev, "failed to find sensor: %d", ret); - goto error_power_off; + goto error_probe; } + imx355->hwcfg = imx355_get_hwcfg(imx355->dev); + if (!imx355->hwcfg) { + dev_err(imx355->dev, "failed to get hwcfg"); + ret = -ENODEV; + goto error_probe; + } + + /* 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); - goto error_power_off; + goto error_probe; } /* Initialize subdev */ @@ -1262,13 +1730,6 @@ 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_handler_free; - } - /* * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. @@ -1281,22 +1742,19 @@ static int imx355_probe(struct i2c_client *client) goto error_media_entity_runtime_pm; pm_runtime_idle(imx355->dev); - pm_runtime_set_autosuspend_delay(imx355->dev, 1000); - pm_runtime_use_autosuspend(imx355->dev); return 0; error_media_entity_runtime_pm: pm_runtime_disable(imx355->dev); pm_runtime_set_suspended(imx355->dev); - v4l2_subdev_cleanup(&imx355->sd); media_entity_cleanup(&imx355->sd.entity); error_handler_free: v4l2_ctrl_handler_free(imx355->sd.ctrl_handler); -error_power_off: - imx355_power_off(imx355->dev); +error_probe: + mutex_destroy(&imx355->mutex); return ret; } @@ -1307,16 +1765,13 @@ 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); pm_runtime_disable(imx355->dev); + pm_runtime_set_suspended(imx355->dev); - if (!pm_runtime_status_suspended(imx355->dev)) { - imx355_power_off(imx355->dev); - pm_runtime_set_suspended(imx355->dev); - } + mutex_destroy(&imx355->mutex); } static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = { @@ -1325,18 +1780,10 @@ static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = { }; MODULE_DEVICE_TABLE(acpi, imx355_acpi_ids); -static const struct of_device_id imx355_match_table[] = { - { .compatible = "sony,imx355", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, imx355_match_table); - static struct i2c_driver imx355_i2c_driver = { .driver = { .name = "imx355", .acpi_match_table = ACPI_PTR(imx355_acpi_ids), - .of_match_table = imx355_match_table, - .pm = &imx355_pm_ops, }, .probe = imx355_probe, .remove = imx355_remove, From 42a376d9053ae0e6ce403fbdbe8f3702ec42c6be Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Mon, 16 Feb 2026 19:27:34 -0500 Subject: [PATCH 02/30] media: i2c: imx355: Support devicetree and power management Commit cd9f95628e375 upstream. A device tree compatible makes it possible for this driver to be used on Open Firmware devices. Initialization of power-managed resources such as the reset GPIO and voltage regulators can be specified in the device tree and handled by the driver. Add support for this so the Pixel 3a can use the driver. Reviewed-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Signed-off-by: Richard Acayan Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx355.c | 111 ++++++++++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 96f368ef44f68..a9dc55672f60c 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -3,9 +3,13 @@ #include #include +#include +#include #include #include +#include #include +#include #include #include @@ -125,6 +129,15 @@ struct imx355 { * Protect access to sensor v4l2 controls. */ struct mutex mutex; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data *supplies; +}; + +static const struct regulator_bulk_data imx355_supplies[] = { + { .supply = "avdd" }, + { .supply = "dvdd" }, + { .supply = "dovdd" }, }; static const struct imx355_reg imx355_global_regs[] = { @@ -1515,6 +1528,52 @@ static const struct v4l2_subdev_internal_ops imx355_internal_ops = { .open = imx355_open, }; +static int imx355_power_off(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx355 *imx355 = to_imx355(sd); + + gpiod_set_value_cansleep(imx355->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(imx355_supplies), imx355->supplies); + clk_disable_unprepare(imx355->clk); + + return 0; +} + +static int imx355_power_on(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx355 *imx355 = to_imx355(sd); + int ret; + + ret = clk_prepare_enable(imx355->clk); + if (ret) + return dev_err_probe(dev, ret, "failed to enable clocks"); + + ret = regulator_bulk_enable(ARRAY_SIZE(imx355_supplies), + imx355->supplies); + if (ret) { + dev_err_probe(dev, ret, "failed to enable regulators"); + goto error_disable_clocks; + } + + usleep_range(1000, 2000); + gpiod_set_value_cansleep(imx355->reset_gpio, 0); + usleep_range(10000, 11000); + + return 0; + +error_disable_clocks: + clk_disable_unprepare(imx355->clk); + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx355_pm_ops, imx355_power_off, + imx355_power_on, NULL); + /* Initialize control handlers */ static int imx355_init_controls(struct imx355 *imx355) { @@ -1689,16 +1748,26 @@ static int imx355_probe(struct i2c_client *client) "external clock %lu is not supported\n", freq); - /* Initialize subdev */ - v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); - - /* Check module identity */ - ret = imx355_identify_module(imx355); + ret = devm_regulator_bulk_get_const(imx355->dev, + ARRAY_SIZE(imx355_supplies), + imx355_supplies, + &imx355->supplies); if (ret) { - dev_err(imx355->dev, "failed to find sensor: %d", ret); + dev_err_probe(imx355->dev, ret, "could not get regulators"); goto error_probe; } + imx355->reset_gpio = devm_gpiod_get_optional(imx355->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(imx355->reset_gpio)) { + ret = dev_err_probe(imx355->dev, PTR_ERR(imx355->reset_gpio), + "failed to get gpios"); + goto error_probe; + } + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); + imx355->hwcfg = imx355_get_hwcfg(imx355->dev); if (!imx355->hwcfg) { dev_err(imx355->dev, "failed to get hwcfg"); @@ -1706,13 +1775,24 @@ static int imx355_probe(struct i2c_client *client) goto error_probe; } + ret = imx355_power_on(imx355->dev); + if (ret) + goto error_probe; + + /* Check module identity */ + ret = imx355_identify_module(imx355); + if (ret) { + dev_err(imx355->dev, "failed to find sensor: %d", ret); + 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); - goto error_probe; + goto error_power_off; } /* Initialize subdev */ @@ -1753,6 +1833,9 @@ static int imx355_probe(struct i2c_client *client) error_handler_free: v4l2_ctrl_handler_free(imx355->sd.ctrl_handler); +error_power_off: + imx355_power_off(imx355->dev); + error_probe: mutex_destroy(&imx355->mutex); @@ -1769,7 +1852,11 @@ static void imx355_remove(struct i2c_client *client) v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(imx355->dev); - pm_runtime_set_suspended(imx355->dev); + + if (!pm_runtime_status_suspended(imx355->dev)) { + imx355_power_off(imx355->dev); + pm_runtime_set_suspended(imx355->dev); + } mutex_destroy(&imx355->mutex); } @@ -1780,10 +1867,18 @@ static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = { }; MODULE_DEVICE_TABLE(acpi, imx355_acpi_ids); +static const struct of_device_id imx355_match_table[] = { + { .compatible = "sony,imx355", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx355_match_table); + static struct i2c_driver imx355_i2c_driver = { .driver = { .name = "imx355", .acpi_match_table = ACPI_PTR(imx355_acpi_ids), + .of_match_table = imx355_match_table, + .pm = &imx355_pm_ops, }, .probe = imx355_probe, .remove = imx355_remove, From 484d1bd4d9dd80052018fa54127b3736bc8ba27c Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Tue, 10 Mar 2026 22:03:28 -0400 Subject: [PATCH 03/30] media: i2c: imx355: Restrict data lanes to 4 Commit d3ac6212df604 usptream. The IMX355 sensor driver currently supports having 4 data lanes. There can't be more or less, so check if the firmware specifies 4 lanes. Suggested-by: Sakari Ailus Link: https://lore.kernel.org/r/aW3uFcT1zmiF4GUP@kekkonen.localdomain Signed-off-by: Richard Acayan Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx355.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index a9dc55672f60c..ac59908f57aae 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -66,6 +66,9 @@ #define IMX355_EXT_CLK 19200000 #define IMX355_LINK_FREQ_INDEX 0 +/* number of data lanes */ +#define IMX355_DATA_LANES 4 + struct imx355_reg { u16 address; u8 val; @@ -1705,6 +1708,9 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) if (!cfg) goto out_err; + if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX355_DATA_LANES) + goto out_err; + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, bus_cfg.nr_of_link_frequencies, link_freq_menu_items, From 3e57688d34b1537e6f1876a856c55919a79fe2f2 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Wed, 15 Jul 2026 12:43:15 +0100 Subject: [PATCH 04/30] media: imx355: Add support for 24 MHz external clock Commit 64faa6aa51bd7 upstream. 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 Signed-off-by: Sakari Ailus --- 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 ac59908f57aae..a6ee7532018fc 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 4f073e92254d14d179acbb477f19c9c1099ac32e Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:17 +0100 Subject: [PATCH 05/30] media: imx355: Remove duplicated registers from the mode tables Commit 5ac4c9b680af9 upstream. 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 Signed-off-by: Sakari Ailus --- 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 a6ee7532018fc..01d6c28a2656e 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 d9972b876f929c134eb12b9a3cd031a9a8667536 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:18 +0100 Subject: [PATCH 06/30] media: imx355: Remove setting FRM_LENGTH_LINES in the mode regs Commit c8703ce868acf upstream. 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 Signed-off-by: Sakari Ailus --- 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 01d6c28a2656e..b40d89d86156e 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 d7e321c290b0cf278f74fbdd57b20b49b792c5f1 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:19 +0100 Subject: [PATCH 07/30] media: imx355: Programmatically set the crop parameters for each mode Commit f8ec1f0c06e4b upstream. 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 Signed-off-by: Sakari Ailus --- 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 b40d89d86156e..123ceed26d053 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 47447a97cf6951b72ee12f675c950f0b7f805071 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:20 +0100 Subject: [PATCH 08/30] media: imx355: Set register LINE_LENGTH_PCK programmatically Commit 8722701663b91 upstream. 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 Signed-off-by: Sakari Ailus --- 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 123ceed26d053..e9f4bea67d3a7 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 51c71b6ac3a6d836e5d4734a0e33e8c0032b1c37 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:21 +0100 Subject: [PATCH 09/30] media: imx355: Set binning mode registers programmatically Commit 70a99e08c4738 upstream. Compute the binning registers based on the difference between the mode width/height vs the crop width/height. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- 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 e9f4bea67d3a7..95487b12939de 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 4fa83788778f188f2c462ebf262594e4715ee9fd Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:22 +0100 Subject: [PATCH 10/30] media: imx355: Remove link_freq_index from each mode as ununsed Commit d8c0dd60e923d upstream. The link_freq_index value in imx355_mode is unused, so remove it. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- 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 95487b12939de..b329da85ddc09 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 77286b88f1355748a2d15d6ffa154acdf1a043cc Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:23 +0100 Subject: [PATCH 11/30] media: imx355: pixel_rate never changes, so don't recompute Commit 2ed5e717a47f0 upstream. 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 Signed-off-by: Sakari Ailus --- 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 b329da85ddc09..879e98b8a8efd 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 6280c9288945f5e8c3b2ca71ca0406c8df77f8b0 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:24 +0100 Subject: [PATCH 12/30] media: imx355: Remove redundant fll_min, and implement fixed offset Commit ac2d97c65db49 upstream. 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 Signed-off-by: Sakari Ailus --- 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 879e98b8a8efd..649a6fc9abe9a 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 0970b04fcab428d90b1b7dcf934195543e16e91f Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:25 +0100 Subject: [PATCH 13/30] media: imx355: Add support for get_selection Commit 8a52937caec74 upstream. Provide all the cropping information via get_selection. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- 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 649a6fc9abe9a..120eda46d9820 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 ee05ec04de60d9afb1b366a15da664cb0882c9c6 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:26 +0100 Subject: [PATCH 14/30] media: imx355: Use pm_runtime autosuspend_delay Commit a21872acb7429 upstream. 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 Signed-off-by: Sakari Ailus --- 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 120eda46d9820..17a2acc1152df 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 099cf113b8d095f550b7e1efa6ed03b0cbbee66e Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:27 +0100 Subject: [PATCH 15/30] media: imx355: Convert to new CCI register access helpers Commit 314a674393878 upstream. Use the new comon CCI register access helpers to replace the private register access helpers in the imx355 driver. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 500 +++++++++++++++---------------------- 1 file changed, 195 insertions(+), 305 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 17a2acc1152df..52bdfe6362310 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 ea4d05ddcb8e0cb637e18e5e0dd4a65f6d11b351 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:28 +0100 Subject: [PATCH 16/30] media: imx355: Set the colorspace in the format Commit ba533bb043a8a upstream. 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 Signed-off-by: Sakari Ailus --- 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 52bdfe6362310..fa8a6851b44ef 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 1f8d20cd30571c63c80bd38dc81986078ecce68a Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:29 +0100 Subject: [PATCH 17/30] media: imx355: Define the exposure offset, and use that define Commit f31cb77bb1d14 upstream. 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 Signed-off-by: Sakari Ailus --- 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 fa8a6851b44ef..cc27a193cd815 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 45477945f032791817c36c9ec8e9cbe1d37c4bbf Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:30 +0100 Subject: [PATCH 18/30] media: imx355: Use NULL ctrl_ops for HBLANK as it is a read-only control Commit 2ae189fc1c2cd upstream. 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 Signed-off-by: Sakari Ailus --- 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 cc27a193cd815..3b3bf41485c29 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 391f997aced2bff20932a03cb1fc0e572e61d532 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:31 +0100 Subject: [PATCH 19/30] media: imx355: Compute link frequency from PLL setup Commit b8d87e9bdb881 upstream. In preparation for additional options in the PLL setup, compute the link frequency rather than using a hardcoded value. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- 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 3b3bf41485c29..d8f8efc0c927a 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 594c51fcbc7055012beffcd0db44ad76e253be14 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:32 +0100 Subject: [PATCH 20/30] media: imx355: Support 2 lane readout Commit 7c3aad6fbaff0 upstream. 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 Signed-off-by: Sakari Ailus --- 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 d8f8efc0c927a..0462e0feee362 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 1feed0ed638f59ebac1ff1e205946a02589ef51b Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:33 +0100 Subject: [PATCH 21/30] media: imx355: Switch to using the subdev state Commit 9f36cdacfdc4f upstream. 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 Signed-off-by: Sakari Ailus --- 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 0462e0feee362..97f4594f7ed9a 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 2f746b352e8d09b0ae43183f851da0f1f249e319 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:34 +0100 Subject: [PATCH 22/30] media: imx355: Remove storing cur_mode in the state Commit b7f17b9fe0ec1 upstream. 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 Signed-off-by: Sakari Ailus --- 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 97f4594f7ed9a..8eb8588cb71bb 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); From 4206f7df74eb38444a6e19b84d51f3287a937414 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Wed, 19 Aug 2026 22:13:14 +0200 Subject: [PATCH 23/30] media: imx355: reuse existing CCS defines The driver may not be MIPI CCS compliant, but does use same address and often set same values as compliant drivers. Do not define for every Sony imx* driver registers we already know and are standard. Signed-off-by: David Heidelberg Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 84 ++++++++++++-------------------------- 1 file changed, 25 insertions(+), 59 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 8eb8588cb71bb..2c845b7b3df4f 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -19,49 +19,30 @@ #include #include -#define IMX355_REG_MODE_SELECT CCI_REG8(0x0100) -#define IMX355_MODE_STANDBY 0x00 -#define IMX355_MODE_STREAMING 0x01 +#include "ccs/ccs-regs.h" -/* Chip ID */ -#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 /* V_TIMING internal */ -#define IMX355_REG_FLL CCI_REG16(0x0340) #define IMX355_FLL_MAX 0xffff #define IMX355_VBLANK_MIN 20 -#define IMX355_REG_LLP CCI_REG16(0x0342) #define IMX355_LLP_MAX 0xffff -#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 CCI_REG16(0x0202) #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) #define IMX355_ANA_GAIN_MIN 0 #define IMX355_ANA_GAIN_MAX 960 #define IMX355_ANA_GAIN_STEP 1 @@ -69,29 +50,14 @@ /* Digital gain control */ #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 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 - +/* Link rate register: 16-bit wide, unlike the CCS 32-bit one */ #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) - -/* Flip Control */ -#define IMX355_REG_ORIENTATION CCI_REG8(0x0101) - #define IMX355_PIXEL_ARRAY_TOP 0 #define IMX355_PIXEL_ARRAY_LEFT 0 #define IMX355_PIXEL_ARRAY_WIDTH 3280 @@ -621,29 +587,29 @@ 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 = cci_write(imx355->regmap, IMX355_REG_ANALOG_GAIN, + ret = cci_write(imx355->regmap, CCS_R_ANALOG_GAIN_CODE_GLOBAL, ctrl->val, NULL); break; case V4L2_CID_DIGITAL_GAIN: - ret = cci_write(imx355->regmap, IMX355_REG_DIG_GAIN_GLOBAL, + ret = cci_write(imx355->regmap, CCS_R_DIGITAL_GAIN_GLOBAL, ctrl->val, NULL); break; case V4L2_CID_EXPOSURE: - ret = cci_write(imx355->regmap, IMX355_REG_EXPOSURE, + ret = cci_write(imx355->regmap, CCS_R_COARSE_INTEGRATION_TIME, ctrl->val, NULL); break; case V4L2_CID_VBLANK: /* Update FLL that meets expected vertical blanking */ - ret = cci_write(imx355->regmap, IMX355_REG_FLL, + ret = cci_write(imx355->regmap, CCS_R_FRAME_LENGTH_LINES, format->height + ctrl->val, NULL); break; case V4L2_CID_TEST_PATTERN: - ret = cci_write(imx355->regmap, IMX355_REG_TEST_PATTERN, + ret = cci_write(imx355->regmap, CCS_R_TEST_PATTERN_MODE, ctrl->val, NULL); break; case V4L2_CID_HFLIP: case V4L2_CID_VFLIP: - ret = cci_write(imx355->regmap, IMX355_REG_ORIENTATION, + ret = cci_write(imx355->regmap, CCS_R_IMAGE_ORIENTATION, imx355->hflip->val | imx355->vflip->val << 1, NULL); break; @@ -828,27 +794,27 @@ static int imx355_start_streaming(struct imx355 *imx355) mode->reg_list.num_of_regs, &ret); /* Set readout crop and size registers */ - cci_write(imx355->regmap, IMX355_REG_X_ADD_START, crop->left, + cci_write(imx355->regmap, CCS_R_X_ADDR_START, crop->left, &ret); - cci_write(imx355->regmap, IMX355_REG_Y_ADD_START, crop->top, &ret); - cci_write(imx355->regmap, IMX355_REG_X_ADD_END, + cci_write(imx355->regmap, CCS_R_Y_ADDR_START, crop->top, &ret); + cci_write(imx355->regmap, CCS_R_X_ADDR_END, crop->width + crop->left - 1, &ret); - cci_write(imx355->regmap, IMX355_REG_Y_ADD_END, + cci_write(imx355->regmap, CCS_R_Y_ADDR_END, 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); + cci_write(imx355->regmap, CCS_R_X_OUTPUT_SIZE, fmt->width, &ret); + cci_write(imx355->regmap, CCS_R_Y_OUTPUT_SIZE, fmt->height, &ret); binning_mode = ((crop->width / fmt->width) << 4) | (crop->height / fmt->height); - cci_write(imx355->regmap, IMX355_REG_BINNING_MODE, + cci_write(imx355->regmap, CCS_R_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); + cci_write(imx355->regmap, CCS_R_BINNING_TYPE, binning_mode, &ret); + cci_write(imx355->regmap, CCS_R_BINNING_WEIGHTING, 0x00, &ret); /* Set PLL registers for the external clock frequency */ - cci_write(imx355->regmap, IMX355_REG_EXTCLK_FREQ, + cci_write(imx355->regmap, CCS_R_EXTCLK_FREQUENCY_MHZ, imx355->clk_params->extclk_freq, &ret); - cci_write(imx355->regmap, IMX355_REG_PLL_OP_MUL, + cci_write(imx355->regmap, CCS_R_OP_PLL_MULTIPLIER, 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); @@ -856,7 +822,7 @@ static int imx355_start_streaming(struct imx355 *imx355) lane_idx ? 2 : 1, &ret); /* Set MIPI configuration */ - cci_write(imx355->regmap, IMX355_REG_LANE_SEL, + cci_write(imx355->regmap, CCS_R_CSI_LANE_MODE, imx355->hwcfg->num_lanes - 1, &ret); link_bitrate = imx355->link_freq->qmenu_int[imx355->link_freq->val] * @@ -869,14 +835,14 @@ static int imx355_start_streaming(struct imx355 *imx355) cci_write(imx355->regmap, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, &ret); /* set line length */ - cci_write(imx355->regmap, IMX355_REG_LLP, + cci_write(imx355->regmap, CCS_R_LINE_LENGTH_PCK, imx355->hblank->val + fmt->width, &ret); /* Apply customized values from user */ if (!ret) ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); - cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, IMX355_MODE_STREAMING, + cci_write(imx355->regmap, CCS_R_MODE_SELECT, CCS_MODE_SELECT_STREAMING, &ret); return ret; @@ -885,8 +851,8 @@ static int imx355_start_streaming(struct imx355 *imx355) /* Stop streaming */ static int imx355_stop_streaming(struct imx355 *imx355) { - return cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, - IMX355_MODE_STANDBY, NULL); + return cci_write(imx355->regmap, CCS_R_MODE_SELECT, + CCS_MODE_SELECT_SOFTWARE_STANDBY, NULL); } static int imx355_set_stream(struct v4l2_subdev *sd, int enable) @@ -936,7 +902,7 @@ static int imx355_identify_module(struct imx355 *imx355) int ret; u64 val; - ret = cci_read(imx355->regmap, IMX355_REG_CHIP_ID, &val, NULL); + ret = cci_read(imx355->regmap, CCS_R_SENSOR_MODEL_ID, &val, NULL); if (ret) return ret; From 9d426d22aa80792b2c9bf0302fcf9b4e40258cfc Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Wed, 26 Aug 2026 10:51:21 +0200 Subject: [PATCH 24/30] media: imx355: use full CCS PLL divider registers The imx355 implements the lower byte of the standard MIPI CCS PLL divider registers VT_PIX_CLK_DIV, VT_SYS_CLK_DIV and OP_PRE_PLL_CLK_DIV. The high bytes default to zero and the mode register tables already program adjacent 16-bit registers (e.g. the PLL multiplier at 0x0306) in CCS big-endian byte order, so write the full 16-bit CCS registers instead of the odd-address 8-bit subregisters. Signed-off-by: David Heidelberg Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 2c845b7b3df4f..4b58a7a5bdf2a 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -24,9 +24,6 @@ #define IMX355_CHIP_ID 0x0355 /* PLL registers that depend on the external clock frequency */ -#define IMX355_REG_PLL_OP_PREDIV CCI_REG8(0x030d) -#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 @@ -205,13 +202,13 @@ static const struct cci_reg_sequence imx355_global_regs[] = { { CCI_REG8(0x305a), 0x00 }, { CCI_REG8(0x0112), 0x0a }, { CCI_REG8(0x0113), 0x0a }, - { IMX355_REG_PLL_IVT_PCK_DIV, IMX355_PLL_IVT_PCK_DIV }, - { CCI_REG8(0x0303), 0x01 }, + { CCS_R_VT_PIX_CLK_DIV, IMX355_PLL_IVT_PCK_DIV }, + { CCS_R_VT_SYS_CLK_DIV, 0x01 }, { CCI_REG8(0x0305), 0x02 }, { CCI_REG8(0x0306), 0x00 }, { CCI_REG8(0x0307), 0x78 }, { CCI_REG8(0x030b), 0x01 }, - { IMX355_REG_PLL_OP_PREDIV, IMX355_PLL_OP_PREDIV }, + { CCS_R_OP_PRE_PLL_CLK_DIV, IMX355_PLL_OP_PREDIV }, { CCI_REG8(0x0310), 0x00 }, { CCI_REG8(0x0220), 0x00 }, { CCI_REG8(0x0222), 0x01 }, @@ -816,9 +813,9 @@ static int imx355_start_streaming(struct imx355 *imx355) imx355->clk_params->extclk_freq, &ret); cci_write(imx355->regmap, CCS_R_OP_PLL_MULTIPLIER, imx355->clk_params->pll_op_mpy[lane_idx], &ret); - cci_write(imx355->regmap, IMX355_REG_PLL_OP_PREDIV, + cci_write(imx355->regmap, CCS_R_OP_PRE_PLL_CLK_DIV, imx355->clk_params->pll_op_prediv[lane_idx], &ret); - cci_write(imx355->regmap, IMX355_REG_PLL_IVT_SYSCK_DIV, + cci_write(imx355->regmap, CCS_R_VT_SYS_CLK_DIV, lane_idx ? 2 : 1, &ret); /* Set MIPI configuration */ From 27ab271323407c464678dc84072c557ca9c15220 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Wed, 26 Aug 2026 11:40:43 +0200 Subject: [PATCH 25/30] media: imx355: compute PLL configuration with ccs_pll_calculate() Replace the hand-tuned per-clock parameter tables (supported external clocks, OP PLL multipliers and pre-dividers) with the generic MIPI CCS PLL calculator. This generalises the driver to any external clock frequency within the documented limits instead of only 19.2 and 24 MHz. Signed-off-by: David Heidelberg Signed-off-by: Dave Stevenson --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/imx355.c | 177 +++++++++++++++++++++++++++---------- 2 files changed, 132 insertions(+), 46 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index e1c13cc5b3126..c487124074602 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -272,6 +272,7 @@ config VIDEO_IMX335 config VIDEO_IMX355 tristate "Sony IMX355 sensor support" select V4L2_CCI_I2C + select VIDEO_CCS_PLL 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 4b58a7a5bdf2a..bbf165efb58d6 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -20,6 +20,7 @@ #include #include "ccs/ccs-regs.h" +#include "ccs-pll.h" #define IMX355_CHIP_ID 0x0355 @@ -83,38 +84,69 @@ 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[2]; /* OP system PLL multiplier */ - u8 pll_op_prediv[2]; /* OP system pre PLL d */ -}; +#define IMX355_EXTCLK_FREQ_MHZ_REG(x) DIV_ROUND_CLOSEST_ULL((x) * 256, \ + 1000000U) /* - * The clock tree is in single PLL mode, so PREDIV_VT and MPY_IVT do nothing. - * 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. + * The sensor does not implement the CCS capability and limit registers, and + * its datasheet does not document the PLL limits. The values below are + * educated guesses that cover the known good configurations, e.g.: + * + * - 4 lanes, 19.2 MHz extclk: pre-div 2, multiplier 75 -> 720 MHz OP PLL + * output clock (360 MHz link frequency), + * - 2 lanes, 24 MHz extclk: pre-div 3, multiplier 111 -> 888 MHz OP PLL + * output clock (444 MHz link frequency). + * + * In both cases the PLL input clock is 8--12 MHz. */ -static const struct imx355_clk_params imx355_clk_params[] = { - { - .ext_clk = 19200000, - .extclk_freq = 0x1333, - .pll_op_mpy = { 75, 92 }, - .pll_op_prediv = { 2, 2 } +static const struct ccs_pll_limits imx355_ccs_pll_limits = { + .min_ext_clk_freq_hz = 6000000, + .max_ext_clk_freq_hz = 27000000, + + .vt_fr = { + .min_pre_pll_clk_div = 1, + .max_pre_pll_clk_div = 15, + /* Value is an educated guess as we don't have a spec */ + .min_pll_ip_clk_freq_hz = 2000000, + /* Value is an educated guess as we don't have a spec */ + .max_pll_ip_clk_freq_hz = 24000000, + .min_pll_multiplier = 10, + .max_pll_multiplier = 500, + .min_pll_op_clk_freq_hz = 320000000, + .max_pll_op_clk_freq_hz = 1000000000, }, - { - .ext_clk = 24000000, - .extclk_freq = 0x1800, - .pll_op_mpy = { 60, 111 }, - .pll_op_prediv = { 2, 3 } + .op_fr = { + .min_pre_pll_clk_div = 1, + .max_pre_pll_clk_div = 15, + .min_pll_ip_clk_freq_hz = 2000000, + .max_pll_ip_clk_freq_hz = 24000000, + .min_pll_multiplier = 10, + .max_pll_multiplier = 500, + .min_pll_op_clk_freq_hz = 320000000, + .max_pll_op_clk_freq_hz = 1000000000, + }, + + .vt_bk = { + .min_sys_clk_div = 1, + .max_sys_clk_div = 2, + .min_pix_clk_div = 4, + .max_pix_clk_div = 10, + .min_pix_clk_freq_hz = 80000000, + .max_pix_clk_freq_hz = 180000000, + }, + .op_bk = { + .min_sys_clk_div = 1, + .max_sys_clk_div = 2, + .min_pix_clk_div = 4, + .max_pix_clk_div = 10, + .min_pix_clk_freq_hz = 80000000, + .max_pix_clk_freq_hz = 180000000, }, }; struct imx355_hwcfg { - s64 link_freq_menu; + s64 *link_frequencies; + unsigned int n_link_frequencies; unsigned long link_freq_bitmap; unsigned int num_lanes; }; @@ -137,7 +169,7 @@ struct imx355 { struct v4l2_ctrl *hflip; struct imx355_hwcfg *hwcfg; - const struct imx355_clk_params *clk_params; + u32 extclk_freq; /* External clock frequency (Hz) */ struct gpio_desc *reset_gpio; struct regulator_bulk_data *supplies; @@ -764,6 +796,29 @@ static int imx355_entity_init_state(struct v4l2_subdev *subdev, return 0; } +static int imx355_pll_calculate(struct imx355 *imx355, unsigned int num_lanes, + s64 link_freq, struct ccs_pll *pll) +{ + memset(pll, 0, sizeof(*pll)); + + pll->bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY; + pll->op_lanes = num_lanes; + pll->vt_lanes = num_lanes; + pll->csi2.lanes = num_lanes; + + pll->binning_horizontal = 1; + pll->binning_vertical = 1; + pll->scale_m = 1; + pll->scale_n = 1; + pll->bits_per_pixel = 10; + pll->op_bits_per_lane = 10; + pll->flags = CCS_PLL_FLAG_LANE_SPEED_MODEL; + pll->link_freq = link_freq; + pll->ext_clk_freq_hz = imx355->extclk_freq; + + return ccs_pll_calculate(imx355->dev, &imx355_ccs_pll_limits, pll); +} + /* Start streaming */ static int imx355_start_streaming(struct imx355 *imx355) { @@ -772,9 +827,16 @@ static int imx355_start_streaming(struct imx355 *imx355) const struct imx355_mode *mode; int lane_idx = imx355->hwcfg->num_lanes == 4 ? 0 : 1; struct v4l2_rect *crop; + struct ccs_pll pll; u64 link_bitrate; u8 binning_mode; - int ret = 0; + int ret; + + ret = imx355_pll_calculate(imx355, imx355->hwcfg->num_lanes, + imx355->link_freq->qmenu_int[imx355->link_freq->val], + &pll); + if (ret) + return ret; /* Global Setting */ cci_multi_reg_write(imx355->regmap, imx355_global_regs, @@ -810,11 +872,11 @@ static int imx355_start_streaming(struct imx355 *imx355) /* Set PLL registers for the external clock frequency */ cci_write(imx355->regmap, CCS_R_EXTCLK_FREQUENCY_MHZ, - imx355->clk_params->extclk_freq, &ret); - cci_write(imx355->regmap, CCS_R_OP_PLL_MULTIPLIER, - imx355->clk_params->pll_op_mpy[lane_idx], &ret); + IMX355_EXTCLK_FREQ_MHZ_REG(imx355->extclk_freq), &ret); cci_write(imx355->regmap, CCS_R_OP_PRE_PLL_CLK_DIV, - imx355->clk_params->pll_op_prediv[lane_idx], &ret); + pll.op_fr.pre_pll_clk_div, &ret); + cci_write(imx355->regmap, CCS_R_OP_PLL_MULTIPLIER, + pll.op_fr.pll_multiplier, &ret); cci_write(imx355->regmap, CCS_R_VT_SYS_CLK_DIV, lane_idx ? 2 : 1, &ret); @@ -1006,13 +1068,15 @@ static int imx355_init_controls(struct imx355 *imx355) return ret; imx355->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx355_ctrl_ops, - V4L2_CID_LINK_FREQ, 0, 0, - &imx355->hwcfg->link_freq_menu); + V4L2_CID_LINK_FREQ, + imx355->hwcfg->n_link_frequencies - 1, + 0, imx355->hwcfg->link_frequencies); 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->hwcfg->link_freq_menu * 2 * imx355->hwcfg->num_lanes; + pixel_rate = imx355->hwcfg->link_frequencies[0] * 2 * + imx355->hwcfg->num_lanes; do_div(pixel_rate, 10); v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1093,10 +1157,9 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) 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 lane_idx; + unsigned int i; int ret; if (!fwnode) @@ -1120,12 +1183,37 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) 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); + cfg->link_frequencies = devm_kcalloc(dev, bus_cfg.nr_of_link_frequencies, + sizeof(*cfg->link_frequencies), + GFP_KERNEL); + if (!cfg->link_frequencies) + goto out_err; + + /* + * Validate each advertised link frequency against the PLL limits of + * the sensor, for the number of lanes in use. + */ + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { + struct ccs_pll pll; + int pll_ret; + + pll_ret = imx355_pll_calculate(imx355, cfg->num_lanes, + bus_cfg.link_frequencies[i], + &pll); + if (pll_ret) + continue; + + cfg->link_frequencies[cfg->n_link_frequencies++] = + bus_cfg.link_frequencies[i]; + } + + if (!cfg->n_link_frequencies) + goto out_err; + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, bus_cfg.nr_of_link_frequencies, - &cfg->link_freq_menu, 1, + cfg->link_frequencies, + cfg->n_link_frequencies, &cfg->link_freq_bitmap); if (ret) goto out_err; @@ -1163,16 +1251,13 @@ static int imx355_probe(struct i2c_client *client) "failed to get clock\n"); freq = clk_get_rate(imx355->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) + if (freq < imx355_ccs_pll_limits.min_ext_clk_freq_hz || + freq > imx355_ccs_pll_limits.max_ext_clk_freq_hz) { return dev_err_probe(imx355->dev, -EINVAL, "external clock %lu is not supported\n", freq); + } + imx355->extclk_freq = freq; ret = devm_regulator_bulk_get_const(imx355->dev, ARRAY_SIZE(imx355_supplies), From 51053344dc3d1a056f88d13a8924b29acb8cabba Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 14 Sep 2026 17:31:49 +0100 Subject: [PATCH 26/30] imx355: fixup ccs-pll --- drivers/media/i2c/imx355.c | 89 +++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index bbf165efb58d6..6e6efd50e83c5 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -88,47 +88,41 @@ struct imx355_mode { 1000000U) /* - * The sensor does not implement the CCS capability and limit registers, and - * its datasheet does not document the PLL limits. The values below are - * educated guesses that cover the known good configurations, e.g.: + * The sensor does not implement the CCS capability and limit registers. + * The datasheet gives some limits on PLL configuration, so these have been + * translated into the CCS PLL helper equivalents. * + * The main configurations used are: * - 4 lanes, 19.2 MHz extclk: pre-div 2, multiplier 75 -> 720 MHz OP PLL * output clock (360 MHz link frequency), - * - 2 lanes, 24 MHz extclk: pre-div 3, multiplier 111 -> 888 MHz OP PLL + * - 2 lanes, 24 MHz extclk: pre-div 2, multiplier 74 -> 888 MHz OP PLL * output clock (444 MHz link frequency). * - * In both cases the PLL input clock is 8--12 MHz. + * The supported PLL input clock range is 6-27 MHz. */ static const struct ccs_pll_limits imx355_ccs_pll_limits = { .min_ext_clk_freq_hz = 6000000, .max_ext_clk_freq_hz = 27000000, .vt_fr = { - .min_pre_pll_clk_div = 1, - .max_pre_pll_clk_div = 15, - /* Value is an educated guess as we don't have a spec */ - .min_pll_ip_clk_freq_hz = 2000000, - /* Value is an educated guess as we don't have a spec */ - .max_pll_ip_clk_freq_hz = 24000000, - .min_pll_multiplier = 10, - .max_pll_multiplier = 500, - .min_pll_op_clk_freq_hz = 320000000, - .max_pll_op_clk_freq_hz = 1000000000, + .min_pre_pll_clk_div = 2, + .max_pre_pll_clk_div = 4, + .min_pll_ip_clk_freq_hz = 6000000, + .max_pll_ip_clk_freq_hz = 27000000, + .min_pll_multiplier = 16, + .max_pll_multiplier = 148, + .min_pll_op_clk_freq_hz = 360000000, + .max_pll_op_clk_freq_hz = 890000000, }, .op_fr = { - .min_pre_pll_clk_div = 1, - .max_pre_pll_clk_div = 15, - .min_pll_ip_clk_freq_hz = 2000000, - .max_pll_ip_clk_freq_hz = 24000000, - .min_pll_multiplier = 10, - .max_pll_multiplier = 500, - .min_pll_op_clk_freq_hz = 320000000, - .max_pll_op_clk_freq_hz = 1000000000, + /* Not required as dual PPL mode not supported */ }, .vt_bk = { .min_sys_clk_div = 1, .max_sys_clk_div = 2, + .min_sys_clk_freq_hz = 320000000, + .max_sys_clk_freq_hz = 1000000000, .min_pix_clk_div = 4, .max_pix_clk_div = 10, .min_pix_clk_freq_hz = 80000000, @@ -137,11 +131,15 @@ static const struct ccs_pll_limits imx355_ccs_pll_limits = { .op_bk = { .min_sys_clk_div = 1, .max_sys_clk_div = 2, - .min_pix_clk_div = 4, - .max_pix_clk_div = 10, - .min_pix_clk_freq_hz = 80000000, - .max_pix_clk_freq_hz = 180000000, + .min_sys_clk_freq_hz = 360000000, + .max_sys_clk_freq_hz = 1000000000, + .min_pix_clk_div = 5, + .max_pix_clk_div = 5, + .min_pix_clk_freq_hz = 72000000, + .max_pix_clk_freq_hz = 288000000, }, + .min_line_length_pck_bin = 1836, + .min_line_length_pck = 3672, }; struct imx355_hwcfg { @@ -234,13 +232,6 @@ static const struct cci_reg_sequence imx355_global_regs[] = { { CCI_REG8(0x305a), 0x00 }, { CCI_REG8(0x0112), 0x0a }, { CCI_REG8(0x0113), 0x0a }, - { CCS_R_VT_PIX_CLK_DIV, IMX355_PLL_IVT_PCK_DIV }, - { CCS_R_VT_SYS_CLK_DIV, 0x01 }, - { CCI_REG8(0x0305), 0x02 }, - { CCI_REG8(0x0306), 0x00 }, - { CCI_REG8(0x0307), 0x78 }, - { CCI_REG8(0x030b), 0x01 }, - { CCS_R_OP_PRE_PLL_CLK_DIV, IMX355_PLL_OP_PREDIV }, { CCI_REG8(0x0310), 0x00 }, { CCI_REG8(0x0220), 0x00 }, { CCI_REG8(0x0222), 0x01 }, @@ -842,6 +833,26 @@ static int imx355_start_streaming(struct imx355 *imx355) cci_multi_reg_write(imx355->regmap, imx355_global_regs, ARRAY_SIZE(imx355_global_regs), &ret); + /* Set PLL registers for the external clock frequency */ + cci_write(imx355->regmap, CCS_R_EXTCLK_FREQUENCY_MHZ, + IMX355_EXTCLK_FREQ_MHZ_REG(imx355->extclk_freq), &ret); + + /* CCS 1-PLL mode uses VT PLL, but IMX355 uses OP PLL */ + cci_write(imx355->regmap, CCS_R_OP_PRE_PLL_CLK_DIV, + pll.vt_fr.pre_pll_clk_div, &ret); + cci_write(imx355->regmap, CCS_R_OP_PLL_MULTIPLIER, + pll.vt_fr.pll_multiplier, &ret); + cci_write(imx355->regmap, CCS_R_VT_SYS_CLK_DIV, + lane_idx ? 2 : 1, &ret); + + cci_write(imx355->regmap, CCS_R_VT_PIX_CLK_DIV, pll.vt_bk.pix_clk_div, &ret); + cci_write(imx355->regmap, CCS_R_VT_SYS_CLK_DIV, pll.vt_bk.sys_clk_div, &ret); + cci_write(imx355->regmap, CCS_R_PRE_PLL_CLK_DIV, pll.vt_fr.pre_pll_clk_div, &ret); + cci_write(imx355->regmap, CCS_R_PLL_MULTIPLIER, pll.vt_fr.pll_multiplier, &ret); + + cci_write(imx355->regmap, CCS_R_OP_PIX_CLK_DIV, pll.op_bk.pix_clk_div, &ret); + cci_write(imx355->regmap, CCS_R_OP_SYS_CLK_DIV, pll.op_bk.sys_clk_div, &ret); + /* Apply values of current mode */ state = v4l2_subdev_get_locked_active_state(&imx355->sd); fmt = v4l2_subdev_state_get_format(state, 0); @@ -870,16 +881,6 @@ static int imx355_start_streaming(struct imx355 *imx355) cci_write(imx355->regmap, CCS_R_BINNING_TYPE, binning_mode, &ret); cci_write(imx355->regmap, CCS_R_BINNING_WEIGHTING, 0x00, &ret); - /* Set PLL registers for the external clock frequency */ - cci_write(imx355->regmap, CCS_R_EXTCLK_FREQUENCY_MHZ, - IMX355_EXTCLK_FREQ_MHZ_REG(imx355->extclk_freq), &ret); - cci_write(imx355->regmap, CCS_R_OP_PRE_PLL_CLK_DIV, - pll.op_fr.pre_pll_clk_div, &ret); - cci_write(imx355->regmap, CCS_R_OP_PLL_MULTIPLIER, - pll.op_fr.pll_multiplier, &ret); - cci_write(imx355->regmap, CCS_R_VT_SYS_CLK_DIV, - lane_idx ? 2 : 1, &ret); - /* Set MIPI configuration */ cci_write(imx355->regmap, CCS_R_CSI_LANE_MODE, imx355->hwcfg->num_lanes - 1, &ret); From b2066036335ed17075c73d888ce6fb529335cbfa Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 14 Sep 2026 19:45:36 +0100 Subject: [PATCH 27/30] media: imx355: Increase max PLL rate Overclock slightly to meet performance requirements Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 6e6efd50e83c5..83ff16cbbdb21 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -112,7 +112,7 @@ static const struct ccs_pll_limits imx355_ccs_pll_limits = { .min_pll_multiplier = 16, .max_pll_multiplier = 148, .min_pll_op_clk_freq_hz = 360000000, - .max_pll_op_clk_freq_hz = 890000000, + .max_pll_op_clk_freq_hz = 960000000, }, .op_fr = { /* Not required as dual PPL mode not supported */ @@ -136,7 +136,7 @@ static const struct ccs_pll_limits imx355_ccs_pll_limits = { .min_pix_clk_div = 5, .max_pix_clk_div = 5, .min_pix_clk_freq_hz = 72000000, - .max_pix_clk_freq_hz = 288000000, + .max_pix_clk_freq_hz = 312000000, }, .min_line_length_pck_bin = 1836, .min_line_length_pck = 3672, From 86343bddb161b7a7830a9ae1288e30ef716e0b6a Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 14 Sep 2026 19:47:03 +0100 Subject: [PATCH 28/30] media: imx355: Add a cropped 640x480 mode Achieves 209fps. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 83ff16cbbdb21..a3ea5ba6ea07e 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -315,6 +315,11 @@ static const struct cci_reg_sequence mode_820x616_regs[] = { { CCI_REG8(0x0701), 0x78 }, }; +static const struct cci_reg_sequence mode_640x480_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, +}; + static const char * const imx355_test_pattern_menu[] = { "Disabled", "Solid Colour", @@ -549,6 +554,22 @@ static const struct imx355_mode supported_modes[] = { .regs = mode_820x616_regs, }, }, + { + .width = 640, + .height = 480, + .crop = { + .width = 1280, + .height = 960, + .left = 1000, + .top = 752, + }, + .fll_def = 520, + .llp = 1836, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_640x480_regs), + .regs = mode_640x480_regs, + }, + }, }; static inline struct imx355 *to_imx355(struct v4l2_subdev *_sd) From 99c269382450166179a2d5b78c2fb79079a3824d Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 12 May 2026 16:47:32 +0100 Subject: [PATCH 29/30] media: imx355: Make V4L2_CID_HBLANK controllable V4L2_CID_HBLANK / LLP was a read-only control that set min/ max/default all to the same value. The datasheet does state "H-sync timing is fixed in order to avoid the sensor internal interference (FPN) in IMX355 by line_length". No interference has currently been observed, and the default is always to leave it at the minimum (and approved) value. Allowing it to be controllable therefore has no effect unless a frame period longer than can be achieved just through VBLANK is requested. Exposure times up to 14.9s/frame can be achieved when combined with the max FLL / VBLANK value. Signed-off-by: Dave Stevenson --- drivers/media/i2c/imx355.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index a3ea5ba6ea07e..ac1403e0148e4 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -644,6 +644,11 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) ret = cci_write(imx355->regmap, CCS_R_FRAME_LENGTH_LINES, format->height + ctrl->val, NULL); break; + case V4L2_CID_HBLANK: + ret = cci_write(imx355->regmap, CCS_R_LINE_LENGTH_PCK, + format->width + ctrl->val, + NULL); + break; case V4L2_CID_TEST_PATTERN: ret = cci_write(imx355->regmap, CCS_R_TEST_PATTERN_MODE, ctrl->val, NULL); @@ -760,12 +765,10 @@ imx355_set_pad_format(struct v4l2_subdev *sd, h_blank = mode->llp - mode->width; - /* - * Currently hblank is not changeable. - * So FPS control is done only by vblank. - */ __v4l2_ctrl_modify_range(imx355->hblank, h_blank, - h_blank, 1, h_blank); + IMX355_LLP_MAX - mode->width, 1, + h_blank); + __v4l2_ctrl_s_ctrl(imx355->hblank, h_blank); } return 0; @@ -915,10 +918,6 @@ static int imx355_start_streaming(struct imx355 *imx355) /* set digital gain control to all color mode */ cci_write(imx355->regmap, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, &ret); - /* set line length */ - cci_write(imx355->regmap, CCS_R_LINE_LENGTH_PCK, - imx355->hblank->val + fmt->width, &ret); - /* Apply customized values from user */ if (!ret) ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); @@ -1112,10 +1111,10 @@ static int imx355_init_controls(struct imx355 *imx355) 1, vblank_def); hblank = mode->llp - mode->width; - 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; + imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_HBLANK, hblank, + IMX355_LLP_MAX - mode->width, 1, + hblank); /* fll >= exposure time + adjust parameter (default value is 10) */ exposure_max = mode->fll_def - IMX355_EXPOSURE_OFFSET; From 41401f36272f81afe727960aa9dff9f45f5fc7a6 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 14 Sep 2026 19:46:36 +0100 Subject: [PATCH 30/30] dtoverlays: imx355: Increase link frequency to meet performance targets Signed-off-by: Dave Stevenson --- arch/arm/boot/dts/overlays/imx355.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/overlays/imx355.dtsi b/arch/arm/boot/dts/overlays/imx355.dtsi index f06ae64b239b2..a1be88ec46132 100644 --- a/arch/arm/boot/dts/overlays/imx355.dtsi +++ b/arch/arm/boot/dts/overlays/imx355.dtsi @@ -20,7 +20,7 @@ cam_node: imx355@1a { clock-lanes = <0>; data-lanes = <1 2>; link-frequencies = - /bits/ 64 <444000000>; + /bits/ 64 <480000000>; }; }; };