Skip to content

Commit 4000eb6

Browse files
committed
drivers: video: common: add shared implementation for imagers
Imagers, also known as image sensors, have drivers that look very similar. Add common implementation that fits most image sensors, which can be bypassed wherever this does not make the implementation simpler. Signed-off-by: Josuah Demangeon <me@josuah.net>
1 parent 9929491 commit 4000eb6

File tree

2 files changed

+316
-0
lines changed

2 files changed

+316
-0
lines changed

drivers/video/video_common.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,197 @@ int video_write_cci_multi(const struct i2c_dt_spec *i2c, const struct video_reg
343343

344344
return 0;
345345
}
346+
/* Common implementation for imagers (a.k.a. image sensor) drivers */
347+
348+
int video_imager_set_mode(const struct device *dev, const struct video_imager_mode *mode)
349+
{
350+
const struct video_imager_config *cfg = dev->config;
351+
struct video_imager_data *data = cfg->data;
352+
int ret;
353+
354+
if (data->mode == mode) {
355+
LOG_DBG("%s is arlready in the mode requested", dev->name);
356+
return 0;
357+
}
358+
359+
/* Write each register table to the device */
360+
for (int i = 0; i < ARRAY_SIZE(mode->regs) && mode->regs[i] != NULL; i++) {
361+
ret = cfg->write_multi(&cfg->i2c, mode->regs[i]);
362+
if (ret != 0) {
363+
LOG_ERR("Could not set %s to mode %p, %u FPS", dev->name, mode, mode->fps);
364+
return ret;
365+
}
366+
}
367+
368+
data->mode = mode;
369+
370+
return 0;
371+
}
372+
373+
int video_imager_set_frmival(const struct device *dev, enum video_endpoint_id ep,
374+
struct video_frmival *frmival)
375+
{
376+
const struct video_imager_config *cfg = dev->config;
377+
struct video_imager_data *data = cfg->data;
378+
struct video_frmival_enum fie = {.format = &data->fmt, .discrete = *frmival};
379+
380+
if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
381+
return -EINVAL;
382+
}
383+
384+
video_closest_frmival(dev, ep, &fie);
385+
386+
return video_imager_set_mode(dev, &cfg->modes[data->fmt_id][fie.index]);
387+
}
388+
389+
int video_imager_get_frmival(const struct device *dev, enum video_endpoint_id ep,
390+
struct video_frmival *frmival)
391+
{
392+
const struct video_imager_config *cfg = dev->config;
393+
struct video_imager_data *data = cfg->data;
394+
395+
if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
396+
return -EINVAL;
397+
}
398+
399+
frmival->numerator = 1;
400+
frmival->denominator = data->mode->fps;
401+
402+
return 0;
403+
}
404+
405+
int video_imager_enum_frmival(const struct device *dev, enum video_endpoint_id ep,
406+
struct video_frmival_enum *fie)
407+
{
408+
const struct video_imager_config *cfg = dev->config;
409+
const struct video_imager_mode *modes;
410+
size_t fmt_id = 0;
411+
int ret;
412+
413+
if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
414+
return -EINVAL;
415+
}
416+
417+
ret = video_format_caps_index(cfg->fmts, fie->format, &fmt_id);
418+
if (ret != 0) {
419+
LOG_ERR("Format '%s' %ux%u not found for %s",
420+
VIDEO_FOURCC_TO_STR(fie->format->pixelformat),
421+
fie->format->width, fie->format->height, dev->name);
422+
return ret;
423+
}
424+
425+
modes = cfg->modes[fmt_id];
426+
427+
for (int i = 0;; i++) {
428+
if (modes[i].fps == 0) {
429+
return -EINVAL;
430+
}
431+
432+
if (i == fie->index) {
433+
fie->type = VIDEO_FRMIVAL_TYPE_DISCRETE;
434+
fie->discrete.numerator = 1;
435+
fie->discrete.denominator = modes[i].fps;
436+
break;
437+
}
438+
}
439+
440+
return 0;
441+
}
442+
443+
int video_imager_set_fmt(const struct device *const dev, enum video_endpoint_id ep,
444+
struct video_format *fmt)
445+
{
446+
const struct video_imager_config *cfg = dev->config;
447+
struct video_imager_data *data = cfg->data;
448+
size_t fmt_id;
449+
int ret;
450+
451+
if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
452+
LOG_ERR("Only the output endpoint is supported for %s", dev->name);
453+
return -EINVAL;
454+
}
455+
456+
ret = video_format_caps_index(cfg->fmts, fmt, &fmt_id);
457+
if (ret != 0) {
458+
LOG_ERR("Format '%s' %ux%u not found for device %s",
459+
VIDEO_FOURCC_TO_STR(fmt->pixelformat), fmt->width, fmt->height, dev->name);
460+
return ret;
461+
}
462+
463+
ret = video_imager_set_mode(dev, &cfg->modes[fmt_id][0]);
464+
if (ret != 0) {
465+
return ret;
466+
}
467+
468+
data->fmt_id = fmt_id;
469+
data->fmt = *fmt;
470+
471+
return 0;
472+
}
473+
474+
int video_imager_get_fmt(const struct device *dev, enum video_endpoint_id ep,
475+
struct video_format *fmt)
476+
{
477+
const struct video_imager_config *cfg = dev->config;
478+
struct video_imager_data *data = cfg->data;
479+
480+
if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
481+
return -EINVAL;
482+
}
483+
484+
*fmt = data->fmt;
485+
486+
return 0;
487+
}
488+
489+
int video_imager_get_caps(const struct device *dev, enum video_endpoint_id ep,
490+
struct video_caps *caps)
491+
{
492+
const struct video_imager_config *cfg = dev->config;
493+
494+
if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
495+
return -EINVAL;
496+
}
497+
498+
caps->format_caps = cfg->fmts;
499+
500+
return 0;
501+
}
502+
503+
int video_imager_init(const struct device *dev, const struct video_reg *init_regs,
504+
int default_fmt_idx)
505+
{
506+
const struct video_imager_config *cfg = dev->config;
507+
struct video_format fmt;
508+
int ret;
509+
510+
__ASSERT_NO_MSG(cfg->modes != NULL);
511+
__ASSERT_NO_MSG(cfg->fmts != NULL);
512+
513+
if (!device_is_ready(cfg->i2c.bus)) {
514+
LOG_ERR("I2C bus device %s is not ready", cfg->i2c.bus->name);
515+
return -ENODEV;
516+
}
517+
518+
if (init_regs != NULL) {
519+
ret = cfg->write_multi(&cfg->i2c, init_regs);
520+
if (ret != 0) {
521+
LOG_ERR("Could not set %s initial registers", dev->name);
522+
return ret;
523+
}
524+
}
525+
526+
fmt.pixelformat = cfg->fmts[default_fmt_idx].pixelformat;
527+
fmt.width = cfg->fmts[default_fmt_idx].width_max;
528+
fmt.height = cfg->fmts[default_fmt_idx].height_max;
529+
fmt.pitch = fmt.width * video_bits_per_pixel(fmt.pixelformat) / BITS_PER_BYTE;
530+
531+
ret = video_set_format(dev, VIDEO_EP_OUT, &fmt);
532+
if (ret != 0) {
533+
LOG_ERR("Failed to set %s to default format '%s' %ux%u",
534+
dev->name, VIDEO_FOURCC_TO_STR(fmt.pixelformat), fmt.width, fmt.height);
535+
return ret;
536+
}
537+
538+
return 0;
539+
}

