drivers: video: common: Add utilities to seek frmival/caps structures

Introduce a video_get_format_index() utility to help finding a caps
entry out of a given format. Introduce several utilities to seek and
apply frame intervals.

Signed-off-by: Josuah Demangeon <me@josuah.net>
This commit is contained in:
Josuah Demangeon 2024-10-21 14:06:49 +00:00 committed by Anas Nashif
commit 46a262ffe6
6 changed files with 314 additions and 1 deletions

View file

@ -1,9 +1,12 @@
/*
* Copyright (c) 2019, Linaro Limited
* Copyright (c) 2024, tinyVision.ai Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/video.h>
@ -83,3 +86,81 @@ void video_buffer_release(struct video_buffer *vbuf)
VIDEO_COMMON_FREE(block->data);
}
}
int video_format_caps_index(const struct video_format_cap *fmts, const struct video_format *fmt,
size_t *idx)
{
for (int i = 0; fmts[i].pixelformat != 0; i++) {
if (fmts[i].pixelformat == fmt->pixelformat &&
IN_RANGE(fmt->width, fmts[i].width_min, fmts[i].width_max) &&
IN_RANGE(fmt->height, fmts[i].height_min, fmts[i].height_max)) {
*idx = i;
return 0;
}
}
return -ENOENT;
}
void video_closest_frmival_stepwise(const struct video_frmival_stepwise *stepwise,
const struct video_frmival *desired,
struct video_frmival *match)
{
uint64_t min = stepwise->min.numerator;
uint64_t max = stepwise->max.numerator;
uint64_t step = stepwise->step.numerator;
uint64_t goal = desired->numerator;
/* Set a common denominator to all values */
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;
/* Saturate the desired value to the min/max supported */
goal = CLAMP(goal, min, max);
/* Compute a numerator and denominator */
match->numerator = min + DIV_ROUND_CLOSEST(goal - min, step) * step;
match->denominator = stepwise->min.denominator * stepwise->max.denominator *
stepwise->step.denominator * desired->denominator;
}
void video_closest_frmival(const struct device *dev, enum video_endpoint_id ep,
struct video_frmival_enum *match)
{
uint64_t best_diff_nsec = INT32_MAX;
struct video_frmival desired = match->discrete;
struct video_frmival_enum fie = {.format = match->format};
__ASSERT(match->type != VIDEO_FRMIVAL_TYPE_STEPWISE,
"cannot find range matching the range, only a value matching the range");
while (video_enum_frmival(dev, ep, &fie) == 0) {
struct video_frmival tmp = {0};
uint64_t diff_nsec = 0, a, b;
switch (fie.type) {
case VIDEO_FRMIVAL_TYPE_DISCRETE:
tmp = fie.discrete;
break;
case VIDEO_FRMIVAL_TYPE_STEPWISE:
video_closest_frmival_stepwise(&fie.stepwise, &desired, &tmp);
break;
default:
__ASSERT(false, "invalid answer from the queried video device");
}
a = video_frmival_nsec(&desired);
b = video_frmival_nsec(&tmp);
diff_nsec = a > b ? a - b : b - a;
if (diff_nsec < best_diff_nsec) {
best_diff_nsec = diff_nsec;
memcpy(&match->discrete, &tmp, sizeof(tmp));
/* The video_enum_frmival() function will increment fie.index every time.
* Compensate for it to get the current index, not the next index.
*/
match->index = fie.index - 1;
}
}
}