Skip to content

Commit 6d80f29

Browse files
josuahAlain Volmat
authored andcommitted
drivers: video: common: introduce CCI utilities
Add a library for the Camera Common Interface, part of the MIPI CSI protocol standard defining methods to configure a camera device over I2C, such as which size for the register address/data. Signed-off-by: Josuah Demangeon <me@josuah.net>
1 parent b2c6d96 commit 6d80f29

File tree

3 files changed

+421
-2
lines changed

3 files changed

+421
-2
lines changed

drivers/video/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ config VIDEO_BUFFER_SMH_ATTRIBUTE
5050
1: SMH_REG_ATTR_NON_CACHEABLE
5151
2: SMH_REG_ATTR_EXTERNAL
5252

53+
config VIDEO_I2C_RETRY_NUM
54+
int "Number of retries after a failed I2C communication"
55+
default 0
56+
help
57+
If set to 0, only a single write attempt will be done with no retry.
58+
The default is to not retry. Board configuration files or user project can then
59+
use the number of retries that matches their situation.
60+
5361
source "drivers/video/Kconfig.esp32_dvp"
5462

5563
source "drivers/video/Kconfig.mcux_csi"

drivers/video/video_common.c

Lines changed: 180 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
/*
22
* Copyright (c) 2019, Linaro Limited
3-
* Copyright (c) 2024, tinyVision.ai Inc.
3+
* Copyright (c) 2024-2025, tinyVision.ai Inc.
44
*
55
* SPDX-License-Identifier: Apache-2.0
66
*/
77

88
#include <string.h>
99

10-
#include <zephyr/kernel.h>
10+
#include <zephyr/device.h>
11+
#include <zephyr/drivers/i2c.h>
1112
#include <zephyr/drivers/video.h>
13+
#include <zephyr/kernel.h>
14+
#include <zephyr/logging/log.h>
15+
#include <zephyr/sys/byteorder.h>
16+
#include <zephyr/sys/util.h>
17+
18+
#include "video_common.h"
19+
20+
LOG_MODULE_REGISTER(video_common, CONFIG_VIDEO_LOG_LEVEL);
1221