drivers/video/video_common.h

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,126 @@ int video_read_cci_reg(const struct i2c_dt_spec *i2c, uint32_t reg_addr, uint32_
135135
*/
136136
int video_write_cci_multi(const struct i2c_dt_spec *i2c, const struct video_reg *regs);
137137

138+
/** @} */
139+
140+
/**
141+
* @defgroup video_imager Video Imager (image sensor) shared implementation
142+
*
143+
* This API is targeting image sensor driver developers.
144+
*
145+
* It provides a common implementation that only requires implementing a table of
146+
* @ref video_imager_mode that lists the various resolution, frame rates and associated I2C
147+
* configuration registers.
148+
*
149+
* The @c video_imager_... functions can be submitted directly to the @rev video_api.
150+
* If a driver also needs to do extra work before or after applying a mode, it is possible
151+
* to provide a custom wrapper or skip these default implementation altogether.
152+
*
153+
* @{
154+
*/
155+
156+
/**
157+
* @brief Table entry for an imaging mode of the sensor device.
158+
*
159+
* AA mode can be applied to an imager to configure a particular framerate, resolution, and pixel
160+
* format. The index of the table of modes is meant to match the index of the table of formats.
161+
*/
162+
struct video_imager_mode {
163+
/* FPS for this mode */
164+
uint16_t fps;
165+
/* Multiple lists of registers to allow sharing common sets of registers across modes. */
166+
const struct video_reg *regs[3];
167+
};
168+
169+
/**
170+
* @brief A video imager device is expected to have dev->data point to this structure.
171+
*
172+
* In order to support custom data structure, it is possible to store an extra pointer
173+
* in the dev->config struct. See existing drivers for an example.
174+
*/
175+
struct video_imager_config {
176+
/** List of all formats supported by this sensor */
177+
const struct video_format_cap *fmts;
178+
/** Array of modes tables, one table per format cap lislted by "fmts" */
179+
const struct video_imager_mode **modes;
180+
/** I2C device to write the registers to */
181+
struct i2c_dt_spec i2c;
182+
/** Write a table of registers onto the device */
183+
int (*write_multi)(const struct i2c_dt_spec *i2c, const struct video_reg *regs);
184+
/** Reference to a ; */
185+
struct video_imager_data *data;
186+
};
187+
188+
/**
189+
* @brief A video imager device is expected to have dev->data point to this structure.
190+
*
191+
* In order to support custom data structure, it is possible to store an extra pointer
192+
* in the dev->config struct. See existing drivers for an example.
193+
*/
194+
struct video_imager_data {
195+
/** Index of the currently active format in both modes[] and fmts[] */
196+
int fmt_id;
197+
/** Currently active video format */
198+
struct video_format fmt;
199+
/** Currently active operating mode as defined above */
200+
const struct video_imager_mode *mode;
201+
};
202+
203+
/**
204+
* @brief Initialize one row of a @struct video_format_cap with fixed width and height.
205+
*
206+
* The minimum and maximum are the same for both width and height fields.
207+
* @param
208+
*/
209+
#define VIDEO_IMAGER_FORMAT_CAP(pixfmt, width, height) \
210+
{ \
211+
.width_min = (width), .width_max = (width), .width_step = 0, \
212+
.height_min = (height), .height_max = (height), .height_step = 0, \
213+
.pixelformat = (pixfmt), \
214+
}
215+
216+
/**
217+
* @brief Set the operating mode of the imager as defined in @ref video_imager_mode.
218+
*
219+
* If the default immplementation for the video API are used, there is no need to explicitly call
220+
* this function in the image sensor driver.
221+
*
222+
* @param dev Device that has a struct video_imager in @c dev->data.
223+
* @param mode The mode to apply to the image sensor.
224+
* @return 0 if successful, or negative error number otherwise.
225+
*/
226+
int video_imager_set_mode(const struct device *dev, const struct video_imager_mode *mode);
227+
228+
/** @brief Default implementation for image drivers frame interval selection */
229+
int video_imager_set_frmival(const struct device *dev, enum video_endpoint_id ep,
230+
struct video_frmival *frmival);
231+
/** @brief Default implementation for image drivers frame interval query */
232+
int video_imager_get_frmival(const struct device *dev, enum video_endpoint_id ep,
233+
struct video_frmival *frmival);
234+
/** @brief Default implementation for image drivers frame interval enumeration */
235+
int video_imager_enum_frmival(const struct device *dev, enum video_endpoint_id ep,
236+
struct video_frmival_enum *fie);
237+
/** @brief Default implementation for image drivers format selection */
238+
int video_imager_set_fmt(const struct device *const dev, enum video_endpoint_id ep,
239+
struct video_format *fmt);
240+
/** @brief Default implementation for image drivers format query */
241+
int video_imager_get_fmt(const struct device *dev, enum video_endpoint_id ep,
242+
struct video_format *fmt);
243+
/** @brief Default implementation for image drivers format capabilities */
244+
int video_imager_get_caps(const struct device *dev, enum video_endpoint_id ep,
245+
struct video_caps *caps);
246+
247+
/**
248+
* Initialize an imager by loading init_regs onto the device, and setting the default format.
249+
*
250+
* @param dev Device that has a struct video_imager in @c dev->data.
251+
* @param init_regs If non-NULL, table of registers to configure at init.
252+
* @param default_fmt_idx Default format index to apply at init.
253+
* @return 0 if successful, or negative error number otherwise.
254+
*/
255+
int video_imager_init(const struct device *dev, const struct video_reg *init_regs,
256+
int default_fmt_idx);
257+
258+
/** @} */
259+
138260
#endif /* ZEPHYR_DRIVERS_VIDEO_COMMON_H_ */

0 commit comments

Comments
 (0)