Skip to content

Commit e2dfd8d

Browse files
josuahalanridel
andcommitted
drivers: video: imx219: add new image sensor from Sony
Add support for the IMX219 MIPI-only image sensor, as present on the Raspberry Pi camera v2 module usable with various 15-pin and 22-pin MIPI lanes. Signed-off-by: Josuah Demangeon <me@josuah.net> Co-authored-by: Alan Shaju <alanshaju@rideltech.com>
1 parent 29e2d99 commit e2dfd8d

File tree

6 files changed

+306
-0
lines changed

6 files changed

+306
-0
lines changed

drivers/video/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ zephyr_library_sources_ifdef(CONFIG_VIDEO_ESP32 video_esp32_dvp.c)
1818
zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_SDMA video_mcux_smartdma.c)
1919
zephyr_library_sources_ifdef(CONFIG_VIDEO_EMUL_IMAGER video_emul_imager.c)
2020
zephyr_library_sources_ifdef(CONFIG_VIDEO_EMUL_RX video_emul_rx.c)
21+
zephyr_library_sources_ifdef(CONFIG_VIDEO_IMX219 imx219.c)

drivers/video/Kconfig

+2
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,6 @@ source "drivers/video/Kconfig.emul_imager"
8686

8787
source "drivers/video/Kconfig.emul_rx"
8888

89+
source "drivers/video/Kconfig.imx219"
90+
8991
endif # VIDEO

drivers/video/Kconfig.imx219

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2024-2025 tinyVision.ai Inc.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config VIDEO_IMX219
5+
bool "IMX219 8 MP CMOS image sensor"
6+
select I2C
7+
depends on DT_HAS_SONY_IMX219_ENABLED
8+
default y
9+
help
10+
Enable the driver for the Sony IMX219 8 Mega-Pixel CMOS image sensor

drivers/video/imx219.c

