Skip to content

Commit 49a546b

Browse files
committed
Add ISX031 MIPI CSI support for kernel v6.12.34 Ubuntu 24.04 on RPL, ARL & MTL
Add patches: 0059-media-isx031-Add-support-on-ISX031-MIPI-CSI.patch 0060-media-isx031-ISX031-x2-lanes-couldn-t-stream-after-c.patch 0061-media-isx031-pm_runtime_get_sync-returns-Permission-.patch Verified sensor driver compatibility with RPL, ARL & MTL kernel 6.12.34 Verified sensor driver compatibility for isx031 mipi direct & gmsl Signed-off-by: Goh, Wei Khang1 <[email protected]>
1 parent 5237be7 commit 49a546b

File tree

4 files changed

+514
-0
lines changed

4 files changed

+514
-0
lines changed
Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
From c0bc32d888fb1c7d291b7b20f07f5d37cd18edcd Mon Sep 17 00:00:00 2001
2+
From: "Goh, Wei Khang1" <[email protected]>
3+
Date: Thu, 16 Oct 2025 10:23:20 +0800
4+
Subject: [PATCH 1/3] media: isx031: Add support on ISX031 MIPI CSI
5+
6+
* isx031: Add support on ISX031 MIPI CSI (D3)
7+
8+
- Verified sensor driver compatibility with RPL & ARL kernel 6.12
9+
- Added ISX031 sensor driver implementation
10+
- Added reference XML configuration for ISX031 sensor
11+
- Added reference BIOS configuration for Camera Option & Control Logic
12+
- Added user-space command for sensor testing
13+
14+
* isx031: Refine is_direct flag for sensor type checking
15+
16+
- Simplified sensor type selection (MIPI/GMSL) using is_direct flag
17+
18+
* isx031: Remove redundant isx031_reset()
19+
20+
- Removed isx031_reset() as sensor reset action completed by devm_gpiod_get_optional().
21+
22+
* isx031: Add sensor power control for isx031_suspend() & isx031_resume()
23+
24+
- Added sensor power off for isx031_suspend()
25+
- Added sensor power on for isx031_resume()
26+
- Fixed compilation warning
27+
28+
Signed-off-by: Lui, Jonathan Ming Jun <[email protected]>
29+
Signed-off-by: Goh, Wei Khang1 <[email protected]>
30+
---
31+
drivers/media/i2c/isx031.c | 146 +++++++++++++++++++++------
32+
drivers/media/i2c/max9x/serdes.c | 2 +-
33+
drivers/media/pci/intel/ipu-bridge.c | 2 +
34+
3 files changed, 119 insertions(+), 31 deletions(-)
35+
36+
diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c
37+
index acaf41a82fbe..8a6f60ccdacb 100644
38+
--- a/drivers/media/i2c/isx031.c
39+
+++ b/drivers/media/i2c/isx031.c
40+
@@ -34,6 +34,10 @@
41+
#define ISX031_REG_MODE_SET_F_LOCK 0xBEF0
42+
#define ISX031_MODE_UNLOCK 0x53
43+
44+
+struct isx031_info {
45+
+ bool is_direct;
46+
+};
47+
+
48+
struct isx031_reg {
49+
enum {
50+
ISX031_REG_LEN_DELAY = 0,
51+
@@ -79,7 +83,7 @@ struct isx031 {
52+
/* Previous mode */
53+
const struct isx031_mode *pre_mode;
54+
55+
- /* To serialize asynchronus callbacks */
56+
+ /* To serialize asynchronous callbacks */
57+
struct mutex mutex;
58+
59+
/* i2c client */
60+
@@ -90,6 +94,15 @@ struct isx031 {
61+
62+
/* Streaming on/off */
63+
bool streaming;
64+
+
65+
+ struct v4l2_ctrl_handler ctrls;
66+
+
67+
+ /* MIPI direct connection */
68+
+ bool is_direct;
69+
+};
70+
+
71+
+static const s64 isx031_link_frequencies[] = {
72+
+ 300000000ULL
73+
};
74+
75+
static const struct isx031_reg isx031_init_reg[] = {
76+
@@ -101,7 +114,7 @@ static const struct isx031_reg isx031_init_reg[] = {
77+
static const struct isx031_reg isx031_framesync_reg[] = {
78+
/* External sync */
79+
{ISX031_REG_LEN_08BIT, 0xBF14, 0x01}, /* SG_MODE_APL */
80+
- {ISX031_REG_LEN_08BIT, 0x8AFF, 0x0c}, /* Hi-Z (input setting or output disabled) */
81+
+ {ISX031_REG_LEN_08BIT, 0x8AFF, 0x0c}, /* Hi-Z (input setting or output disabled) */
82+
{ISX031_REG_LEN_08BIT, 0x0153, 0x00},
83+
{ISX031_REG_LEN_08BIT, 0x8AF0, 0x01}, /* external pulse-based sync */
84+
{ISX031_REG_LEN_08BIT, 0x0144, 0x00},
85+
@@ -224,21 +237,6 @@ static const struct isx031_mode supported_modes[] = {
86+
},
87+
};
88+
89+
-static int isx031_reset(struct gpio_desc *reset_gpio)
90+
-{
91+
- if (!IS_ERR_OR_NULL(reset_gpio)) {
92+
- gpiod_set_value_cansleep(reset_gpio, 0);
93+
- usleep_range(500, 1000);
94+
- gpiod_set_value_cansleep(reset_gpio, 1);
95+
- /*Needs to sleep for quite a while before register writes*/
96+
- usleep_range(200 * 1000, 200 * 1000 + 500);
97+
-
98+
- return 0;
99+
- }
100+
-
101+
- return -EINVAL;
102+
-}
103+
-
104+
static int isx031_read_reg(struct isx031 *isx031, u16 reg, u16 len, u32 *val)
105+
{
106+
struct i2c_client *client = isx031->client;
107+
@@ -351,7 +349,7 @@ static int isx031_mode_transit(struct isx031 *isx031, int state)
108+
return ret;
109+
}
110+
111+
- /*streaming transit to standby need 1 frame+5ms*/
112+
+ /* streaming transit to standby need 1 frame+5ms */
113+
retry = 50;
114+
while (retry--) {
115+
ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE,
116+
@@ -384,7 +382,7 @@ static int isx031_identify_module(struct isx031 *isx031)
117+
118+
dev_dbg(&client->dev, "sensor in mode 0x%x", val);
119+
120+
- /* if sensor alreay in ISX031_STATE_STARTUP, can access i2c write directly*/
121+
+ /* if sensor alreay in ISX031_STATE_STARTUP, can access i2c write directly */
122+
if (val == ISX031_STATE_STREAMING) {
123+
if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP))
124+
return ret;
125+
@@ -431,6 +429,14 @@ static int isx031_start_streaming(struct isx031 *isx031)
126+
dev_dbg(&client->dev, "same mode, skip write reg list");
127+
}
128+
129+
+ if (isx031->is_direct) {
130+
+ ret = __v4l2_ctrl_handler_setup(&isx031->ctrls);
131+
+ if (ret) {
132+
+ dev_err(&client->dev, "failed to setup ctrls");
133+
+ return ret;
134+
+ }
135+
+ }
136+
+
137+
ret = isx031_mode_transit(isx031, ISX031_STATE_STREAMING);
138+
if (ret) {
139+
dev_err(&client->dev, "failed to start streaming");
140+
@@ -509,6 +515,9 @@ static int __maybe_unused isx031_suspend(struct device *dev)
141+
142+
mutex_unlock(&isx031->mutex);
143+
144+
+ /* Active low gpio reset, set 1 to power off sensor */
145+
+ gpiod_set_value_cansleep(isx031->reset_gpio, 1);
146+
+
147+
return 0;
148+
}
149+
150+
@@ -519,9 +528,23 @@ static int __maybe_unused isx031_resume(struct device *dev)
151+
struct isx031 *isx031 = to_isx031(sd);
152+
const struct isx031_reg_list *reg_list;
153+
int ret;
154+
+ int count = 0;
155+
156+
- if (isx031->reset_gpio != NULL)
157+
- isx031_reset(isx031->reset_gpio);
158+
+ /* Active low gpio reset, set 0 to power on sensor,
159+
+ * sensor must on back before start resume
160+
+ */
161+
+ if (isx031->reset_gpio != NULL) {
162+
+ do {
163+
+ gpiod_set_value_cansleep(isx031->reset_gpio, 0);
164+
+ ret = gpiod_get_value_cansleep(isx031->reset_gpio);
165+
+ usleep_range(200 * 1000, 200 * 1000 + 500);
166+
+
167+
+ if (++count >= 5) {
168+
+ dev_err(&client->dev, "%s: failed to power on reset gpio, reset gpio is %d", __func__, ret);
169+
+ break;
170+
+ }
171+
+ } while (ret != 0);
172+
+ }
173+
174+
ret = isx031_identify_module(isx031);
175+
if (ret == 0) {
176+
@@ -755,10 +778,40 @@ static void isx031_remove(struct i2c_client *client)
177+
#endif
178+
}
179+
180+
+static int isx031_set_ctrl(struct v4l2_ctrl *ctrl)
181+
+{
182+
+ return 0;
183+
+}
184+
+
185+
+static const struct v4l2_ctrl_ops isx031_ctrl_ops = {
186+
+ .s_ctrl = isx031_set_ctrl,
187+
+};
188+
+
189+
+static int isx031_ctrls_init(struct isx031 *sensor)
190+
+{
191+
+ int ret = 0;
192+
+ struct v4l2_ctrl *ctrl;
193+
+
194+
+ v4l2_ctrl_handler_init(&sensor->ctrls, 10);
195+
+
196+
+ /* There's a need to set the link frequency because IPU6 dictates it. */
197+
+ ctrl = v4l2_ctrl_new_int_menu(&sensor->ctrls, &isx031_ctrl_ops,
198+
+ V4L2_CID_LINK_FREQ,
199+
+ ARRAY_SIZE(isx031_link_frequencies) - 1, 0,
200+
+ isx031_link_frequencies);
201+
+
202+
+ if (ctrl)
203+
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
204+
+
205+
+ sensor->sd.ctrl_handler = &sensor->ctrls;
206+
+ return ret;
207+
+}
208+
+
209+
static int isx031_probe(struct i2c_client *client)
210+
{
211+
struct v4l2_subdev *sd;
212+
struct isx031 *isx031;
213+
+ const struct isx031_info *info;
214+
const struct isx031_reg_list *reg_list;
215+
int ret;
216+
217+
@@ -772,19 +825,31 @@ static int isx031_probe(struct i2c_client *client)
218+
dev_warn(&client->dev, "no platform data provided\n");
219+
220+
isx031->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
221+
- GPIOD_OUT_HIGH);
222+
+ GPIOD_OUT_LOW);
223+
+
224+
if (IS_ERR(isx031->reset_gpio))
225+
return -EPROBE_DEFER;
226+
else if (isx031->reset_gpio == NULL)
227+
dev_warn(&client->dev, "Reset GPIO not found");
228+
- else {
229+
+ else
230+
dev_dbg(&client->dev, "Found reset GPIO");
231+
- isx031_reset(isx031->reset_gpio);
232+
- }
233+
+
234+
+ info = device_get_match_data(&client->dev);
235+
+ if (info)
236+
+ isx031->is_direct = info->is_direct;
237+
+ else
238+
+ isx031->is_direct = false;
239+
240+
/* initialize subdevice */
241+
sd = &isx031->sd;
242+
v4l2_i2c_subdev_init(sd, client, &isx031_subdev_ops);
243+
+ if (isx031->is_direct) {
244+
+ ret = isx031_ctrls_init(isx031);
245+
+ if (ret) {
246+
+ dev_err(&client->dev, "failed to init sensor ctrls: %d", ret);
247+
+ return ret;
248+
+ }
249+
+ }
250+
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 10, 0)
251+
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
252+
#else
253+
@@ -798,9 +863,13 @@ static int isx031_probe(struct i2c_client *client)
254+
isx031->pad.flags = MEDIA_PAD_FL_SOURCE;
255+
ret = media_entity_pads_init(&sd->entity, 1, &isx031->pad);
256+
if (ret < 0) {
257+
- dev_err(&client->dev,
258+
- "%s : media entity init Failed %d\n", __func__, ret);
259+
- return ret;
260+
+ dev_err(&client->dev, "failed to init entity pads: %d", ret);
261+
+ goto probe_error_v4l2_ctrl_handler_free;
262+
+ }
263+
+
264+
+ if (isx031->is_direct) {
265+
+ isx031->sd.state_lock = isx031->sd.ctrl_handler->lock;
266+
+ v4l2_subdev_init_finalize(&isx031->sd);
267+
}
268+
269+
ret = isx031_identify_module(isx031);
270+
@@ -851,6 +920,9 @@ static int isx031_probe(struct i2c_client *client)
271+
pm_runtime_disable(&client->dev);
272+
mutex_destroy(&isx031->mutex);
273+
274+
+probe_error_v4l2_ctrl_handler_free:
275+
+ v4l2_ctrl_handler_free(isx031->sd.ctrl_handler);
276+
+
277+
return ret;
278+
}
279+
280+
@@ -860,13 +932,25 @@ static const struct dev_pm_ops isx031_pm_ops = {
281+
282+
static const struct i2c_device_id isx031_id_table[] = {
283+
{ "isx031", 0 },
284+
- { /* sentinel */ },
285+
+ {}
286+
};
287+
MODULE_DEVICE_TABLE(i2c, isx031_id_table);
288+
289+
+static const struct isx031_info isx031_mipi_info = {
290+
+ .is_direct = true,
291+
+};
292+
+
293+
+static const struct acpi_device_id isx031_acpi_ids[] = {
294+
+ { "INTC3031", (kernel_ulong_t)&isx031_mipi_info },
295+
+ {}
296+
+};
297+
+
298+
+MODULE_DEVICE_TABLE(acpi, isx031_acpi_ids);
299+
+
300+
static struct i2c_driver isx031_i2c_driver = {
301+
.driver = {
302+
.name = "isx031",
303+
+ .acpi_match_table = ACPI_PTR(isx031_acpi_ids),
304+
.pm = &isx031_pm_ops,
305+
},
306+
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
307+
@@ -880,6 +964,8 @@ static struct i2c_driver isx031_i2c_driver = {
308+
309+
module_i2c_driver(isx031_i2c_driver);
310+
311+
-MODULE_AUTHOR("Hao Yao <[email protected]>");
312+
MODULE_DESCRIPTION("isx031 sensor driver");
313+
+MODULE_AUTHOR("Hao Yao <[email protected]>");
314+
+MODULE_AUTHOR("Jonathan Lui <[email protected]>");
315+
+MODULE_AUTHOR("Wei Khang, Goh <[email protected]>");
316+
MODULE_LICENSE("GPL v2");
317+
diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c
318+
index f29949d96aa7..424d0014d68e 100644
319+
--- a/drivers/media/i2c/max9x/serdes.c
320+
+++ b/drivers/media/i2c/max9x/serdes.c
321+
@@ -1655,7 +1655,7 @@ static int max9x_registered(struct v4l2_subdev *sd)
322+
.dev_id = "",
323+
.table = {
324+
GPIO_LOOKUP("", 0, "reset",
325+
- GPIO_ACTIVE_HIGH),
326+
+ GPIO_ACTIVE_LOW),
327+
{}
328+
},
329+
};
330+
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
331+
index 4e4f86a1f0b8..def371fe499b 100644
332+
--- a/drivers/media/pci/intel/ipu-bridge.c
333+
+++ b/drivers/media/pci/intel/ipu-bridge.c
334+
@@ -91,6 +91,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
335+
IPU_SENSOR_CONFIG("INTC10C5", 0),
336+
/* Lontium lt6911uxc */
337+
IPU_SENSOR_CONFIG("INTC10B1", 0),
338+
+ /* D3 Embedded ISX031 */
339+
+ IPU_SENSOR_CONFIG("INTC3031", 1, 300000000)
340+
};
341+
342+
static const struct ipu_sensor_config ipu_supported_sensors_dummy[] = {
343+
--
344+
2.17.1
345+

0 commit comments

Comments
 (0)