From c298f9514f99b9dfde64bb5f07fe09ee8dfdcd48 Mon Sep 17 00:00:00 2001 From: David Plowman Date: Tue, 29 Jun 2021 14:43:01 +0100 Subject: [PATCH] media: i2c: imx477: Extend driver to support imx378 sensor The imx378 sensor is almost identical to the imx477 and can be supported as a "compatible" sensor with just a few extra register writes. Signed-off-by: David Plowman --- drivers/media/i2c/Kconfig | 2 +- drivers/media/i2c/imx477.c | 68 +++++++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 10 deletions(-) --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -807,7 +807,7 @@ config VIDEO_IMX477 depends on MEDIA_CAMERA_SUPPORT help This is a Video4Linux2 sensor driver for the Sony - IMX477 camera. + IMX477 camera. Also supports the Sony IMX378. To compile this driver as a module, choose M here: the module will be called imx477. --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ /* Chip ID */ #define IMX477_REG_CHIP_ID 0x0016 #define IMX477_CHIP_ID 0x0477 +#define IMX378_CHIP_ID 0x0378 #define IMX477_REG_MODE_SELECT 0x0100 #define IMX477_MODE_STANDBY 0x00 @@ -1069,6 +1071,11 @@ static const char * const imx477_supply_ #define IMX477_XCLR_MIN_DELAY_US 8000 #define IMX477_XCLR_DELAY_RANGE_US 1000 +struct imx477_compatible_data { + unsigned int chip_id; + struct imx477_reg_list extra_regs; +}; + struct imx477 { struct v4l2_subdev sd; struct media_pad pad[NUM_PADS]; @@ -1107,6 +1114,9 @@ struct imx477 { /* Current long exposure factor in use. Set through V4L2_CID_VBLANK */ unsigned int long_exp_shift; + + /* Any extra information related to different compatible sensors */ + const struct imx477_compatible_data *compatible_data; }; static inline struct imx477 *to_imx477(struct v4l2_subdev *_sd) @@ -1673,11 +1683,18 @@ static int imx477_start_streaming(struct { struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); const struct imx477_reg_list *reg_list; + const struct imx477_reg_list *extra_regs; int ret; if (!imx477->common_regs_written) { ret = imx477_write_regs(imx477, mode_common_regs, ARRAY_SIZE(mode_common_regs)); + if (!ret) { + extra_regs = &imx477->compatible_data->extra_regs; + ret = imx477_write_regs(imx477, extra_regs->regs, + extra_regs->num_of_regs); + } + if (ret) { dev_err(&client->dev, "%s failed to set common settings\n", __func__); @@ -1863,7 +1880,7 @@ static int imx477_get_regulators(struct } /* Verify chip ID */ -static int imx477_identify_module(struct imx477 *imx477) +static int imx477_identify_module(struct imx477 *imx477, u32 expected_id) { struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); int ret; @@ -1873,16 +1890,18 @@ static int imx477_identify_module(struct IMX477_REG_VALUE_16BIT, &val); if (ret) { dev_err(&client->dev, "failed to read chip id %x, with error %d\n", - IMX477_CHIP_ID, ret); + expected_id, ret); return ret; } - if (val != IMX477_CHIP_ID) { + if (val != expected_id) { dev_err(&client->dev, "chip id mismatch: %x!=%x\n", - IMX477_CHIP_ID, val); + expected_id, val); return -EIO; } + dev_info(&client->dev, "Device found is imx%x\n", val); + return 0; } @@ -2078,10 +2097,39 @@ error_out: return ret; } +static const struct imx477_compatible_data imx477_compatible = { + .chip_id = IMX477_CHIP_ID, + .extra_regs = { + .num_of_regs = 0, + .regs = NULL + } +}; + +static const struct imx477_reg imx378_regs[] = { + {0x3e35, 0x01}, + {0x4421, 0x08}, + {0x3ff9, 0x00}, +}; + +static const struct imx477_compatible_data imx378_compatible = { + .chip_id = IMX378_CHIP_ID, + .extra_regs = { + .num_of_regs = ARRAY_SIZE(imx378_regs), + .regs = imx378_regs + } +}; + +static const struct of_device_id imx477_dt_ids[] = { + { .compatible = "sony,imx477", .data = &imx477_compatible }, + { .compatible = "sony,imx378", .data = &imx378_compatible }, + { /* sentinel */ } +}; + static int imx477_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct imx477 *imx477; + const struct of_device_id *match; int ret; imx477 = devm_kzalloc(&client->dev, sizeof(*imx477), GFP_KERNEL); @@ -2090,6 +2138,12 @@ static int imx477_probe(struct i2c_clien v4l2_i2c_subdev_init(&imx477->sd, client, &imx477_subdev_ops); + match = of_match_device(imx477_dt_ids, dev); + if (!match) + return -ENODEV; + imx477->compatible_data = + (const struct imx477_compatible_data *)match->data; + /* Check the hardware configuration in device tree */ if (imx477_check_hwcfg(dev)) return -EINVAL; @@ -2126,7 +2180,7 @@ static int imx477_probe(struct i2c_clien if (ret) return ret; - ret = imx477_identify_module(imx477); + ret = imx477_identify_module(imx477, imx477->compatible_data->chip_id); if (ret) goto error_power_off; @@ -2198,10 +2252,6 @@ static int imx477_remove(struct i2c_clie return 0; } -static const struct of_device_id imx477_dt_ids[] = { - { .compatible = "sony,imx477" }, - { /* sentinel */ } -}; MODULE_DEVICE_TABLE(of, imx477_dt_ids); static const struct dev_pm_ops imx477_pm_ops = {