1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
From c298f9514f99b9dfde64bb5f07fe09ee8dfdcd48 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
@@ -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 <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 = {
|