Skip to content

Commit

Permalink
video: sensor_skeleton: starting point for writing new sensors
Browse files Browse the repository at this point in the history
Provide a default implementation of an image sensor driver to
facilitate writing new drivers, and provide some reference to
modify at the same time as API changes.

Introduce a video_get_format_index() to help finding a caps
entry out of a given format.

Fixes: zephyrproject-rtos#73867
Signed-off-by: Josuah Demangeon <[email protected]>
  • Loading branch information
josuah committed Oct 13, 2024
1 parent b6aed5c commit 59483ff
Show file tree
Hide file tree
Showing 3 changed files with 615 additions and 0 deletions.
163 changes: 163 additions & 0 deletions drivers/video/video_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Copyright (c) 2024, tinyVision.ai Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_VIDEO_COMMON_H
#define ZEPHYR_INCLUDE_VIDEO_COMMON_H

#include <zephyr/sys_clock.h>
#include <zephyr/sys/util.h>
#include <zephyr/drivers/video.h>

/**
* @brief Provide the minimum/maximum/default 32-bit value depending on the CID.
*
* Video CIDs can contain sub-operations. This function facilitates
* implementation of video controls in the drivers by handling these
* the range-related CIDs.
*
* @param cid The Video Control ID which contains the operation.
* @param min The minimum value returned if the CID requested it.
* @param max The maximum value returned if the CID requested it.
* @param def The default value returned if the CID requested it.
*
* @return 0 if the operation was regarding a range CID and the value could
* be set. In which case, there is nothing else to do than returning 0.
* @return 1 if the operation is not for the range, but the current value,
* in which case it is the duty of the driver to query the current
* value to the hardware
* @return A negative error code if an error occurred.
*/
int video_get_range_u32(unsigned int cid, uint32_t *u32, uint32_t min, uint32_t max, uint32_t def);

/**
* @brief Check if the 32-bit value is within range for this CID.
*
* Before setting a video control, a driver might be interested in checking
* if it is within a valid range. This function facilitates it by reusing the
* video_get_ctrl() API using @c VIDEO_CTRL_GET_MIN and @c VIDEO_CTRL_GET_MAX
* to validate the input.
*
* @param dev The video device to query to learn about the min and max.
* @param cid The CID for which to check the range.
* @param u32 The 32-bit value that must be matched against the range.
*/
int video_check_range_u32(const struct device *dev, unsigned int cid, uint32_t u32);

/**
* @brief Search for a format that matches in a list of capabilities
*
* @param fmts The format capability list to search.
* @param fmts_num The number of capabilities in that list.
* @param fmt The format to find in the list.
* @param idx The pointer to a number of the first format that matches.
*
* @return 0 when a format is found.
* @return -ENOENT when no matching format is found.
*/
static inline int video_get_format_index(struct video_format_cap *fmts, struct video_format *fmt,
int *idx)
{
for (int i = 0; fmts[i].pixelformat != 0; i++) {
if (fmts[i].width_min == fmt->width && fmts[i].height_min == fmt->height &&
fmts[i].pixelformat == fmt->pixelformat) {
*idx = i;
return 0;
}
}
return -ENOENT;
}

/**
* @brief Compute the difference between two frame intervals
*
* @param ap First frame interval.
* @param bp Second frame interval.
*
* @return The signed difference in microsecond between the two frame intervals.
*/
static inline int64_t video_diff_frmival_usec(struct video_frmival *ap, struct video_frmival *bp)
{
struct frmival a = *ap;
struct frmival b = *bp;

if (a.denominator != b.denominator) {
a.numerator *= b.denominator;
a.denominator *= b.denominator;
b.numerator *= a.denominator;
b.denominator *= a.denominator;
}

/* Return the difference in microseconds */
return DIV_ROUND_CLOSEST((int64_t)USEC_PER_SEC * a.numerator, a.denominator) -
DIV_ROUND_CLOSEST((int64_t)USEC_PER_SEC * b.numerator, b.denominator);
}

/**
* @brief Find the closest match to a frame interval value within a stepwise frame interval.
*
* @param stepwise The stepwise frame interval range to search
* @param desired The frame interval for which find the closest match
* @param best_match The resulting frame interval closest to @p desired
*/
static inline void video_closest_frmival_stepwise(const struct video_frmival_stepwise *stepwise,
const struct video_frmival *desired,
struct video_frmival *best_match)
{
uint32_t min = stepwise->min.numerator;
uint32_t max = stepwise->max.numerator;
uint32_t step = stepwise->step.numerator;
uint32_t desi = desired->numerator;

min *= stepwise->max.denominator * stepwise->step.denominator * desired->denominator;
max *= stepwise->min.denominator * stepwise->step.denominator * desired->denominator;
step *= stepwise->min.denominator * stepwise->max.denominator * desired->denominator;
goal *= stepwise->min.denominator * stepwise->max.denominator * stepwise->step.denominator;

best_match->denominator = stepwise->min.denominator * stepwise->max.denominator *
stepwise->step.denominator * desired->denominator;
best_match->numerator = min + DIV_ROUND_CLOSEST(goal - min, step) * step;
}

/**
* @brief Find the closest match to a frame interval value within a video device.
*
* @param dev Video device to search within.
* @param desired Frame interval for which find a close match.
* @param best_fie Frame interval enumerator pointing at the closest match.
*/
static inline void video_closest_frmival(const struct device *dev,
const struct video_frmival *desired,
struct video_frmival_enum *best_fie)
{
int32_t best_diff_usec = INT32_MAX;
struct video_frmival_enum fie = {.format = best_fie->format};
int ret;

while (video_enum_frmival(dev, ep, &fie) == 0) {
struct video_frmival best_stepwise = {0};
int32_t diff_usec;

switch (fie->type) {
case VIDEO_FRMIVAL_TYPE_DISCRETE:
diff_usec = video_diff_frmival_usec(desired, &fie->discrete);
break;
case VIDEO_FRMIVAL_TYPE_DISCRETE:
video_closest_frmival_stepwise(&fie->stepwise, desired, &best_stepwise);
diff_usec = video_diff_frmival_usec(desired, &best_stepwise);
break;
default:
__ASSERT(false, "invalid video device")
return;
}

if (ABS(diff_usec) < best_diff_usec) {
best_diff_usec = diff_usec;
memcpy(best_fie, &fie, sizeof(*best_fie));
}
}
}

#endif /* ZEPHYR_INCLUDE_VIDEO_COMMON_H */
Loading

0 comments on commit 59483ff

Please sign in to comment.