1 /*
2 * Copyright (c) 2024 tinyVision.ai Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT zephyr_video_emul_imager
8
9 #include <string.h>
10
11 #include <zephyr/kernel.h>
12 #include <zephyr/device.h>
13 #include <zephyr/sys/util.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/drivers/video.h>
16 #include <zephyr/drivers/video-controls.h>
17 #include <zephyr/drivers/i2c.h>
18 #include <zephyr/logging/log.h>
19
20 LOG_MODULE_REGISTER(video_emul_imager, CONFIG_VIDEO_LOG_LEVEL);
21
22 #define EMUL_IMAGER_REG_SENSOR_ID 0x0000
23 #define EMUL_IMAGER_SENSOR_ID 0x99
24 #define EMUL_IMAGER_REG_CTRL 0x0001
25 #define EMUL_IMAGER_REG_INIT1 0x0002
26 #define EMUL_IMAGER_REG_INIT2 0x0003
27 #define EMUL_IMAGER_REG_TIMING1 0x0004
28 #define EMUL_IMAGER_REG_TIMING2 0x0005
29 #define EMUL_IMAGER_REG_TIMING3 0x0006
30 #define EMUL_IMAGER_REG_EXPOSURE 0x0007
31 #define EMUL_IMAGER_REG_GAIN 0x0008
32 #define EMUL_IMAGER_REG_PATTERN 0x0009
33 #define EMUL_IMAGER_PATTERN_OFF 0x00
34 #define EMUL_IMAGER_PATTERN_BARS1 0x01
35 #define EMUL_IMAGER_PATTERN_BARS2 0x02
36
37 /* Emulated register bank */
38 uint8_t emul_imager_fake_regs[10];
39
40 enum emul_imager_fmt_id {
41 RGB565_64x20,
42 YUYV_64x20,
43 };
44
45 struct emul_imager_reg {
46 uint16_t addr;
47 uint8_t value;
48 };
49
50 struct emul_imager_mode {
51 uint8_t fps;
52 /* List of registers lists to configure the various properties of the sensor.
53 * This permits to deduplicate the list of registers in case some lare sections
54 * are repeated across modes, such as the resolution for different FPS.
55 */
56 const struct emul_imager_reg *regs[2];
57 /* More fields can be added according to the needs of the sensor driver */
58 };
59
60 struct emul_imager_config {
61 struct i2c_dt_spec i2c;
62 };
63
64 struct emul_imager_data {
65 /* First field is a framebuffer for I/O emulation purpose */
66 uint8_t framebuffer[CONFIG_VIDEO_EMUL_IMAGER_FRAMEBUFFER_SIZE];
67 /* Other fields are shared with real hardware drivers */
68 const struct emul_imager_mode *mode;
69 enum emul_imager_fmt_id fmt_id;
70 struct video_format fmt;
71 };
72
73 /* Initial parameters of the sensors common to all modes. */
74 static const struct emul_imager_reg emul_imager_init_regs[] = {
75 {EMUL_IMAGER_REG_CTRL, 0x00},
76 /* Example comment about REG_INIT1 */
77 {EMUL_IMAGER_REG_INIT1, 0x10},
78 {EMUL_IMAGER_REG_INIT2, 0x00},
79 {0},
80 };
81
82 /* List of registers aggregated together in "modes" that can be applied
83 * to set the timing parameters and other mode-dependent configuration.
84 */
85
86 static const struct emul_imager_reg emul_imager_rgb565_64x20[] = {
87 {EMUL_IMAGER_REG_TIMING1, 0x64},
88 {EMUL_IMAGER_REG_TIMING2, 0x20},
89 {0},
90 };
91 static const struct emul_imager_reg emul_imager_rgb565_64x20_15fps[] = {
92 {EMUL_IMAGER_REG_TIMING3, 15},
93 {0},
94 };
95 static const struct emul_imager_reg emul_imager_rgb565_64x20_30fps[] = {
96 {EMUL_IMAGER_REG_TIMING3, 30},
97 {0},
98 };
99 static const struct emul_imager_reg emul_imager_rgb565_64x20_60fps[] = {
100 {EMUL_IMAGER_REG_TIMING3, 60},
101 {0},
102 };
103 struct emul_imager_mode emul_imager_rgb565_64x20_modes[] = {
104 {.fps = 15, .regs = {emul_imager_rgb565_64x20, emul_imager_rgb565_64x20_15fps}},
105 {.fps = 30, .regs = {emul_imager_rgb565_64x20, emul_imager_rgb565_64x20_30fps}},
106 {.fps = 60, .regs = {emul_imager_rgb565_64x20, emul_imager_rgb565_64x20_60fps}},
107 {0},
108 };
109
110 static const struct emul_imager_reg emul_imager_yuyv_64x20[] = {
111 {EMUL_IMAGER_REG_TIMING1, 0x64},
112 {EMUL_IMAGER_REG_TIMING2, 0x20},
113 {0},
114 };
115 static const struct emul_imager_reg emul_imager_yuyv_64x20_15fps[] = {
116 {EMUL_IMAGER_REG_TIMING3, 15},
117 {0},
118 };
119 static const struct emul_imager_reg emul_imager_yuyv_64x20_30fps[] = {
120 {EMUL_IMAGER_REG_TIMING3, 30},
121 {0},
122 };
123 struct emul_imager_mode emul_imager_yuyv_64x20_modes[] = {
124 {.fps = 15, .regs = {emul_imager_yuyv_64x20, emul_imager_yuyv_64x20_15fps}},
125 {.fps = 30, .regs = {emul_imager_yuyv_64x20, emul_imager_yuyv_64x20_30fps}},
126 {0},
127 };
128
129 /* Summary of all the modes of all the frame formats, with the format ID as
130 * index, matching fmts[].
131 */
132 static const struct emul_imager_mode *emul_imager_modes[] = {
133 [RGB565_64x20] = emul_imager_rgb565_64x20_modes,
134 [YUYV_64x20] = emul_imager_yuyv_64x20_modes,
135 };
136
137 /* Video device capabilities where the supported resolutions and pixel formats are listed.
138 * The format ID is used as index to fetch the matching mode from the list above.
139 */
140 #define EMUL_IMAGER_VIDEO_FORMAT_CAP(width, height, format) \
141 { \
142 .pixelformat = (format), \
143 .width_min = (width), \
144 .width_max = (width), \
145 .height_min = (height), \
146 .height_max = (height), \
147 .width_step = 0, \
148 .height_step = 0, \
149 }
150 static const struct video_format_cap fmts[] = {
151 [RGB565_64x20] = EMUL_IMAGER_VIDEO_FORMAT_CAP(64, 20, VIDEO_PIX_FMT_RGB565),
152 [YUYV_64x20] = EMUL_IMAGER_VIDEO_FORMAT_CAP(64, 20, VIDEO_PIX_FMT_YUYV),
153 {0},
154 };
155
156 /* Emulated I2C register interface, to replace with actual I2C calls for real hardware */
emul_imager_read_reg(const struct device * const dev,uint8_t reg_addr,uint8_t * value)157 static int emul_imager_read_reg(const struct device *const dev, uint8_t reg_addr, uint8_t *value)
158 {
159 LOG_DBG("%s placeholder for I2C read from 0x%02x", dev->name, reg_addr);
160 switch (reg_addr) {
161 case EMUL_IMAGER_REG_SENSOR_ID:
162 *value = EMUL_IMAGER_SENSOR_ID;
163 break;
164 default:
165 *value = emul_imager_fake_regs[reg_addr];
166 }
167 return 0;
168 }
169
170 /* Helper to read a full integer directly from a register */
emul_imager_read_int(const struct device * const dev,uint8_t reg_addr,int * value)171 static int emul_imager_read_int(const struct device *const dev, uint8_t reg_addr, int *value)
172 {
173 uint8_t val8;
174 int ret;
175
176 ret = emul_imager_read_reg(dev, reg_addr, &val8);
177 *value = val8;
178 return ret;
179 }
180
181 /* Some sensors will need reg8 or reg16 variants. */
emul_imager_write_reg(const struct device * const dev,uint8_t reg_addr,uint8_t value)182 static int emul_imager_write_reg(const struct device *const dev, uint8_t reg_addr, uint8_t value)
183 {
184 LOG_DBG("%s placeholder for I2C write 0x%08x to 0x%02x", dev->name, value, reg_addr);
185 emul_imager_fake_regs[reg_addr] = value;
186 return 0;
187 }
188
emul_imager_write_multi(const struct device * const dev,const struct emul_imager_reg * regs)189 static int emul_imager_write_multi(const struct device *const dev,
190 const struct emul_imager_reg *regs)
191 {
192 int ret;
193
194 for (int i = 0; regs[i].addr != 0; i++) {
195 ret = emul_imager_write_reg(dev, regs[i].addr, regs[i].value);
196 if (ret < 0) {
197 return ret;
198 }
199 }
200 return 0;
201 }
202
emul_imager_set_ctrl(const struct device * dev,unsigned int cid,void * value)203 static int emul_imager_set_ctrl(const struct device *dev, unsigned int cid, void *value)
204 {
205 switch (cid) {
206 case VIDEO_CID_EXPOSURE:
207 return emul_imager_write_reg(dev, EMUL_IMAGER_REG_EXPOSURE, (int)value);
208 case VIDEO_CID_GAIN:
209 return emul_imager_write_reg(dev, EMUL_IMAGER_REG_GAIN, (int)value);
210 case VIDEO_CID_TEST_PATTERN:
211 return emul_imager_write_reg(dev, EMUL_IMAGER_REG_PATTERN, (int)value);
212 default:
213 return -ENOTSUP;
214 }
215 }
216
emul_imager_get_ctrl(const struct device * dev,unsigned int cid,void * value)217 static int emul_imager_get_ctrl(const struct device *dev, unsigned int cid, void *value)
218 {
219 struct emul_imager_data *data = dev->data;
220
221 switch (cid) {
222 case VIDEO_CID_EXPOSURE:
223 return emul_imager_read_int(dev, EMUL_IMAGER_REG_EXPOSURE, value);
224 case VIDEO_CID_GAIN:
225 return emul_imager_read_int(dev, EMUL_IMAGER_REG_GAIN, value);
226 case VIDEO_CID_TEST_PATTERN:
227 return emul_imager_read_int(dev, EMUL_IMAGER_REG_PATTERN, value);
228 case VIDEO_CID_PIXEL_RATE:
229 *(int64_t *)value = (int64_t)data->fmt.width * data->fmt.pitch * data->mode->fps;
230 return 0;
231 default:
232 return -ENOTSUP;
233 }
234 }
235
236 /* Customize this function according to your "struct emul_imager_mode". */
emul_imager_set_mode(const struct device * dev,const struct emul_imager_mode * mode)237 static int emul_imager_set_mode(const struct device *dev, const struct emul_imager_mode *mode)
238 {
239 struct emul_imager_data *data = dev->data;
240 int ret;
241
242 if (data->mode == mode) {
243 return 0;
244 }
245
246 LOG_DBG("Applying mode %p at %d FPS", mode, mode->fps);
247
248 /* Apply all the configuration registers for that mode */
249 for (int i = 0; i < 2; i++) {
250 ret = emul_imager_write_multi(dev, mode->regs[i]);
251 if (ret < 0) {
252 goto err;
253 }
254 }
255
256 data->mode = mode;
257 return 0;
258 err:
259 LOG_ERR("Could not apply %s mode %p (%u FPS)", dev->name, mode, mode->fps);
260 return ret;
261 }
262
emul_imager_set_frmival(const struct device * dev,enum video_endpoint_id ep,struct video_frmival * frmival)263 static int emul_imager_set_frmival(const struct device *dev, enum video_endpoint_id ep,
264 struct video_frmival *frmival)
265 {
266 struct emul_imager_data *data = dev->data;
267 struct video_frmival_enum fie = {.format = &data->fmt, .discrete = *frmival};
268
269 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
270 return -EINVAL;
271 }
272
273 video_closest_frmival(dev, ep, &fie);
274 LOG_DBG("Applying frame interval number %u", fie.index);
275 return emul_imager_set_mode(dev, &emul_imager_modes[data->fmt_id][fie.index]);
276 }
277
emul_imager_get_frmival(const struct device * dev,enum video_endpoint_id ep,struct video_frmival * frmival)278 static int emul_imager_get_frmival(const struct device *dev, enum video_endpoint_id ep,
279 struct video_frmival *frmival)
280 {
281 struct emul_imager_data *data = dev->data;
282
283 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
284 return -EINVAL;
285 }
286
287 frmival->numerator = 1;
288 frmival->denominator = data->mode->fps;
289 return 0;
290 }
291
emul_imager_enum_frmival(const struct device * dev,enum video_endpoint_id ep,struct video_frmival_enum * fie)292 static int emul_imager_enum_frmival(const struct device *dev, enum video_endpoint_id ep,
293 struct video_frmival_enum *fie)
294 {
295 const struct emul_imager_mode *mode;
296 size_t fmt_id;
297 int ret;
298
299 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
300 return -EINVAL;
301 }
302
303 ret = video_format_caps_index(fmts, fie->format, &fmt_id);
304 if (ret < 0) {
305 return ret;
306 }
307
308 mode = &emul_imager_modes[fmt_id][fie->index];
309
310 fie->type = VIDEO_FRMIVAL_TYPE_DISCRETE;
311 fie->discrete.numerator = 1;
312 fie->discrete.denominator = mode->fps;
313 fie->index++;
314
315 return mode->fps == 0;
316 }
317
318 /* White, Yellow, Cyan, Green, Magenta, Red, Blue, Black */
319 static const uint16_t pattern_8bars_yuv[8][3] = {
320 {0xFF, 0x7F, 0x7F}, {0xFF, 0x00, 0xFF}, {0xFF, 0xFF, 0x00}, {0x7F, 0x00, 0x00},
321 {0x00, 0xFF, 0xFF}, {0x00, 0x00, 0xFF}, {0x00, 0xFF, 0x00}, {0x00, 0x7F, 0x7F}};
322 static const uint16_t pattern_8bars_rgb[8][3] = {
323 {0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0x00}, {0x00, 0xFF, 0xFF}, {0x00, 0xFF, 0x00},
324 {0xFF, 0x00, 0xFF}, {0xFF, 0x00, 0x00}, {0x00, 0x00, 0xFF}, {0x00, 0x00, 0x00}};
emul_imager_fill_framebuffer(const struct device * const dev,struct video_format * fmt)325 static void emul_imager_fill_framebuffer(const struct device *const dev, struct video_format *fmt)
326 {
327 struct emul_imager_data *data = dev->data;
328 uint16_t *fb16 = (uint16_t *)data->framebuffer;
329 uint16_t r, g, b, y, uv;
330
331 /* Fill the first row of the emulated framebuffer */
332 switch (fmt->pixelformat) {
333 case VIDEO_PIX_FMT_YUYV:
334 for (size_t i = 0; i < fmt->width; i++) {
335 y = pattern_8bars_yuv[i * 8 / fmt->width][0];
336 uv = pattern_8bars_yuv[i * 8 / fmt->width][1 + i % 2];
337 fb16[i] = sys_cpu_to_be16(y << 8 | uv << 0);
338 }
339 break;
340 case VIDEO_PIX_FMT_RGB565:
341 for (size_t i = 0; i < fmt->width; i++) {
342 r = pattern_8bars_rgb[i * 8 / fmt->width][0] >> (8 - 5);
343 g = pattern_8bars_rgb[i * 8 / fmt->width][1] >> (8 - 6);
344 b = pattern_8bars_rgb[i * 8 / fmt->width][2] >> (8 - 5);
345 fb16[i] = sys_cpu_to_le16((r << 11) | (g << 6) | (b << 0));
346 }
347 break;
348 default:
349 LOG_WRN("Unsupported pixel format %x, supported: %x, %x", fmt->pixelformat,
350 VIDEO_PIX_FMT_YUYV, VIDEO_PIX_FMT_RGB565);
351 memset(fb16, 0, fmt->pitch);
352 }
353
354 /* Duplicate the first row over the whole frame */
355 for (size_t i = 1; i < fmt->height; i++) {
356 memcpy(data->framebuffer + fmt->pitch * i, data->framebuffer, fmt->pitch);
357 }
358 }
359
emul_imager_set_fmt(const struct device * const dev,enum video_endpoint_id ep,struct video_format * fmt)360 static int emul_imager_set_fmt(const struct device *const dev, enum video_endpoint_id ep,
361 struct video_format *fmt)
362 {
363 struct emul_imager_data *data = dev->data;
364 size_t fmt_id;
365 int ret;
366
367 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
368 return -EINVAL;
369 }
370
371 if (fmt->pitch * fmt->height > CONFIG_VIDEO_EMUL_IMAGER_FRAMEBUFFER_SIZE) {
372 LOG_ERR("%s has %u bytes of memory, unable to support %x %ux%u (%u bytes)",
373 dev->name, CONFIG_VIDEO_EMUL_IMAGER_FRAMEBUFFER_SIZE, fmt->pixelformat,
374 fmt->width, fmt->height, fmt->pitch * fmt->height);
375 return -ENOMEM;
376 }
377
378 if (memcmp(&data->fmt, fmt, sizeof(data->fmt)) == 0) {
379 return 0;
380 }
381
382 ret = video_format_caps_index(fmts, fmt, &fmt_id);
383 if (ret < 0) {
384 LOG_ERR("Format %x %ux%u not found for %s", fmt->pixelformat, fmt->width,
385 fmt->height, dev->name);
386 return ret;
387 }
388
389 ret = emul_imager_set_mode(dev, &emul_imager_modes[fmt_id][0]);
390 if (ret < 0) {
391 return ret;
392 }
393
394 /* Change the image pattern on the framebuffer */
395 emul_imager_fill_framebuffer(dev, fmt);
396
397 data->fmt_id = fmt_id;
398 data->fmt = *fmt;
399 return 0;
400 }
401
emul_imager_get_fmt(const struct device * dev,enum video_endpoint_id ep,struct video_format * fmt)402 static int emul_imager_get_fmt(const struct device *dev, enum video_endpoint_id ep,
403 struct video_format *fmt)
404 {
405 struct emul_imager_data *data = dev->data;
406
407 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
408 return -EINVAL;
409 }
410
411 *fmt = data->fmt;
412 return 0;
413 }
414
emul_imager_get_caps(const struct device * dev,enum video_endpoint_id ep,struct video_caps * caps)415 static int emul_imager_get_caps(const struct device *dev, enum video_endpoint_id ep,
416 struct video_caps *caps)
417 {
418 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
419 return -EINVAL;
420 }
421
422 caps->format_caps = fmts;
423 return 0;
424 }
425
emul_imager_stream_start(const struct device * dev)426 static int emul_imager_stream_start(const struct device *dev)
427 {
428 return emul_imager_write_reg(dev, EMUL_IMAGER_REG_CTRL, 1);
429 }
430
emul_imager_stream_stop(const struct device * dev)431 static int emul_imager_stream_stop(const struct device *dev)
432 {
433 return emul_imager_write_reg(dev, EMUL_IMAGER_REG_CTRL, 0);
434 }
435
436 static DEVICE_API(video, emul_imager_driver_api) = {
437 .set_ctrl = emul_imager_set_ctrl,
438 .get_ctrl = emul_imager_get_ctrl,
439 .set_frmival = emul_imager_set_frmival,
440 .get_frmival = emul_imager_get_frmival,
441 .enum_frmival = emul_imager_enum_frmival,
442 .set_format = emul_imager_set_fmt,
443 .get_format = emul_imager_get_fmt,
444 .get_caps = emul_imager_get_caps,
445 .stream_start = emul_imager_stream_start,
446 .stream_stop = emul_imager_stream_stop,
447 };
448
emul_imager_init(const struct device * dev)449 int emul_imager_init(const struct device *dev)
450 {
451 struct video_format fmt;
452 uint8_t sensor_id;
453 int ret;
454
455 if (/* !i2c_is_ready_dt(&cfg->i2c) */ false) {
456 /* LOG_ERR("Bus %s is not ready", cfg->i2c.bus->name); */
457 return -ENODEV;
458 }
459
460 ret = emul_imager_read_reg(dev, EMUL_IMAGER_REG_SENSOR_ID, &sensor_id);
461 if (ret < 0 || sensor_id != EMUL_IMAGER_SENSOR_ID) {
462 LOG_ERR("Failed to get %s correct sensor ID (0x%x", dev->name, sensor_id);
463 return ret;
464 }
465
466 ret = emul_imager_write_multi(dev, emul_imager_init_regs);
467 if (ret < 0) {
468 LOG_ERR("Could not set %s initial registers", dev->name);
469 return ret;
470 }
471
472 fmt.pixelformat = fmts[0].pixelformat;
473 fmt.width = fmts[0].width_min;
474 fmt.height = fmts[0].height_min;
475 fmt.pitch = fmt.width * 2;
476
477 ret = emul_imager_set_fmt(dev, VIDEO_EP_OUT, &fmt);
478 if (ret < 0) {
479 LOG_ERR("Failed to set %s to default format %x %ux%u", dev->name, fmt.pixelformat,
480 fmt.width, fmt.height);
481 }
482
483 return 0;
484 }
485
486 #define EMUL_IMAGER_DEFINE(inst) \
487 static struct emul_imager_data emul_imager_data_##inst; \
488 \
489 static const struct emul_imager_config emul_imager_cfg_##inst = { \
490 .i2c = /* I2C_DT_SPEC_INST_GET(inst) */ {0}, \
491 }; \
492 \
493 DEVICE_DT_INST_DEFINE(inst, &emul_imager_init, NULL, &emul_imager_data_##inst, \
494 &emul_imager_cfg_##inst, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \
495 &emul_imager_driver_api);
496
497 DT_INST_FOREACH_STATUS_OKAY(EMUL_IMAGER_DEFINE)
498