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