Build system: x86_64 Build-tested: bcm2708, bcm2709, bcm2710, bcm2711 Run-tested: bcm2708/RPiB+, bcm2709/RPi3B, bcm2710/RPi3B, bcm2711/RPi4B Signed-off-by: Marty Jones <mj8263788@gmail.com> Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
		
			
				
	
	
		
			191 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 99a58cae54a24652ab2e088d0eec2a1a31b22d86 Mon Sep 17 00:00:00 2001
 | 
						|
From: David Plowman <david.plowman@raspberrypi.com>
 | 
						|
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 <david.plowman@raspberrypi.com>
 | 
						|
---
 | 
						|
 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
 | 
						|
@@ -823,7 +823,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 <linux/gpio/consumer.h>
 | 
						|
 #include <linux/i2c.h>
 | 
						|
 #include <linux/module.h>
 | 
						|
+#include <linux/of_device.h>
 | 
						|
 #include <linux/pm_runtime.h>
 | 
						|
 #include <linux/regulator/consumer.h>
 | 
						|
 #include <media/v4l2-ctrls.h>
 | 
						|
@@ -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 = {
 |