Skip to content

Commit 63c60da

Browse files
committed
drivers: video: Himax HM01B0 camera sensor driver
Add Himax HM01B0 camera sensor driver. It depends on I2C and it is required to configure the camera. Signed-off-by: Antonino Scarpaci <[email protected]>
1 parent 5945a3f commit 63c60da

File tree

5 files changed

+359
-0
lines changed

5 files changed

+359
-0
lines changed

drivers/video/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ zephyr_library_sources_ifdef(CONFIG_VIDEO_EMUL_RX video_emul_rx.c)
2525
zephyr_library_sources_ifdef(CONFIG_VIDEO_IMX335 imx335.c)
2626
zephyr_library_sources_ifdef(CONFIG_VIDEO_ST_MIPID02 video_st_mipid02.c)
2727
zephyr_library_sources_ifdef(CONFIG_VIDEO_STM32_DCMIPP video_stm32_dcmipp.c)
28+
zephyr_library_sources_ifdef(CONFIG_VIDEO_HIMAX_HM01B0 hm01b0.c)
2829

2930
zephyr_linker_sources(DATA_SECTIONS video.ld)

drivers/video/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ source "drivers/video/Kconfig.ov9655"
8484

8585
source "drivers/video/Kconfig.gc2145"
8686

87+
source "drivers/video/Kconfig.hm01b0"
88+
8789
source "drivers/video/Kconfig.mcux_sdma"
8890

8991
source "drivers/video/Kconfig.emul_imager"

drivers/video/Kconfig.hm01b0

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2025 The Zephyr Project Contributors
2+
# SPDX-License-Identifier: Apache-2.0
3+
config VIDEO_HIMAX_HM01B0
4+
bool "Real-time monochrome camera Himax HM01B0 sensor"
5+
depends on DT_HAS_HIMAX_HM01B0_ENABLED
6+
select I2C
7+
default y
8+
help
9+
Enable driver for monochrome camera Himax HM01B0 sensor.

