diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -259,6 +259,7 @@ config VIDEO_IMX335 config VIDEO_IMX355 tristate "Sony IMX355 sensor support" + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX355 camera. @@ -287,6 +288,16 @@ config VIDEO_IMX415 To compile this driver as a module, choose M here: the module will be called imx415. +config VIDEO_IMX471 + tristate "Sony IMX471 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the Sony + IMX471 camera. + + To compile this driver as a module, choose M here: the + module will be called imx471. + config VIDEO_MAX9271_LIB tristate diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_IMX412) += imx412.o obj-$(CONFIG_VIDEO_IMX415) += imx415.o +obj-$(CONFIG_VIDEO_IMX471) += imx471.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o obj-$(CONFIG_VIDEO_KS0127) += ks0127.o diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c --- a/drivers/media/i2c/cvs/core.c +++ b/drivers/media/i2c/cvs/core.c @@ -31,6 +31,7 @@ #define PCI_DEVICE_ID_INTEL_IPU7 0x645d /* MTL / LNL */ #define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d /* ARL / PTL */ +#define PCI_DEVICE_ID_INTEL_IPU8 0xd719 /* NVL */ /* * IPU7 PCI device IDs not covered by ipu6_pci_tbl in ipu6-pci-table.h. @@ -39,6 +40,7 @@ static const struct pci_device_id icvs_ipu7_tbl[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU8) }, { } }; diff --git a/drivers/media/i2c/cvs/icvs.h b/drivers/media/i2c/cvs/icvs.h --- a/drivers/media/i2c/cvs/icvs.h +++ b/drivers/media/i2c/cvs/icvs.h @@ -432,7 +432,6 @@ enum icvs_state { * @freq_ctrl: (future) frequency control pointer * @pads: Local media pads (sink/source) * @nr_of_lanes: Active CSI-2 lane count - * @link_freq: Current link frequency (Hz) * @ipu_link: PM runtime device link (IPU consumer, CVS supplier) * @res: Resource capability (light/full) * @caps: Reported device protocol capabilities @@ -458,7 +457,6 @@ struct icvs { struct v4l2_ctrl *freq_ctrl; struct media_pad pads[ICVS_CSI_NUM_PADS]; u32 nr_of_lanes; - u64 link_freq; struct device_link *ipu_link; enum icvs_resources res; struct icvs_dev_capabilities caps; diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c --- a/drivers/media/i2c/cvs/v4l2.c +++ b/drivers/media/i2c/cvs/v4l2.c @@ -46,6 +46,7 @@ static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { /** * csi_set_link_cfg - Program default CSI-2 link parameters * @ctx: CVS device context + * @link_freq: Link frequency (Hz) * * Populates a HOST_SET_MIPI_CONFIG command using current lane count and * link frequency, then submits it to the device. @@ -53,12 +54,12 @@ static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { * * Return: 0 on success or negative errno. */ -static int csi_set_link_cfg(struct icvs *ctx) +static int csi_set_link_cfg(struct icvs *ctx, u64 link_freq) { struct icvs_cmd cmd = { .cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG), .param.conf.nr_of_lanes = ctx->nr_of_lanes, - .param.conf.link_freq = ctx->link_freq, + .param.conf.link_freq = link_freq, }; size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf); @@ -91,7 +92,7 @@ static int cvs_csi_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev *remote_sd = media_entity_to_v4l2_subdev(ctx->remote->entity); struct device *dev = cvs_dev(ctx); - s64 freq; + s64 link_freq; int ret; /* cvs_set_link_owner(ICVS_CSI_LINK_HOST) */ @@ -99,15 +100,14 @@ static int cvs_csi_enable_streams(struct v4l2_subdev *sd, if (ret < 0) return ret; - freq = v4l2_get_link_freq(ctx->remote, 0, 0); - if (freq < 0) { - ret = freq; + link_freq = v4l2_get_link_freq(ctx->remote, 0, 0); + if (link_freq < 0) { + ret = link_freq; goto err_rpm_put; } - ctx->link_freq = freq; if (ctx->i2c_client) { - ret = csi_set_link_cfg(ctx); + ret = csi_set_link_cfg(ctx, link_freq); if (ret < 0) goto err_rpm_put_sync; } @@ -345,7 +345,6 @@ static int cvs_csi_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, if (freq < 0) return -EINVAL; - ctx->link_freq = freq; cfg->link_freq = freq; return 0; diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -9,74 +9,97 @@ #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 +#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 0x0340 +#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 0x0202 +#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 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_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 0x0101 +#define IMX355_REG_ORIENTATION CCI_REG8(0x0101) -/* 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 */ -#define IMX355_DATA_LANES 4 - -struct imx355_reg { - u16 address; - u8 val; -}; +#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_list { u32 num_of_regs; - const struct imx355_reg *regs; + const struct cci_reg_sequence *regs; }; /* Mode : resolution and related config&values */ @@ -85,28 +108,58 @@ 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; @@ -114,24 +167,14 @@ 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; - - /* - * Mutex for serialized access: - * Protect sensor set pad format and start/stop streaming safely. - * Protect access to sensor v4l2 controls. - */ - struct mutex mutex; + const struct imx355_clk_params *clk_params; struct gpio_desc *reset_gpio; struct regulator_bulk_data *supplies; @@ -143,750 +186,147 @@ static const struct regulator_bulk_data imx355_supplies[] = { { .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), 0x00 }, + { CCI_REG8(0x0307), 0x78 }, + { CCI_REG8(0x030b), 0x01 }, + { 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 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 struct cci_reg_sequence mode_820x616_regs[] = { + { CCI_REG8(0x0700), 0x02 }, + { CCI_REG8(0x0701), 0x78 }, }; static const char * const imx355_test_pattern_menu[] = { @@ -897,23 +337,19 @@ 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, @@ -922,10 +358,14 @@ 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, @@ -934,10 +374,14 @@ 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, @@ -946,10 +390,14 @@ 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, @@ -958,10 +406,14 @@ 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, @@ -970,10 +422,14 @@ 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, @@ -982,10 +438,14 @@ 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, @@ -994,10 +454,14 @@ 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, @@ -1006,10 +470,14 @@ 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, @@ -1018,10 +486,14 @@ 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, @@ -1030,10 +502,14 @@ 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, @@ -1042,10 +518,14 @@ 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, @@ -1054,10 +534,14 @@ 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, @@ -1066,10 +550,14 @@ 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, @@ -1095,116 +583,28 @@ 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 = imx355->cur_mode->height + ctrl->val - 10; + max = format->height + ctrl->val - IMX355_EXPOSURE_OFFSET; __v4l2_ctrl_modify_range(imx355->exposure, imx355->exposure->minimum, max, imx355->exposure->step, max); @@ -1221,31 +621,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, + format->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; @@ -1272,9 +672,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; } @@ -1288,12 +686,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; @@ -1311,36 +706,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; -} - -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; + 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 @@ -1351,13 +720,8 @@ imx355_set_pad_format(struct v4l2_subdev *sd, struct imx355 *imx355 = to_imx355(sd); const struct imx355_mode *mode; struct v4l2_mbus_framefmt *framefmt; - s32 vblank_def; - s32 vblank_min; + struct v4l2_rect *crop; s64 h_blank; - u64 pixel_rate; - u32 height; - - mutex_lock(&imx355->mutex); /* * Only one bayer order is supported. @@ -1370,23 +734,25 @@ 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; - } 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); + 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) { /* 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_s_ctrl(imx355->vblank, vblank_def); - h_blank = mode->llp - imx355->cur_mode->width; + __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; + /* * Currently hblank is not changeable. * So FPS control is done only by vblank. @@ -1395,7 +761,42 @@ imx355_set_pad_format(struct v4l2_subdev *sd, h_blank, 1, h_blank); } - mutex_unlock(&imx355->mutex); + 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; + } + + 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; } @@ -1403,52 +804,98 @@ imx355_set_pad_format(struct v4l2_subdev *sd, /* Start streaming */ static int imx355_start_streaming(struct imx355 *imx355) { - const struct imx355_reg_list *reg_list; - int ret; + 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; /* 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 */ - 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; - } + /* 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); /* 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 */ + cci_write(imx355->regmap, IMX355_REG_LLP, + imx355->hblank->val + fmt->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); - return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, - 1, IMX355_MODE_STREAMING); + cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, IMX355_MODE_STREAMING, + &ret); + + 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) { 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); @@ -1464,21 +911,21 @@ 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 */ __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(imx355->dev); + pm_runtime_put_autosuspend(imx355->dev); err_unlock: - mutex_unlock(&imx355->mutex); + v4l2_subdev_unlock_state(state); return ret; } @@ -1487,14 +934,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; } @@ -1512,9 +959,10 @@ 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, }; static const struct v4l2_subdev_ops imx355_subdev_ops = { @@ -1528,7 +976,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) @@ -1582,13 +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 vblank_min; s64 hblank; u64 pixel_rate; - const struct imx355_mode *mode; - u32 max; int ret; ctrl_hdlr = &imx355->ctrl_handler; @@ -1596,40 +1042,34 @@ 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, 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 * imx355->hwcfg->num_lanes; 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; 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); 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; /* 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, @@ -1683,14 +1123,17 @@ 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 lane_idx; int ret; if (!fwnode) @@ -1708,13 +1151,18 @@ 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) + 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, - link_freq_menu_items, - ARRAY_SIZE(link_freq_menu_items), + &cfg->link_freq_menu, 1, &cfg->link_freq_bitmap); if (ret) goto out_err; @@ -1741,7 +1189,10 @@ 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), + "Unable to initialize I2C\n"); imx355->clk = devm_v4l2_sensor_clk_get(imx355->dev, NULL); if (IS_ERR(imx355->clk)) @@ -1749,7 +1200,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); @@ -1760,7 +1217,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", @@ -1768,22 +1225,21 @@ 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 */ 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; - 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); @@ -1792,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); @@ -1816,24 +1269,36 @@ 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. */ 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) - 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: @@ -1842,9 +1307,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; } @@ -1854,6 +1316,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); @@ -1864,7 +1327,7 @@ static void imx355_remove(struct i2c_client *client) pm_runtime_set_suspended(imx355->dev); } - mutex_destroy(&imx355->mutex); + pm_runtime_dont_use_autosuspend(imx355->dev); } static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = { diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c new file mode 100644 --- /dev/null +++ b/drivers/media/i2c/imx471.c @@ -0,0 +1,957 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx471.c - imx471 sensor driver + * + * Copyright (C) 2025 Intel Corporation + * Copyright (C) 2026 Kate Hsuan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX471_REG_MODE_SELECT CCI_REG8(0x0100) +#define IMX471_MODE_STANDBY 0x00 +#define IMX471_MODE_STREAMING 0x01 + +/* Chip ID */ +#define IMX471_REG_CHIP_ID CCI_REG16(0x0016) +#define IMX471_CHIP_ID 0x0471 + +/* V_TIMING internal */ +#define IMX471_REG_FLL CCI_REG16(0x0340) +#define IMX471_FLL_MAX 0xffff + +/* Exposure control */ +#define IMX471_REG_EXPOSURE CCI_REG16(0x0202) +#define IMX471_EXPOSURE_MIN 1 +#define IMX471_EXPOSURE_STEP 1 +#define IMX471_EXPOSURE_DEFAULT 1270 + +/* Default exposure margin */ +#define IMX471_EXPOSURE_MARGIN 18 + +/* Analog gain control */ +#define IMX471_REG_ANALOG_GAIN CCI_REG16(0x0204) +#define IMX471_ANA_GAIN_MIN 0 +#define IMX471_ANA_GAIN_MAX 800 +#define IMX471_ANA_GAIN_STEP 1 +#define IMX471_ANA_GAIN_DEFAULT 0 + +/* Digital gain control */ +#define IMX471_REG_DPGA_USE_GLOBAL_GAIN CCI_REG16(0x3ff9) +#define IMX471_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e) +#define IMX471_DGTL_GAIN_MIN 256 +#define IMX471_DGTL_GAIN_MAX 4095 +#define IMX471_DGTL_GAIN_STEP 1 +#define IMX471_DGTL_GAIN_DEFAULT 256 + +/* HFLIP and VFLIP control */ +#define IMX471_REG_ORIENTATION CCI_REG8(0x0101) + +/* Test Pattern Control */ +#define IMX471_REG_TEST_PATTERN CCI_REG8(0x0600) + +/* default link frequency and external clock */ +#define IMX471_LINK_FREQ_DEFAULT 200000000LL +#define IMX471_EXT_CLK 19200000 + +/* PLL */ +#define IMX471_REG_VTPXCK_DIV CCI_REG8(0x0301) +#define IMX471_REG_VTSYCK_DIV CCI_REG8(0x0303) +#define IMX471_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305) +#define IMX471_REG_PLL_VT_MPY CCI_REG16(0x0306) +#define IMX471_REG_OPPXCK_DIV CCI_REG8(0x0309) +#define IMX471_REG_OPSYCK_DIV CCI_REG8(0x030b) +#define IMX471_REG_PLL_MULT_DRIV CCI_REG8(0x0310) +#define IMX471_PLL_SINGLE 0 +#define IMX471_PLL_DUAL 1 + +/* IMX471 native and active pixel array size */ +#define IMX471_NATIVE_WIDTH 4672 +#define IMX471_NATIVE_HEIGHT 3512 +#define IMX471_PIXEL_ARRAY_LEFT 8 +#define IMX471_PIXEL_ARRAY_TOP 8 +#define IMX471_PIXEL_ARRAY_WIDTH 4656 +#define IMX471_PIXEL_ARRAY_HEIGHT 3496 + +#define IMX471_REG_EXCK_FREQ CCI_REG16(0x0136) + +#define IMX471_REG_CSI_DATA_FORMAT CCI_REG16(0x0112) +#define IMX471_CSI_DATA_FORMAT_RAW10 0x0a0a + +#define IMX471_REG_CSI_LANE_MODE CCI_REG8(0x0114) +#define IMX471_CSI_2_LANE_MODE 1 +#define IMX471_CSI_4_LANE_MODE 3 + +#define IMX471_REG_X_ADD_STA CCI_REG16(0x0344) +#define IMX471_REG_Y_ADD_STA CCI_REG16(0x0346) +#define IMX471_REG_X_ADD_END CCI_REG16(0x0348) +#define IMX471_REG_Y_ADD_END CCI_REG16(0x034a) +#define IMX471_REG_X_OUTPUT_SIZE CCI_REG16(0x034c) +#define IMX471_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e) +#define IMX471_REG_X_EVEN_INC CCI_REG8(0x0381) +#define IMX471_REG_X_ODD_INC CCI_REG8(0x0383) +#define IMX471_REG_Y_EVEN_INC CCI_REG8(0x0385) +#define IMX471_REG_Y_ODD_INC CCI_REG8(0x0387) + +#define IMX471_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408) +#define IMX471_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a) +#define IMX471_REG_DIG_CROP_WIDTH CCI_REG16(0x040c) +#define IMX471_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e) + +/* Binning mode */ +#define IMX471_REG_BINNING_MODE CCI_REG8(0x0900) +#define IMX471_BINNING_NONE 0 +#define IMX471_BINNING_ENABLE 1 +#define IMX471_REG_BINNING_TYPE CCI_REG8(0x0901) +#define IMX471_REG_BINNING_WEIGHTING CCI_REG8(0x0902) + +#define to_imx471(_sd) container_of_const(_sd, struct imx471, sd) + +static const char * const imx471_supply_name[] = { + "vana", +}; + +struct imx471_mode { + u32 width; + u32 height; + + /* V-timing */ + u32 fll_def; + u32 fll_min; + + /* H-timing */ + u32 llp; + + const struct cci_reg_sequence *default_mode_regs; + unsigned int default_mode_regs_length; +}; + +struct imx471 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *exposure; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(imx471_supply_name)]; + struct clk *img_clk; + + struct device *dev; + struct regmap *regmap; +}; + +static const struct cci_reg_sequence imx471_global_regs[] = { + { IMX471_REG_EXCK_FREQ, 0x1333 }, + { CCI_REG8(0x3c7e), 0x08 }, + { CCI_REG8(0x3c7f), 0x05 }, + { CCI_REG8(0x3e35), 0x00 }, + { CCI_REG8(0x3e36), 0x00 }, + { CCI_REG8(0x3e37), 0x00 }, + { CCI_REG8(0x3f7f), 0x01 }, + { CCI_REG8(0x4431), 0x04 }, + { CCI_REG8(0x531c), 0x01 }, + { CCI_REG8(0x531d), 0x02 }, + { CCI_REG8(0x531e), 0x04 }, + { CCI_REG8(0x5928), 0x00 }, + { CCI_REG8(0x5929), 0x2f }, + { CCI_REG8(0x592a), 0x00 }, + { CCI_REG8(0x592b), 0x85 }, + { CCI_REG8(0x592c), 0x00 }, + { CCI_REG8(0x592d), 0x32 }, + { CCI_REG8(0x592e), 0x00 }, + { CCI_REG8(0x592f), 0x88 }, + { CCI_REG8(0x5930), 0x00 }, + { CCI_REG8(0x5931), 0x3d }, + { CCI_REG8(0x5932), 0x00 }, + { CCI_REG8(0x5933), 0x93 }, + { CCI_REG8(0x5938), 0x00 }, + { CCI_REG8(0x5939), 0x24 }, + { CCI_REG8(0x593a), 0x00 }, + { CCI_REG8(0x593b), 0x7a }, + { CCI_REG8(0x593c), 0x00 }, + { CCI_REG8(0x593d), 0x24 }, + { CCI_REG8(0x593e), 0x00 }, + { CCI_REG8(0x593f), 0x7a }, + { CCI_REG8(0x5940), 0x00 }, + { CCI_REG8(0x5941), 0x2f }, + { CCI_REG8(0x5942), 0x00 }, + { CCI_REG8(0x5943), 0x85 }, + { CCI_REG8(0x5f0e), 0x6e }, + { CCI_REG8(0x5f11), 0xc6 }, + { CCI_REG8(0x5f17), 0x5e }, + { CCI_REG8(0x7990), 0x01 }, + { CCI_REG8(0x7993), 0x5d }, + { CCI_REG8(0x7994), 0x5d }, + { CCI_REG8(0x7995), 0xa1 }, + { CCI_REG8(0x799a), 0x01 }, + { CCI_REG8(0x799d), 0x00 }, + { CCI_REG8(0x8169), 0x01 }, + { CCI_REG8(0x8359), 0x01 }, + { CCI_REG8(0x9302), 0x1e }, + { CCI_REG8(0x9306), 0x1f }, + { CCI_REG8(0x930a), 0x26 }, + { CCI_REG8(0x930e), 0x23 }, + { CCI_REG8(0x9312), 0x23 }, + { CCI_REG8(0x9316), 0x2c }, + { CCI_REG8(0x9317), 0x19 }, + { CCI_REG8(0xb046), 0x01 }, + { CCI_REG8(0xb048), 0x01 }, +}; + +static const struct cci_reg_sequence mode_1928x1088_regs[] = { + { IMX471_REG_CSI_DATA_FORMAT, IMX471_CSI_DATA_FORMAT_RAW10 }, + { IMX471_REG_CSI_LANE_MODE, IMX471_CSI_4_LANE_MODE }, + { IMX471_REG_X_ADD_STA, 8 }, + { IMX471_REG_Y_ADD_STA, 408 }, + { IMX471_REG_X_ADD_END, 4647 }, + { IMX471_REG_Y_ADD_END, 3051 }, + { IMX471_REG_X_EVEN_INC, 1 }, + { IMX471_REG_X_ODD_INC, 1 }, + { IMX471_REG_Y_EVEN_INC, 1 }, + { IMX471_REG_Y_ODD_INC, 1 }, + { IMX471_REG_BINNING_MODE, IMX471_BINNING_ENABLE }, + { IMX471_REG_BINNING_TYPE, 0x22 }, + { IMX471_REG_BINNING_WEIGHTING, 0x08 }, + { IMX471_REG_DIG_CROP_X_OFFSET, 208 }, + { IMX471_REG_DIG_CROP_Y_OFFSET, 108 }, + { IMX471_REG_DIG_CROP_WIDTH, 1928 }, + { IMX471_REG_DIG_CROP_HEIGHT, 1088 }, + { IMX471_REG_X_OUTPUT_SIZE, 1928 }, + { IMX471_REG_Y_OUTPUT_SIZE, 1088 }, + { IMX471_REG_VTPXCK_DIV, 0x06 }, + { IMX471_REG_VTSYCK_DIV, 0x02 }, + { IMX471_REG_PREPLLCK_VT_DIV, 0x02 }, + { IMX471_REG_PLL_VT_MPY, 0x0079 }, + { IMX471_REG_OPSYCK_DIV, 0x01 }, + { CCI_REG8(0x030d), 0x02 }, + { CCI_REG8(0x030e), 0x00 }, + { CCI_REG8(0x030f), 0x53 }, + { IMX471_REG_PLL_MULT_DRIV, IMX471_PLL_DUAL }, + { CCI_REG8(0x3f4c), 0x81 }, + { CCI_REG8(0x3f4d), 0x81 }, + { CCI_REG8(0x3f78), 0x01 }, + { CCI_REG8(0x3f79), 0x31 }, + { CCI_REG8(0x3ffe), 0x00 }, + { CCI_REG8(0x3fff), 0x8a }, + { CCI_REG8(0x5f0a), 0xb6 }, +}; + +static const char * const imx471_test_pattern_menu[] = { + "Disabled", + "Solid Colour", + "Eight Vertical Colour Bars", + "Colour Bars With Fade to Grey", + "Pseudorandom Sequence (PN9)", +}; + +static const s64 link_freq_menu_items[] = { + IMX471_LINK_FREQ_DEFAULT, +}; + +/* + * The Bayer formats for the flipping. + * - no flip + * - h flip + * - v flip + * - h and v flips + */ +static const u32 imx471_hv_flips_bayer_order[] = { + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, +}; + +static const struct imx471_mode imx471_modes[] = { + { + .width = 1928, + .height = 1088, + .fll_def = 1308, + .fll_min = 1308, + .llp = 2328, + .default_mode_regs = mode_1928x1088_regs, + .default_mode_regs_length = ARRAY_SIZE(mode_1928x1088_regs), + }, +}; + +static int imx471_get_regulators(struct device *dev, struct imx471 *sensor) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(imx471_supply_name); i++) + sensor->supplies[i].supply = imx471_supply_name[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx471_supply_name), + sensor->supplies); +} + +static int imx471_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx471 *sensor = container_of_const(ctrl->handler, + struct imx471, + ctrl_handler); + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&sensor->sd); + const struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + int ret; + + if (ctrl->id == V4L2_CID_VBLANK) { + s64 exposure_max = format->height + ctrl->val - + IMX471_EXPOSURE_MARGIN; + ret = __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + exposure_max, + sensor->exposure->step, + exposure_max); + if (ret) + return ret; + } + + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(sensor->regmap, IMX471_REG_ANALOG_GAIN, + ctrl->val, NULL); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = cci_write(sensor->regmap, IMX471_REG_DIG_GAIN_GLOBAL, + ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(sensor->regmap, IMX471_REG_EXPOSURE, + ctrl->val, NULL); + break; + case V4L2_CID_VBLANK: + /* Update FLL that meets expected vertical blanking */ + ret = cci_write(sensor->regmap, IMX471_REG_FLL, + format->height + ctrl->val, NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = cci_write(sensor->regmap, IMX471_REG_TEST_PATTERN, + ctrl->val, NULL); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + ret = cci_write(sensor->regmap, IMX471_REG_ORIENTATION, + sensor->hflip->val | sensor->vflip->val << 1, + NULL); + break; + default: + ret = -EINVAL; + dev_err(sensor->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(sensor->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx471_ctrl_ops = { + .s_ctrl = imx471_set_ctrl, +}; + +static u32 imx471_get_format_code(struct imx471 *sensor) +{ + unsigned int i; + + i = (sensor->vflip->val ? 2 : 0) | (sensor->hflip->val ? 1 : 0); + + return imx471_hv_flips_bayer_order[i]; +} + +static int imx471_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx471 *sensor = to_imx471(sd); + + if (code->index >= (ARRAY_SIZE(imx471_hv_flips_bayer_order) / 4)) + return -EINVAL; + + code->code = imx471_get_format_code(sensor); + + return 0; +} + +static int imx471_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(imx471_modes)) + return -EINVAL; + + fse->min_width = imx471_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = imx471_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void imx471_update_pad_format(struct imx471 *sensor, + const struct imx471_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.code = imx471_get_format_code(sensor); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; +} + +static int imx471_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx471 *sensor = to_imx471(sd); + const struct imx471_mode *mode; + int h_blank, ret; + + mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes), + width, height, fmt->format.width, + fmt->format.height); + + imx471_update_pad_format(sensor, mode, fmt); + + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (media_entity_is_streaming(&sensor->sd.entity)) + return -EBUSY; + + ret = __v4l2_ctrl_modify_range(sensor->vblank, + mode->fll_min - mode->height, + IMX471_FLL_MAX - mode->height, + 1, + mode->fll_def - mode->height); + if (ret) + return ret; + + h_blank = mode->llp - mode->width; + /* + * Currently hblank is not changeable. + * So FPS control is done only by vblank. + */ + return __v4l2_ctrl_modify_range(sensor->hblank, h_blank, + h_blank, 1, h_blank); +} + +static int imx471_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, sel->pad); + break; + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = IMX471_NATIVE_WIDTH; + sel->r.height = IMX471_NATIVE_HEIGHT; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = IMX471_PIXEL_ARRAY_TOP; + sel->r.left = IMX471_PIXEL_ARRAY_LEFT; + sel->r.width = IMX471_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX471_PIXEL_ARRAY_HEIGHT; + return 0; + } + + return -EINVAL; +} + +static int imx471_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .format = { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .width = imx471_modes[0].width, + .height = imx471_modes[0].height, + }, + }; + + return imx471_set_pad_format(sd, sd_state, &fmt); +} + +static int imx471_identify_module(struct imx471 *sensor) +{ + int ret; + u64 val; + + ret = cci_read(sensor->regmap, IMX471_REG_CHIP_ID, &val, NULL); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to read chip id\n"); + + if (val != IMX471_CHIP_ID) + return dev_err_probe(sensor->dev, -EIO, + "chip id mismatch: %x!=%llx\n", + IMX471_CHIP_ID, val); + + return 0; +} + +static int imx471_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx471 *sensor = to_imx471(sd); + + clk_disable_unprepare(sensor->img_clk); + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name), + sensor->supplies); + + return 0; +} + +static int imx471_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx471 *sensor = to_imx471(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(imx471_supply_name), + sensor->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sensor->img_clk); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name), + sensor->supplies); + dev_err(dev, "failed to enable imaging clock: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(sensor->reset_gpio, 0); + + usleep_range(10000, 15000); + + return 0; +} + +static int imx471_enable_stream(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct imx471 *sensor = to_imx471(sd); + const struct imx471_mode *mode; + struct v4l2_mbus_framefmt *fmt; + int ret; + + ret = pm_runtime_resume_and_get(sensor->dev); + if (ret) + return ret; + + ret = imx471_identify_module(sensor); + if (ret) + goto error_powerdown; + + ret = cci_multi_reg_write(sensor->regmap, imx471_global_regs, + ARRAY_SIZE(imx471_global_regs), NULL); + if (ret) { + dev_err(sensor->dev, "failed to set global settings: %d\n", + ret); + goto error_powerdown; + } + + fmt = v4l2_subdev_state_get_format(state, 0); + mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes), + width, height, fmt->width, fmt->height); + + ret = cci_multi_reg_write(sensor->regmap, mode->default_mode_regs, + mode->default_mode_regs_length, NULL); + if (ret) { + dev_err(sensor->dev, "failed to set mode: %d\n", ret); + goto error_powerdown; + } + + ret = cci_write(sensor->regmap, IMX471_REG_DPGA_USE_GLOBAL_GAIN, 1, + NULL); + if (ret) + goto error_powerdown; + + ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler); + if (ret) + goto error_powerdown; + + ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT, + IMX471_MODE_STREAMING, NULL); + if (ret) + goto error_powerdown; + + __v4l2_ctrl_grab(sensor->vflip, true); + __v4l2_ctrl_grab(sensor->hflip, true); + + return ret; + +error_powerdown: + pm_runtime_put(sensor->dev); + + return ret; +} + +static int imx471_disable_stream(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct imx471 *sensor = to_imx471(sd); + int ret; + + ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT, + IMX471_MODE_STANDBY, NULL); + pm_runtime_put(sensor->dev); + + if (ret) + dev_err(sensor->dev, + "failed to disable stream with return value: %d\n", + ret); + + __v4l2_ctrl_grab(sensor->vflip, false); + __v4l2_ctrl_grab(sensor->hflip, false); + + return 0; +} + +static const struct v4l2_subdev_video_ops imx471_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops imx471_pad_ops = { + .enum_mbus_code = imx471_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx471_set_pad_format, + .get_selection = imx471_get_selection, + .enum_frame_size = imx471_enum_frame_size, + .enable_streams = imx471_enable_stream, + .disable_streams = imx471_disable_stream, +}; + +static const struct v4l2_subdev_ops imx471_subdev_ops = { + .video = &imx471_video_ops, + .pad = &imx471_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx471_internal_ops = { + .init_state = imx471_init_state, +}; + +static int imx471_init_controls(struct imx471 *sensor) +{ + const struct imx471_mode *mode = &imx471_modes[0]; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_ctrl *link_freq; + s64 exposure_max, hblank; + u64 pixel_rate; + int ret; + + ret = v4l2_fwnode_device_parse(sensor->dev, &props); + if (ret) { + dev_err(sensor->dev, "failed to parse fwnode: %d\n", ret); + return ret; + } + + ctrl_hdlr = &sensor->ctrl_handler; + v4l2_ctrl_handler_init(ctrl_hdlr, 12); + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx471_ctrl_ops, &props); + + link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &imx471_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, + link_freq_menu_items); + + /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + pixel_rate = div_u64(IMX471_LINK_FREQ_DEFAULT * 2 * 4, 10); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_PIXEL_RATE, pixel_rate, + pixel_rate, 1, pixel_rate); + + sensor->vblank = v4l2_ctrl_new_std(ctrl_hdlr, + &imx471_ctrl_ops, + V4L2_CID_VBLANK, + mode->fll_min - mode->height, + IMX471_FLL_MAX - mode->height, + 1, + mode->fll_def - mode->height); + + hblank = mode->llp - mode->width; + sensor->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + + /* fll >= exposure time + adjust parameter (default value is 18) */ + exposure_max = mode->fll_def - IMX471_EXPOSURE_MARGIN; + sensor->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX471_EXPOSURE_MIN, exposure_max, + IMX471_EXPOSURE_STEP, + IMX471_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX471_ANA_GAIN_MIN, IMX471_ANA_GAIN_MAX, + IMX471_ANA_GAIN_STEP, IMX471_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX471_DGTL_GAIN_MIN, IMX471_DGTL_GAIN_MAX, + IMX471_DGTL_GAIN_STEP, IMX471_DGTL_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx471_test_pattern_menu) - 1, + 0, 0, imx471_test_pattern_menu); + + sensor->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + sensor->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (ctrl_hdlr->error) { + dev_err(sensor->dev, "%s control init failed: %d\n", + __func__, ctrl_hdlr->error); + goto error; + } + + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ctrl_hdlr->error; +} + +static int imx471_check_hwcfg(struct imx471 *sensor) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *ep, *fwnode = dev_fwnode(sensor->dev); + unsigned long link_freq_bitmap; + struct clk *clk; + int ret; + + clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(sensor->dev, PTR_ERR(clk), + "can't get clock frequency\n"); + + if (clk_get_rate(clk) != IMX471_EXT_CLK) + return dev_err_probe(sensor->dev, -EINVAL, + "external clock %lu is not supported\n", + clk_get_rate(clk)); + + ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(sensor->dev, ret, + "parsing endpoint failed\n"); + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { + ret = dev_err_probe(sensor->dev, -EINVAL, + "number of CSI2 data lanes %u is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + goto done_endpoint_free; + } + + ret = v4l2_link_freq_to_bitmap(sensor->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + +done_endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int imx471_probe(struct i2c_client *client) +{ + struct imx471 *sensor; + int ret; + + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return dev_err_probe(&client->dev, -ENOMEM, + "failed to allocate memory\n"); + + sensor->dev = &client->dev; + + ret = imx471_check_hwcfg(sensor); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to check hwcfg: %d\n", ret); + + ret = imx471_get_regulators(sensor->dev, sensor); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to get regulators\n"); + + sensor->reset_gpio = devm_gpiod_get_optional(sensor->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset_gpio), + "failed to get reset gpio\n"); + + sensor->img_clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); + if (IS_ERR(sensor->img_clk)) + return dev_err_probe(sensor->dev, PTR_ERR(sensor->img_clk), + "failed to get imaging clock\n"); + + v4l2_i2c_subdev_init(&sensor->sd, client, &imx471_subdev_ops); + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return dev_err_probe(sensor->dev, PTR_ERR(sensor->regmap), + "failed to initialize CCI\n"); + + ret = imx471_power_on(sensor->dev); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to power on\n"); + + ret = imx471_identify_module(sensor); + if (ret) { + dev_err_probe(sensor->dev, ret, "failed to find sensor: %d\n", + ret); + goto error_power_off; + } + + ret = imx471_init_controls(sensor); + if (ret) { + dev_err_probe(sensor->dev, ret, "failed to init controls: %d\n", + ret); + goto error_power_off; + } + + sensor->sd.internal_ops = &imx471_internal_ops; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret) { + dev_err_probe(sensor->dev, ret, + "failed to init entity pads: %d\n", ret); + goto error_v4l2_ctrl_handler_free; + } + + sensor->sd.state_lock = sensor->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret < 0) { + dev_err_probe(sensor->dev, ret, "failed to init subdev: %d\n", + ret); + goto error_media_entity_pm; + } + + pm_runtime_set_active(sensor->dev); + pm_runtime_enable(sensor->dev); + + ret = v4l2_async_register_subdev_sensor(&sensor->sd); + if (ret < 0) + goto error_v4l2_subdev_cleanup; + + pm_runtime_idle(sensor->dev); + + return 0; + +error_v4l2_subdev_cleanup: + pm_runtime_disable(sensor->dev); + pm_runtime_set_suspended(sensor->dev); + v4l2_subdev_cleanup(&sensor->sd); + +error_media_entity_pm: + media_entity_cleanup(&sensor->sd.entity); + +error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); + +error_power_off: + imx471_power_off(sensor->dev); + + return ret; +} + +static void imx471_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + + if (!pm_runtime_status_suspended(&client->dev)) { + imx471_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx471_pm_ops, imx471_power_off, + imx471_power_on, NULL); + +static const struct acpi_device_id imx471_acpi_ids[] __maybe_unused = { + { "SONY471A" }, + { "TBE20A0" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, imx471_acpi_ids); + +static struct i2c_driver imx471_i2c_driver = { + .driver = { + .name = "imx471", + .acpi_match_table = ACPI_PTR(imx471_acpi_ids), + .pm = pm_sleep_ptr(&imx471_pm_ops), + }, + .probe = imx471_probe, + .remove = imx471_remove, +}; +module_i2c_driver(imx471_i2c_driver); + +MODULE_AUTHOR("Jimmy Su "); +MODULE_AUTHOR("Serin Yeh "); +MODULE_AUTHOR("Kate Hsuan "); +MODULE_DESCRIPTION("Sony imx471 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -1241,41 +1241,42 @@ static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, return ret; } +static const struct regmap_config regmap_config = { + .val_bits = 8, + .reg_bits = 16, + .disable_locking = true, +}; + static int ov2740_register_nvmem(struct i2c_client *client, struct ov2740 *ov2740) { struct nvm_data *nvm; - struct regmap_config regmap_config = { }; - struct nvmem_config nvmem_config = { }; struct regmap *regmap; struct device *dev = ov2740->dev; + struct nvmem_config nvmem_config = { + .name = dev_name(dev), + .dev = dev, + .read_only = true, + .root_only = true, + .owner = THIS_MODULE, + .compat = true, + .base_dev = dev, + .reg_read = ov2740_nvmem_read, + .stride = 1, + .word_size = 1, + .size = CUSTOMER_USE_OTP_SIZE, + }; nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL); if (!nvm) return -ENOMEM; - regmap_config.val_bits = 8; - regmap_config.reg_bits = 16; - regmap_config.disable_locking = true; regmap = devm_regmap_init_i2c(client, ®map_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); nvm->regmap = regmap; - - nvmem_config.name = dev_name(dev); - nvmem_config.dev = dev; - nvmem_config.read_only = true; - nvmem_config.root_only = true; - nvmem_config.owner = THIS_MODULE; - nvmem_config.compat = true; - nvmem_config.base_dev = dev; - nvmem_config.reg_read = ov2740_nvmem_read; - nvmem_config.reg_write = NULL; nvmem_config.priv = nvm; - nvmem_config.stride = 1; - nvmem_config.word_size = 1; - nvmem_config.size = CUSTOMER_USE_OTP_SIZE; nvm->nvmem = devm_nvmem_register(dev, &nvmem_config); if (IS_ERR(nvm->nvmem)) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -49,6 +49,8 @@ * Please keep the list sorted by ACPI HID. */ static const struct ipu_sensor_config ipu_supported_sensors[] = { + /* Himax HM1092 */ + IPU_SENSOR_CONFIG("HIMX1092", 2, 180000000, 180480000), /* Himax HM11B1 */ IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000), /* Himax HM2170 */ @@ -97,6 +99,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), /* Sony IMX471 */ IPU_SENSOR_CONFIG("SONY471A", 1, 200000000), + /* Sony IMX471 (found on Lenovo X1 Carbon G14) */ + IPU_SENSOR_CONFIG("TBE20A0", 1, 200000000), /* Toshiba T4KA3 */ IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), }; @@ -194,8 +198,8 @@ static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) { const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i]; struct acpi_device *consumer, *ivsc_adev; - acpi_handle handle = acpi_device_handle(ACPI_PTR(adev)); + for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1) /* camera sensor depends on IVSC in DSDT if exist */ for_each_acpi_consumer_dev(ivsc_adev, consumer)