1322
#if defined(CONFIG_VIDEO_BUFFER_USE_SHARED_MULTI_HEAP)
1423
#include <zephyr/multi_heap/shared_multi_heap.h>
@@ -164,3 +173,172 @@ void video_closest_frmival(const struct device *dev, enum video_endpoint_id ep,
164173
}
165174
}
166175
}
176+
177+
int video_read_cci_reg(const struct i2c_dt_spec *i2c, uint32_t reg_addr, uint32_t *data)
178+
{
179+
size_t addr_size = FIELD_GET(VIDEO_REG_ADDR_SIZE_MASK, reg_addr);
180+
size_t data_size = FIELD_GET(VIDEO_REG_DATA_SIZE_MASK, reg_addr);
181+
bool big_endian = FIELD_GET(VIDEO_REG_ENDIANNESS_MASK, reg_addr);
182+
uint16_t addr = FIELD_GET(VIDEO_REG_ADDR_MASK, reg_addr);
183+
uint8_t buf_w[sizeof(uint16_t)] = {0};
184+
uint8_t *data_ptr;
185+
int ret;
186+
187+
__ASSERT(addr_size > 0, "The address must have a address size flag");
188+
__ASSERT(data_size > 0, "The address must have a data size flag");
189+
190+
*data = 0;
191+
192+
if (big_endian) {
193+
/* Casting between data sizes in big-endian requires re-aligning */
194+
data_ptr = (uint8_t *)data + sizeof(*data) - data_size;
195+
} else {
196+
/* Casting between data sizes in little-endian is a no-op */
197+
data_ptr = (uint8_t *)data;
198+
}
199+
200+
for (int i = 0; i < data_size; i++) {
201+
if (addr_size == 1) {
202+
buf_w[0] = addr + i;
203+
} else {
204+
sys_put_be16(addr + i, &buf_w[0]);
205+
}
206+
207+
ret = i2c_write_read_dt(i2c, buf_w, addr_size, &data_ptr[i], 1);
208+
if (ret) {
209+
LOG_ERR("Failed to read from register 0x%x", addr + i);
210+
return ret;
211+
}
212+
213+
LOG_HEXDUMP_DBG(buf_w, addr_size, "Data written to the I2C device...");
214+
LOG_HEXDUMP_DBG(&data_ptr[i], 1, "... data read back from the I2C device");
215+
}
216+
217+
*data = big_endian ? sys_be32_to_cpu(*data) : sys_le32_to_cpu(*data);
218+
219+
return 0;
220+
}
221+
222+
static int video_write_reg_retry(const struct i2c_dt_spec *i2c, uint8_t *buf_w, size_t size)
223+
{
224+
int ret;
225+
226+
for (int i = 0;; i++) {
227+
ret = i2c_write_dt(i2c, buf_w, size);
228+
if (!ret) {
229+
break;
230+
}
231+
if (i == CONFIG_VIDEO_I2C_RETRY_NUM) {
232+
LOG_HEXDUMP_ERR(buf_w, size, "failed to write to I2C register");
233+
return ret;
234+
}
235+
236+
k_sleep(K_MSEC(1));
237+
}
238+
239+
return 0;
240+
}
241+
242+
int video_write_cci_reg(const struct i2c_dt_spec *i2c, uint32_t reg_addr, uint32_t data)
243+
{
244+
size_t addr_size = FIELD_GET(VIDEO_REG_ADDR_SIZE_MASK, reg_addr);
245+
size_t data_size = FIELD_GET(VIDEO_REG_DATA_SIZE_MASK, reg_addr);
246+
bool big_endian = FIELD_GET(VIDEO_REG_ENDIANNESS_MASK, reg_addr);
247+
uint16_t addr = FIELD_GET(VIDEO_REG_ADDR_MASK, reg_addr);
248+
uint8_t buf_w[sizeof(uint16_t) + sizeof(uint32_t)] = {0};
249+
uint8_t *data_ptr;
250+
int ret;
251+
252+
__ASSERT(addr_size > 0, "The address must have a address size flag");
253+
__ASSERT(data_size > 0, "The address must have a data size flag");
254+
255+
if (big_endian) {
256+
/* Casting between data sizes in big-endian requires re-aligning */
257+
data = sys_cpu_to_be32(data);
258+
data_ptr = (uint8_t *)&data + sizeof(data) - data_size;
259+
} else {
260+
/* Casting between data sizes in little-endian is a no-op */
261+
data = sys_cpu_to_le32(data);
262+
data_ptr = (uint8_t *)&data;
263+
}
264+
265+
for (int i = 0; i < data_size; i++) {
266+
/* The address is always big-endian as per CCI standard */
267+
if (addr_size == 1) {
268+
buf_w[0] = addr + i;
269+
} else {
270+
sys_put_be16(addr + i, &buf_w[0]);
271+
}
272+
273+
buf_w[addr_size] = data_ptr[i];
274+
275+
LOG_HEXDUMP_DBG(buf_w, addr_size + 1, "Data written to the I2C device");
276+
277+
ret = video_write_reg_retry(i2c, buf_w, addr_size + 1);
278+
if (ret) {
279+
LOG_ERR("Failed to write to register 0x%x", addr + i);
280+
return ret;
281+
}
282+
}
283+
284+
return 0;
285+
}
286+
287+
int video_modify_cci_reg(const struct i2c_dt_spec *i2c, uint32_t reg_addr, uint32_t field_mask,
288+
uint32_t field_value)
289+
{
290+
uint32_t reg;
291+
int ret;
292+
293+
ret = video_read_cci_reg(i2c, reg_addr, &reg);
294+
if (ret) {
295+
return ret;
296+
}
297+
298+
return video_write_cci_reg(i2c, reg_addr, (reg & ~field_mask) | field_value);
299+
}
300+
301+
int video_write_cci_multiregs(const struct i2c_dt_spec *i2c, const struct video_reg *regs,
302+
size_t num_regs)
303+
{
304+
int ret;
305+
306+
for (int i = 0; i < num_regs; i++) {
307+
ret = video_write_cci_reg(i2c, regs[i].addr, regs[i].data);
308+
if (ret) {
309+
return ret;
310+
}
311+
}
312+
313+
return 0;
314+
}
315+
316+
int video_write_cci_multiregs8(const struct i2c_dt_spec *i2c, const struct video_reg8 *regs,
317+
size_t num_regs)
318+
{
319+
int ret;
320+
321+
for (int i = 0; i < num_regs; i++) {
322+
ret = video_write_cci_reg(i2c, regs[i].addr | VIDEO_REG_ADDR8_DATA8, regs[i].data);
323+
if (ret) {
324+
return ret;
325+
}
326+
}
327+
328+
return 0;
329+
}
330+
331+
int video_write_cci_multiregs16(const struct i2c_dt_spec *i2c, const struct video_reg16 *regs,
332+
size_t num_regs)
333+
{
334+
int ret;
335+
336+
for (int i = 0; i < num_regs; i++) {
337+
ret = video_write_cci_reg(i2c, regs[i].addr | VIDEO_REG_ADDR16_DATA8, regs[i].data);
338+
if (ret) {
339+
return ret;
340+
}
341+
}
342+
343+
return 0;
344+
}

0 commit comments

Comments
 (0)