+276
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
/*
2+
* Copyright (c) 2024-2025 tinyVision.ai Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT sony_imx219
8+
9+
#include <zephyr/device.h>
10+
#include <zephyr/drivers/i2c.h>
11+
#include <zephyr/drivers/video.h>
12+
#include <zephyr/drivers/video-controls.h>
13+
#include <zephyr/kernel.h>
14+
#include <zephyr/sys/byteorder.h>
15+
#include <zephyr/logging/log.h>
16+
17+
#include "video_common.h"
18+
19+
LOG_MODULE_REGISTER(imx219, CONFIG_VIDEO_LOG_LEVEL);
20+
21+
#define IMX219_FULL_WIDTH 3280
22+
#define IMX219_FULL_HEIGHT 2464
23+
24+
#define IMX219_REG8(addr) ((addr) | VIDEO_REG_ADDR16_DATA8)
25+
#define IMX219_REG16(addr) ((addr) | VIDEO_REG_ADDR16_DATA16_BE)
26+
27+
#define IMX219_REG_CHIP_ID IMX219_REG16(0x0000)
28+
#define IMX219_REG_SOFTWARE_RESET IMX219_REG8(0x0103)
29+
#define IMX219_REG_MODE_SELECT IMX219_REG8(0x0100)
30+
#define IMX219_REG_ANALOG_GAIN IMX219_REG8(0x0157)
31+
#define IMX219_REG_DIGITAL_GAIN IMX219_REG16(0x0158)
32+
#define IMX219_REG_INTEGRATION_TIME IMX219_REG16(0x015A)
33+
#define IMX219_REG_TESTPATTERN IMX219_REG16(0x0600)
34+
#define IMX219_REG_MODE_SELECT IMX219_REG8(0x0100)
35+
#define IMX219_REG_CSI_LANE_MODE IMX219_REG8(0x0114)
36+
#define IMX219_REG_DPHY_CTRL IMX219_REG8(0x0128)
37+
#define IMX219_REG_EXCK_FREQ IMX219_REG16(0x012a)
38+
#define IMX219_REG_PREPLLCK_VT_DIV IMX219_REG8(0x0304)
39+
#define IMX219_REG_PREPLLCK_OP_DIV IMX219_REG8(0x0305)
40+
#define IMX219_REG_OPPXCK_DIV IMX219_REG8(0x0309)
41+
#define IMX219_REG_VTPXCK_DIV IMX219_REG8(0x0301)
42+
#define IMX219_REG_VTSYCK_DIV IMX219_REG8(0x0303)
43+
#define IMX219_REG_OPSYCK_DIV IMX219_REG8(0x030b)
44+
#define IMX219_REG_PLL_VT_MPY IMX219_REG16(0x0306)
45+
#define IMX219_REG_PLL_OP_MPY IMX219_REG16(0x030c)
46+
#define IMX219_REG_LINE_LENGTH_A IMX219_REG16(0x0162)
47+
#define IMX219_REG_CSI_DATA_FORMAT_A IMX219_REG16(0x018c)
48+
#define IMX219_REG_BINNING_MODE_H IMX219_REG8(0x0174)
49+
#define IMX219_REG_BINNING_MODE_V IMX219_REG8(0x0175)
50+
#define IMX219_REG_ORIENTATION IMX219_REG8(0x0172)
51+
#define IMX219_REG_FRM_LENGTH_A IMX219_REG16(0x0160)
52+
#define IMX219_REG_FRM_LENGTH_A IMX219_REG16(0x0160)
53+
#define IMX219_REG_X_ADD_STA_A IMX219_REG16(0x0164)
54+
#define IMX219_REG_X_ADD_END_A IMX219_REG16(0x0166)
55+
#define IMX219_REG_Y_ADD_STA_A IMX219_REG16(0x0168)
56+
#define IMX219_REG_Y_ADD_END_A IMX219_REG16(0x016a)
57+
#define IMX219_REG_X_OUTPUT_SIZE IMX219_REG16(0x016c)
58+
#define IMX219_REG_Y_OUTPUT_SIZE IMX219_REG16(0x016e)
59+
#define IMX219_REG_X_ODD_INC_A IMX219_REG8(0x0170)
60+
#define IMX219_REG_Y_ODD_INC_A IMX219_REG8(0x0171)
61+
62+
/* Registers to crop down a resolution to a centered width and height */
63+
static const struct video_reg init_regs[] = {
64+
{IMX219_REG_MODE_SELECT, 0x00}, /* Standby */
65+
66+
/* Enable access to registers from 0x3000 to 0x5fff */
67+
{IMX219_REG8(0x30eb), 0x05},
68+
{IMX219_REG8(0x30eb), 0x0c},
69+
{IMX219_REG8(0x300a), 0xff},
70+
{IMX219_REG8(0x300b), 0xff},
71+
{IMX219_REG8(0x30eb), 0x05},
72+
{IMX219_REG8(0x30eb), 0x09},
73+
74+
/* MIPI configuration registers */
75+
{IMX219_REG_CSI_LANE_MODE, 0x01}, /* 2 Lanes */
76+
{IMX219_REG_DPHY_CTRL, 0x00}, /* Timing auto */
77+
78+
/* Clock configuration registers */
79+
{IMX219_REG_EXCK_FREQ, 24 << 8}, /* 24 MHz */
80+
{IMX219_REG_PREPLLCK_VT_DIV, 0x03}, /* Auto */
81+
{IMX219_REG_PREPLLCK_OP_DIV, 0x03}, /* Auto */
82+
{IMX219_REG_OPPXCK_DIV, 10}, /* 10-bits per pixel */
83+
{IMX219_REG_VTPXCK_DIV, 5},
84+
{IMX219_REG_VTSYCK_DIV, 1},
85+
{IMX219_REG_OPSYCK_DIV, 1},
86+
{IMX219_REG_PLL_VT_MPY, 32}, /* Pixel/Sys clock multiplier */
87+
{IMX219_REG_PLL_OP_MPY, 50}, /* Output clock multiplier */
88+
89+
/* Undocumented registers */
90+
{IMX219_REG8(0x455e), 0x00},
91+
{IMX219_REG8(0x471e), 0x4b},
92+
{IMX219_REG8(0x4767), 0x0f},
93+
{IMX219_REG8(0x4750), 0x14},
94+
{IMX219_REG8(0x4540), 0x00},
95+
{IMX219_REG8(0x47b4), 0x14},
96+
{IMX219_REG8(0x4713), 0x30},
97+
{IMX219_REG8(0x478b), 0x10},
98+
{IMX219_REG8(0x478f), 0x10},
99+
{IMX219_REG8(0x4793), 0x10},
100+
{IMX219_REG8(0x4797), 0x0e},
101+
{IMX219_REG8(0x479b), 0x0e},
102+
103+
/* Timing and format registers */
104+
{IMX219_REG_LINE_LENGTH_A, 3448},
105+
{IMX219_REG_X_ODD_INC_A, 1},
106+
{IMX219_REG_Y_ODD_INC_A, 1},
107+
{IMX219_REG_CSI_DATA_FORMAT_A, (10 << 8) | 10}, /* 10-bits per pixels */
108+
109+
/* Custom defaults */
110+
{IMX219_REG_BINNING_MODE_H, 0x00},
111+
{IMX219_REG_BINNING_MODE_V, 0x00},
112+
{IMX219_REG_DIGITAL_GAIN, 5000},
113+
{IMX219_REG_ANALOG_GAIN, 240},
114+
{IMX219_REG_INTEGRATION_TIME, 1600},
115+
{IMX219_REG_ORIENTATION, 0x03},
116+
117+
{0},
118+
};
119+
120+
#define IMX219_REGS_CROP(width, height) \
121+
{IMX219_REG_X_ADD_STA_A, (IMX219_FULL_WIDTH - (width)) / 2}, \
122+
{IMX219_REG_X_ADD_END_A, (IMX219_FULL_WIDTH + (width)) / 2 - 1}, \
123+
{IMX219_REG_Y_ADD_STA_A, (IMX219_FULL_HEIGHT - (height)) / 2}, \
124+
{IMX219_REG_Y_ADD_END_A, (IMX219_FULL_HEIGHT + (height)) / 2 - 1}
125+
126+
static const struct video_reg size_1920x1080[] = {
127+
IMX219_REGS_CROP(1920, 1080),
128+
{IMX219_REG_X_OUTPUT_SIZE, 1920},
129+
{IMX219_REG_Y_OUTPUT_SIZE, 1080},
130+
{IMX219_REG_FRM_LENGTH_A, 1080 + 120},
131+
{0},
132+
};
133+
134+
static const struct video_reg size_640x480[] = {
135+
IMX219_REGS_CROP(640, 480),
136+
{IMX219_REG_X_OUTPUT_SIZE, 640},
137+
{IMX219_REG_Y_OUTPUT_SIZE, 480},
138+
{IMX219_REG_FRM_LENGTH_A, 480 + 120},
139+
{0},
140+
};
141+
142+
static const struct video_imager_mode modes_1920x1080[] = {
143+
{.fps = 30, .regs = {size_1920x1080}},
144+
{0},
145+
};
146+
147+
static const struct video_imager_mode modes_640x480[] = {
148+
{.fps = 30, .regs = {size_640x480}},
149+
{0},
150+
};
151+
152+
static const struct video_imager_mode *modes[] = {
153+
modes_1920x1080,
154+
modes_640x480,
155+
NULL,
156+
};
157+
158+
static const struct video_format_cap fmts[] = {
159+
VIDEO_IMAGER_FORMAT_CAP(VIDEO_PIX_FMT_BGGR8, 1920, 1080),
160+
VIDEO_IMAGER_FORMAT_CAP(VIDEO_PIX_FMT_BGGR8, 640, 480),
161+
{0},
162+
};
163+
164+
static int imx219_set_stream(const struct device *dev, bool on)
165+
{
166+
const struct video_imager_config *cfg = dev->config;
167+
168+
return video_write_cci_reg(&cfg->i2c, IMX219_REG_MODE_SELECT, on ? 0x01 : 0x00);
169+
}
170+
171+
static int imx219_get_ctrl(const struct device *dev, unsigned int cid, void *value)
172+
{
173+
const struct video_imager_config *cfg = dev->config;
174+
uint32_t reg;
175+
int ret;
176+
177+
switch (cid) {
178+
case VIDEO_CID_EXPOSURE:
179+
ret = video_read_cci_reg(&cfg->i2c, IMX219_REG_INTEGRATION_TIME, &reg);
180+
*(uint32_t *)value = reg;
181+
return ret;
182+
case VIDEO_CID_GAIN:
183+
ret = video_read_cci_reg(&cfg->i2c, IMX219_REG_ANALOG_GAIN, &reg);
184+
*(uint32_t *)value = reg;
185+
return ret;
186+
default:
187+
LOG_WRN("Control not supported");
188+
return -ENOTSUP;
189+
}
190+
}
191+
192+
static int imx219_set_ctrl(const struct device *dev, unsigned int cid, void *value)
193+
{
194+
const struct video_imager_config *cfg = dev->config;
195+
196+
switch (cid) {
197+
case VIDEO_CID_EXPOSURE:
198+
/* Values for normal frame rate, different range for low frame rate mode */
199+
return video_write_cci_reg(&cfg->i2c, IMX219_REG_INTEGRATION_TIME, (int)value);
200+
case VIDEO_CID_GAIN:
201+
return video_write_cci_reg(&cfg->i2c, IMX219_REG_ANALOG_GAIN, (int)value);
202+
case VIDEO_CID_TEST_PATTERN:
203+
return video_write_cci_reg(&cfg->i2c, IMX219_REG_TESTPATTERN, (int)value);
204+
default:
205+
LOG_WRN("Control not supported");
206+
return -ENOTSUP;
207+
}
208+
}
209+
210+
static const DEVICE_API(video, imx219_driver_api) = {
211+
/* Local implementation */
212+
.set_stream = imx219_set_stream,
213+
.set_ctrl = imx219_set_ctrl,
214+
.get_ctrl = imx219_get_ctrl,
215+
/* Default implementation */
216+
.set_format = video_imager_set_fmt,
217+
.get_format = video_imager_get_fmt,
218+
.get_caps = video_imager_get_caps,
219+
.set_frmival = video_imager_set_frmival,
220+
.get_frmival = video_imager_get_frmival,
221+
.enum_frmival = video_imager_enum_frmival,
222+
};
223+
224+
static int imx219_init(const struct device *dev)
225+
{
226+
const struct video_imager_config *cfg = dev->config;
227+
uint32_t reg;
228+
int ret;
229+
230+
if (!device_is_ready(cfg->i2c.bus)) {
231+
LOG_ERR("I2C device %s is not ready", cfg->i2c.bus->name);
232+
return -ENODEV;
233+
}
234+
235+
k_sleep(K_MSEC(1));
236+
237+
ret = video_write_cci_reg(&cfg->i2c, IMX219_REG_SOFTWARE_RESET, 1);
238+
if (ret != 0) {
239+
goto err;
240+
}
241+
242+
/* Initializing time of silicon (t5): 32000 clock cycles, 5.3 msec for 6 MHz */
243+
k_sleep(K_MSEC(6));
244+
245+
ret = video_read_cci_reg(&cfg->i2c, IMX219_REG_CHIP_ID, &reg);
246+
if (ret != 0) {
247+
LOG_ERR("Unable to read Chip ID");
248+
goto err;
249+
}
250+
251+
if (reg != 0x0219) {
252+
LOG_ERR("Wrong chip ID %04x", reg);
253+
return -ENODEV;
254+
}
255+
256+
return video_imager_init(dev, init_regs, 0);
257+
err:
258+
LOG_ERR("Error during %s initialization: %s", dev->name, strerror(-ret));
259+
return ret;
260+
}
261+
262+
#define IMX219_INIT(inst) \
263+
static struct video_imager_data imx219_data_##inst; \
264+
\
265+
static const struct video_imager_config imx219_cfg_##inst = { \
266+
.i2c = I2C_DT_SPEC_INST_GET(inst), \
267+
.fmts = fmts, \
268+
.modes = modes, \
269+
.data = &imx219_data_##inst, \
270+
.write_multi = video_write_cci_multi, \
271+
}; \
272+
\
273+
DEVICE_DT_INST_DEFINE(inst, &imx219_init, NULL, NULL, &imx219_cfg_##inst, POST_KERNEL, \
274+
CONFIG_VIDEO_INIT_PRIORITY, &imx219_driver_api);
275+
276+
DT_INST_FOREACH_STATUS_OKAY(IMX219_INIT)

dts/bindings/video/sony,imx219.yaml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2025, tinyVision.ai Inc.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: IMX219 8 Mega-Pixel CMOS image sensor
5+
6+
compatible: "sony,imx219"
7+
8+
include: i2c-device.yaml
9+
10+
child-binding:
11+
child-binding:
12+
include: video-interfaces.yaml

tests/drivers/build_all/video/app.overlay

+5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@
6666
reset-gpios = <&test_gpio 0 0>;
6767
};
6868

69+
test_i2c_imx219: imx219@6 {
70+
compatible = "sony,imx219";
71+
reg = <0x6>;
72+
};
73+
6974
test_i2c_video_emul_imager: video_emul_imager@6 {
7075
compatible = "zephyr,video-emul-imager";
7176
reg = <0x6>;

0 commit comments

Comments
 (0)