drivers/video/hm01b0.c

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
/*
2+
* Copyright The Zephyr Project Contributors
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT himax_hm01b0
8+
9+
#include <zephyr/drivers/i2c.h>
10+
#include <zephyr/drivers/video-controls.h>
11+
#include <zephyr/drivers/video.h>
12+
#include <zephyr/kernel.h>
13+
#include <zephyr/logging/log.h>
14+
#include <zephyr/sys/byteorder.h>
15+
#include <zephyr/sys/util.h>
16+
17+
#include "video_device.h"
18+
#include "video_common.h"
19+
20+
LOG_MODULE_REGISTER(hm01b0, CONFIG_VIDEO_LOG_LEVEL);
21+
#define MAX_FRAME_RATE 10
22+
#define MIN_FRAME_RATE 1
23+
#define HM01B0_ID 0x01B0
24+
25+
#define HM01B0_REG8(addr) ((addr) | VIDEO_REG_ADDR16_DATA8)
26+
#define HM01B0_REG16(addr) ((addr) | VIDEO_REG_ADDR16_DATA16_BE)
27+
#define HM01B0_CCI_ID HM01B0_REG16(0x0000)
28+
#define HM01B0_CCI_STS HM01B0_REG8(0x0100)
29+
#define HM01B0_CCI_RESET HM01B0_REG8(0x0103)
30+
#define HM01B0_CCI_GRP_PARAM_HOLD HM01B0_REG8(0x0104)
31+
#define HM01B0_CCI_INTEGRATION_H HM01B0_REG16(0x0202)
32+
#define HM01B0_CCI_FRAME_LENGTH_LINES HM01B0_REG16(0x0340)
33+
#define HM01B0_CCI_LINE_LENGTH_PCLK HM01B0_REG16(0x0342)
34+
#define HM01B0_CCI_WIDTH HM01B0_REG8(0x0383)
35+
#define HM01B0_CCI_HEIGHT HM01B0_REG8(0x0387)
36+
#define HM01B0_CCI_BINNING_MODE HM01B0_REG8(0x0390)
37+
#define HM01B0_CCI_QVGA_WIN_EN HM01B0_REG8(0x3010)
38+
#define HM01B0_CCI_BIT_CONTROL HM01B0_REG8(0x3059)
39+
#define HM01B0_CCI_OSC_CLOCK_DIV HM01B0_REG8(0x3060)
40+
41+
enum hm01b0_resolution {
42+
RESOLUTION_160x120,
43+
RESOLUTION_320x240,
44+
RESOLUTION_320x320,
45+
};
46+
47+
struct video_reg hm01b0_160x120_regs[] = {
48+
{HM01B0_CCI_WIDTH, 0x3},
49+
{HM01B0_CCI_HEIGHT, 0x3},
50+
{HM01B0_CCI_BINNING_MODE, 0x3},
51+
{HM01B0_CCI_QVGA_WIN_EN, 0x1},
52+
{HM01B0_CCI_FRAME_LENGTH_LINES, 0x80},
53+
{HM01B0_CCI_LINE_LENGTH_PCLK, 0xD7},
54+
};
55+
56+
struct video_reg hm01b0_320x240_regs[] = {
57+
{HM01B0_CCI_WIDTH, 0x1},
58+
{HM01B0_CCI_HEIGHT, 0x1},
59+
{HM01B0_CCI_BINNING_MODE, 0x0},
60+
{HM01B0_CCI_QVGA_WIN_EN, 0x1},
61+
{HM01B0_CCI_FRAME_LENGTH_LINES, 0x104},
62+
{HM01B0_CCI_LINE_LENGTH_PCLK, 0x178},
63+
};
64+
65+
struct video_reg hm01b0_320x320_regs[] = {
66+
{HM01B0_CCI_WIDTH, 0x1},
67+
{HM01B0_CCI_HEIGHT, 0x1},
68+
{HM01B0_CCI_BINNING_MODE, 0x0},
69+
{HM01B0_CCI_QVGA_WIN_EN, 0x0},
70+
{HM01B0_CCI_FRAME_LENGTH_LINES, 0x158},
71+
{HM01B0_CCI_LINE_LENGTH_PCLK, 0x178},
72+
};
73+
74+
struct video_reg *hm01b0_init_regs[] = {
75+
[RESOLUTION_160x120] = hm01b0_160x120_regs,
76+
[RESOLUTION_320x240] = hm01b0_320x240_regs,
77+
[RESOLUTION_320x320] = hm01b0_320x320_regs,
78+
};
79+
80+
struct hm01b0_data {
81+
struct video_format fmt;
82+
uint8_t ctrl_val;
83+
};
84+
85+
struct hm01b0_config {
86+
const struct i2c_dt_spec i2c;
87+
const uint8_t data_bits;
88+
};
89+
90+
#define HM01B0_VIDEO_FORMAT_CAP(width, height, format) \
91+
{ \
92+
.pixelformat = (format), \
93+
.width_min = (width), \
94+
.width_max = (width), \
95+
.height_min = (height), \
96+
.height_max = (height), \
97+
.width_step = 0, \
98+
.height_step = 0, \
99+
}
100+
101+
static const struct video_format_cap hm01b0_fmts[] = {
102+
HM01B0_VIDEO_FORMAT_CAP(160, 120, VIDEO_PIX_FMT_GREY),
103+
HM01B0_VIDEO_FORMAT_CAP(320, 240, VIDEO_PIX_FMT_GREY),
104+
HM01B0_VIDEO_FORMAT_CAP(320, 320, VIDEO_PIX_FMT_GREY),
105+
{0},
106+
};
107+
108+
static int hm01b0_apply_configuration(const struct device *dev, enum hm01b0_resolution resolution)
109+
{
110+
struct hm01b0_data *data = dev->data;
111+
const struct hm01b0_config *config = dev->config;
112+
int ret;
113+
114+
/* Number of registers is the same for all configuration */
115+
ret = video_write_cci_multiregs(&config->i2c, hm01b0_init_regs[resolution],
116+
ARRAY_SIZE(hm01b0_160x120_regs));
117+
if (ret != 0) {
118+
LOG_ERR("Failed to write config list registers (%d)", ret);
119+
return ret;
120+
}
121+
122+
/* REG_BIT_CONTROL */
123+
ret = video_write_cci_reg(&config->i2c, HM01B0_CCI_BIT_CONTROL, data->ctrl_val);
124+
if (ret != 0) {
125+
LOG_ERR("Failed to write BIT_CONTROL reg (%d)", ret);
126+
return ret;
127+
}
128+
/* OSC_CLK_DIV */
129+
ret = video_write_cci_reg(&config->i2c, HM01B0_CCI_OSC_CLOCK_DIV, 0x08);
130+
if (ret != 0) {
131+
LOG_ERR("Failed to write OSC_CLK_DIV reg (%d)", ret);
132+
return ret;
133+
}
134+
/* INTEGRATION_H */
135+
ret = video_write_cci_reg(&config->i2c, HM01B0_CCI_INTEGRATION_H,
136+
hm01b0_init_regs[resolution][5].data / 2);
137+
if (ret != 0) {
138+
LOG_ERR("Failed to write INTEGRATION_H reg (%d)", ret);
139+
return ret;
140+
}
141+
/* GRP_PARAM_HOLD */
142+
ret = video_write_cci_reg(&config->i2c, HM01B0_CCI_GRP_PARAM_HOLD, 0x01);
143+
if (ret != 0) {
144+
LOG_ERR("Failed to write GRP_PARAM_HOLD reg (%d)", ret);
145+
return ret;
146+
}
147+
return ret;
148+
}
149+
150+
static int hm01b0_get_caps(const struct device *dev, struct video_caps *caps)
151+
{
152+
caps->min_vbuf_count = 0;
153+
caps->min_line_count = LINE_COUNT_HEIGHT;
154+
caps->max_line_count = LINE_COUNT_HEIGHT;
155+
caps->format_caps = hm01b0_fmts;
156+
return 0;
157+
}
158+
159+
static int hm01b0_set_fmt(const struct device *dev, struct video_format *fmt)
160+
{
161+
struct hm01b0_data *data = dev->data;
162+
size_t idx;
163+
int ret;
164+
165+
LOG_DBG("HM01B0 set_fmt: %d x %d, fmt: %s", fmt->width, fmt->height,
166+
VIDEO_FOURCC_TO_STR(fmt->pixelformat));
167+
168+
ret = video_format_caps_index(hm01b0_fmts, fmt, &idx);
169+
if (ret != 0) {
170+
LOG_ERR("Image resolution not supported\n");
171+
return ret;
172+
}
173+
174+
if (!memcmp(&data->fmt, fmt, sizeof(data->fmt))) {
175+
return 0;
176+
}
177+
178+
/* Check if camera is capable of handling given format */
179+
ret = hm01b0_apply_configuration(dev, (enum hm01b0_resolution)idx);
180+
if (ret != 0) {
181+
/* Camera is not capable of handling given format */
182+
LOG_ERR("Image resolution not supported");
183+
return ret;
184+
}
185+
data->fmt = *fmt;
186+
return ret;
187+
}
188+
189+
static int hm01b0_get_fmt(const struct device *dev, struct video_format *fmt)
190+
{
191+
struct hm01b0_data *data = dev->data;
192+
193+
*fmt = data->fmt;
194+
LOG_DBG("HM01B0 get_fmt: %d x %d, fmt: %s", fmt->width, fmt->height,
195+
VIDEO_FOURCC_TO_STR(fmt->pixelformat));
196+
return 0;
197+
}
198+
199+
static int hm01b0_set_stream(const struct device *dev, bool enable, enum video_buf_type type)
200+
{
201+
const struct hm01b0_config *config = dev->config;
202+
203+
/* SET MODE_SELECT */
204+
return video_write_cci_reg(&config->i2c, HM01B0_CCI_STS, enable ? 1 : 0);
205+
}
206+
207+
static int hm01b0_soft_reset(const struct device *dev)
208+
{
209+
const struct hm01b0_config *config = dev->config;
210+
uint32_t val = 0xff;
211+
int ret;
212+
213+
ret = video_write_cci_reg(&config->i2c, HM01B0_CCI_RESET, 0x01);
214+
if (ret != 0) {
215+
LOG_ERR("Error writing HM01B0_CCI_RESET (%d)", ret);
216+
return ret;
217+
}
218+
219+
for (int retries = 0; retries < 10; retries++) {
220+
ret = video_read_cci_reg(&config->i2c, HM01B0_CCI_STS, &val);
221+
if (ret != 0 || val == 0x0) {
222+
break;
223+
}
224+
k_msleep(100);
225+
}
226+
if (ret != 0) {
227+
LOG_ERR("Soft reset error (%d)", ret);
228+
}
229+
return ret;
230+
}
231+
232+
static DEVICE_API(video, hm01b0_driver_api) = {
233+
.set_format = hm01b0_set_fmt,
234+
.get_format = hm01b0_get_fmt,
235+
.set_stream = hm01b0_set_stream,
236+
.get_caps = hm01b0_get_caps,
237+
};
238+
239+
static bool hm01b0_check_connection(const struct device *dev)
240+
{
241+
const struct hm01b0_config *config = dev->config;
242+
uint32_t model_id;
243+
int ret;
244+
245+
ret = video_read_cci_reg(&config->i2c, HM01B0_CCI_ID, &model_id);
246+
if (ret != 0) {
247+
LOG_ERR("Error reading id reg (%d)", ret);
248+
return false;
249+
}
250+
return (model_id == HM01B0_ID);
251+
}
252+
253+
static int hm01b0_init(const struct device *dev)
254+
{
255+
struct hm01b0_data *data = dev->data;
256+
const struct hm01b0_config *config = dev->config;
257+
int ret;
258+
259+
if (config->data_bits == 8) {
260+
data->ctrl_val = 0x02;
261+
} else if (config->data_bits == 4) {
262+
data->ctrl_val = 0x42;
263+
} else if (config->data_bits == 1) {
264+
data->ctrl_val = 0x22;
265+
} else {
266+
LOG_ERR("Invalid data bits!");
267+
return -ENODEV;
268+
}
269+
270+
if (!hm01b0_check_connection(dev)) {
271+
LOG_ERR("%s is not ready", dev->name);
272+
return -ENODEV;
273+
}
274+
275+
ret = hm01b0_soft_reset(dev);
276+
if (ret != 0) {
277+
LOG_ERR("error sof reset (%d)", ret);
278+
return ret;
279+
}
280+
281+
struct video_format fmt = {
282+
.pixelformat = VIDEO_PIX_FMT_GREY,
283+
.width = 160,
284+
.height = 120,
285+
.type = VIDEO_BUF_TYPE_OUTPUT,
286+
.pitch = 160 * video_bits_per_pixel(VIDEO_PIX_FMT_GREY) / BITS_PER_BYTE,
287+
};
288+
289+
ret = hm01b0_set_fmt(dev, &fmt);
290+
if (ret != 0) {
291+
LOG_ERR("Error setting video format (%d)", ret);
292+
return ret;
293+
}
294+
return 0;
295+
}
296+
297+
#define HM01B0_INIT(inst) \
298+
const struct hm01b0_config hm01b0_config_##inst = { \
299+
.i2c = I2C_DT_SPEC_INST_GET(inst), \
300+
.data_bits = DT_INST_PROP(inst, data_bits), \
301+
}; \
302+
struct hm01b0_data hm01b0_data_##inst; \
303+
DEVICE_DT_INST_DEFINE(inst, &hm01b0_init, NULL, &hm01b0_data_##inst, \
304+
&hm01b0_config_##inst, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \
305+
&hm01b0_driver_api); \
306+
VIDEO_DEVICE_DEFINE(hm01b0_##inst, DEVICE_DT_INST_GET(inst), NULL);
307+
308+
DT_INST_FOREACH_STATUS_OKAY(HM01B0_INIT)

dts/bindings/video/himax,hm01b0.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#
2+
# Copyright The Zephyr Project Contributors
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
description: |
8+
Himax HM01B0 video sensor.
9+
10+
Example of node configuration:
11+
12+
&i2c0 {
13+
status = "okay";
14+
hm01b0: hm01b0@24 {
15+
compatible = "himax,hm01b0";
16+
reg = <0x24>;
17+
status = "okay";
18+
data-bits = <0x4>;
19+
port {
20+
hm01b0_ep_out: endpoint {
21+
remote-endpoint-label = "video_pio_dma_ep_in";
22+
};
23+
};
24+
};
25+
};
26+
27+
compatible: "himax,hm01b0"
28+
properties:
29+
data-bits:
30+
type: int
31+
default: 0x1
32+
description: Camera input data width. 1/4/8 bits
33+
enum: [1, 4, 8]
34+
35+
include: i2c-device.yaml
36+
37+
child-binding:
38+
child-binding:
39+
include: video-interfaces.yaml

0 commit comments

Comments
 (0)