When enumerating controls with VIDEO_CTRL_FLAG_NEXT_CTRL, if child devices have controls with IDs lower or equal to the ones in the parent devices, those controls will be accidentally skipped. Fix this by resetting the query's ID and tracking of the queried device in the query when moving to the next device in the pipeline. Signed-off-by: Phi Bang Nguyen <phibang.nguyen@nxp.com> Signed-off-by: Josuah Demangeon <me@josuah.net>
661 lines
16 KiB
C
661 lines
16 KiB
C
/*
|
|
* Copyright 2025 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <zephyr/drivers/video.h>
|
|
#include <zephyr/drivers/video-controls.h>
|
|
#include <zephyr/logging/log.h>
|
|
|
|
#include "video_ctrls.h"
|
|
#include "video_device.h"
|
|
|
|
LOG_MODULE_REGISTER(video_ctrls, CONFIG_VIDEO_LOG_LEVEL);
|
|
|
|
static inline const char *const *video_get_std_menu_ctrl(uint32_t id)
|
|
{
|
|
static char const *const power_line_frequency[] = {
|
|
"Disabled", "50 Hz", "60 Hz", "Auto", NULL,
|
|
};
|
|
static char const *const exposure_auto[] = {
|
|
"Auto Mode", "Manual Mode", "Shutter Priority Mode", "Aperture Priority Mode", NULL,
|
|
};
|
|
static char const *const colorfx[] = {
|
|
"None", "Black & White", "Sepia", "Negative", "Emboss", "Sketch", "Sky Blue",
|
|
"Grass Green", "Skin Whiten", "Vivid", "Aqua", "Art Freeze", "Silhouette",
|
|
"Solarization", "Antique", "Set Cb/Cr", NULL,
|
|
};
|
|
static char const *const camera_orientation[] = {
|
|
"Front", "Back", "External", NULL,
|
|
};
|
|
|
|
switch (id) {
|
|
/* User control menus */
|
|
case VIDEO_CID_POWER_LINE_FREQUENCY:
|
|
return power_line_frequency;
|
|
|
|
/* Camera control menus */
|
|
case VIDEO_CID_EXPOSURE_AUTO:
|
|
return exposure_auto;
|
|
case VIDEO_CID_COLORFX:
|
|
return colorfx;
|
|
case VIDEO_CID_CAMERA_ORIENTATION:
|
|
return camera_orientation;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static inline int check_range(enum video_ctrl_type type, struct video_ctrl_range range)
|
|
{
|
|
switch (type) {
|
|
case VIDEO_CTRL_TYPE_BOOLEAN:
|
|
if (range.step != 1 || range.max > 1 || range.min < 0) {
|
|
return -ERANGE;
|
|
}
|
|
return 0;
|
|
case VIDEO_CTRL_TYPE_INTEGER:
|
|
if (range.step == 0 || range.min > range.max ||
|
|
!IN_RANGE(range.def, range.min, range.max)) {
|
|
return -ERANGE;
|
|
}
|
|
return 0;
|
|
case VIDEO_CTRL_TYPE_INTEGER64:
|
|
if (range.step64 == 0 || range.min64 > range.max64 ||
|
|
!IN_RANGE(range.def64, range.min64, range.max64)) {
|
|
return -ERANGE;
|
|
}
|
|
return 0;
|
|
case VIDEO_CTRL_TYPE_MENU:
|
|
case VIDEO_CTRL_TYPE_INTEGER_MENU:
|
|
if (!IN_RANGE(range.min, 0, range.max) ||
|
|
!IN_RANGE(range.def, range.min, range.max)) {
|
|
return -ERANGE;
|
|
}
|
|
return 0;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static inline void set_type_flag(uint32_t id, enum video_ctrl_type *type, uint32_t *flags)
|
|
{
|
|
*flags = 0;
|
|
|
|
switch (id) {
|
|
case VIDEO_CID_AUTO_WHITE_BALANCE:
|
|
case VIDEO_CID_AUTOGAIN:
|
|
case VIDEO_CID_HFLIP:
|
|
case VIDEO_CID_VFLIP:
|
|
case VIDEO_CID_HUE_AUTO:
|
|
case VIDEO_CID_AUTOBRIGHTNESS:
|
|
case VIDEO_CID_EXPOSURE_AUTO_PRIORITY:
|
|
case VIDEO_CID_FOCUS_AUTO:
|
|
case VIDEO_CID_WIDE_DYNAMIC_RANGE:
|
|
*type = VIDEO_CTRL_TYPE_BOOLEAN;
|
|
break;
|
|
case VIDEO_CID_POWER_LINE_FREQUENCY:
|
|
case VIDEO_CID_EXPOSURE_AUTO:
|
|
case VIDEO_CID_COLORFX:
|
|
case VIDEO_CID_TEST_PATTERN:
|
|
case VIDEO_CID_CAMERA_ORIENTATION:
|
|
*type = VIDEO_CTRL_TYPE_MENU;
|
|
break;
|
|
case VIDEO_CID_PIXEL_RATE:
|
|
*type = VIDEO_CTRL_TYPE_INTEGER64;
|
|
*flags |= VIDEO_CTRL_FLAG_READ_ONLY;
|
|
break;
|
|
case VIDEO_CID_LINK_FREQ:
|
|
*type = VIDEO_CTRL_TYPE_INTEGER_MENU;
|
|
break;
|
|
default:
|
|
*type = VIDEO_CTRL_TYPE_INTEGER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id,
|
|
struct video_ctrl_range range)
|
|
{
|
|
int ret;
|
|
uint32_t flags;
|
|
enum video_ctrl_type type;
|
|
struct video_ctrl *vc;
|
|
struct video_device *vdev;
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
__ASSERT_NO_MSG(ctrl != NULL);
|
|
|
|
vdev = video_find_vdev(dev);
|
|
if (!vdev) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Sanity checks */
|
|
if (id < VIDEO_CID_BASE) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
set_type_flag(id, &type, &flags);
|
|
|
|
ret = check_range(type, range);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ctrl->cluster_sz = 0;
|
|
ctrl->cluster = NULL;
|
|
ctrl->is_auto = false;
|
|
ctrl->has_volatiles = false;
|
|
ctrl->menu = NULL;
|
|
ctrl->vdev = vdev;
|
|
ctrl->id = id;
|
|
ctrl->type = type;
|
|
ctrl->flags = flags;
|
|
ctrl->range = range;
|
|
|
|
if (type == VIDEO_CTRL_TYPE_INTEGER64) {
|
|
ctrl->val64 = range.def64;
|
|
} else {
|
|
ctrl->val = range.def;
|
|
}
|
|
|
|
/* Insert in an ascending order of ctrl's id */
|
|
SYS_DLIST_FOR_EACH_CONTAINER(&vdev->ctrls, vc, node) {
|
|
if (vc->id > ctrl->id) {
|
|
sys_dlist_insert(&vc->node, &ctrl->node);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
sys_dlist_append(&vdev->ctrls, &ctrl->node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int video_init_menu_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id,
|
|
uint8_t def, const char *const menu[])
|
|
{
|
|
int ret;
|
|
uint8_t sz = 0;
|
|
const char *const *_menu = menu ? menu : video_get_std_menu_ctrl(id);
|
|
|
|
if (!_menu) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
while (_menu[sz]) {
|
|
sz++;
|
|
}
|
|
|
|
ret = video_init_ctrl(
|
|
ctrl, dev, id,
|
|
(struct video_ctrl_range){.min = 0, .max = sz - 1, .step = 1, .def = def});
|
|
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ctrl->menu = _menu;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int video_init_int_menu_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id,
|
|
uint8_t def, const int64_t menu[], size_t menu_len)
|
|
{
|
|
int ret;
|
|
|
|
if (!menu) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = video_init_ctrl(
|
|
ctrl, dev, id,
|
|
(struct video_ctrl_range){.min = 0, .max = menu_len - 1, .step = 1, .def = def});
|
|
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ctrl->int_menu = menu;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* By definition, the cluster is in manual mode if the master control value is 0 */
|
|
static inline bool is_cluster_manual(const struct video_ctrl *master)
|
|
{
|
|
return master->type == VIDEO_CTRL_TYPE_INTEGER64 ? master->val64 == 0 : master->val == 0;
|
|
}
|
|
|
|
void video_cluster_ctrl(struct video_ctrl *ctrls, uint8_t sz)
|
|
{
|
|
bool has_volatiles = false;
|
|
|
|
__ASSERT(!sz && !ctrls, "The 1st control, i.e. the master, must not be NULL");
|
|
|
|
for (uint8_t i = 0; i < sz; i++) {
|
|
ctrls[i].cluster_sz = sz;
|
|
ctrls[i].cluster = ctrls;
|
|
if (ctrls[i].flags & VIDEO_CTRL_FLAG_VOLATILE) {
|
|
has_volatiles = true;
|
|
}
|
|
}
|
|
|
|
ctrls->has_volatiles = has_volatiles;
|
|
}
|
|
|
|
void video_auto_cluster_ctrl(struct video_ctrl *ctrls, uint8_t sz, bool set_volatile)
|
|
{
|
|
video_cluster_ctrl(ctrls, sz);
|
|
|
|
__ASSERT(sz > 1, "Control auto cluster size must be > 1");
|
|
__ASSERT(!(set_volatile && !DEVICE_API_GET(video, ctrls->vdev->dev)->get_volatile_ctrl),
|
|
"Volatile is set but no ops");
|
|
|
|
ctrls->is_auto = true;
|
|
ctrls->has_volatiles = set_volatile;
|
|
ctrls->flags |= VIDEO_CTRL_FLAG_UPDATE;
|
|
|
|
/* If the cluster is in automatic mode, mark all manual controls inactive and volatile */
|
|
for (uint8_t i = 1; i < sz; i++) {
|
|
if (!is_cluster_manual(ctrls)) {
|
|
ctrls[i].flags |= VIDEO_CTRL_FLAG_INACTIVE |
|
|
(set_volatile ? VIDEO_CTRL_FLAG_VOLATILE : 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int video_find_ctrl(const struct device *dev, uint32_t id, struct video_ctrl **ctrl)
|
|
{
|
|
struct video_device *vdev = video_find_vdev(dev);
|
|
|
|
while (vdev) {
|
|
SYS_DLIST_FOR_EACH_CONTAINER(&vdev->ctrls, *ctrl, node) {
|
|
if ((*ctrl)->id == id) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
vdev = video_find_vdev(vdev->src_dev);
|
|
}
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
int video_get_ctrl(const struct device *dev, struct video_control *control)
|
|
{
|
|
struct video_ctrl *ctrl = NULL;
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
__ASSERT_NO_MSG(control != NULL);
|
|
|
|
int ret = video_find_ctrl(dev, control->id, &ctrl);
|
|
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (ctrl->flags & VIDEO_CTRL_FLAG_WRITE_ONLY) {
|
|
LOG_ERR("Control id 0x%x is write-only\n", control->id);
|
|
return -EACCES;
|
|
}
|
|
|
|
if (ctrl->flags & VIDEO_CTRL_FLAG_VOLATILE) {
|
|
if (DEVICE_API_GET(video, ctrl->vdev->dev)->get_volatile_ctrl == NULL) {
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/* Call driver's get_volatile_ctrl */
|
|
ret = DEVICE_API_GET(video, ctrl->vdev->dev)
|
|
->get_volatile_ctrl(ctrl->vdev->dev,
|
|
ctrl->cluster ? ctrl->cluster->id : ctrl->id);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Give the control's current value to user */
|
|
if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64) {
|
|
control->val64 = ctrl->val64;
|
|
} else {
|
|
control->val = ctrl->val;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int video_set_ctrl(const struct device *dev, struct video_control *control)
|
|
{
|
|
struct video_ctrl *ctrl = NULL;
|
|
int ret;
|
|
uint8_t i = 0;
|
|
int32_t val = 0;
|
|
int64_t val64 = 0;
|
|
|
|
__ASSERT_NO_MSG(dev != NULL);
|
|
__ASSERT_NO_MSG(control != NULL);
|
|
|
|
ret = video_find_ctrl(dev, control->id, &ctrl);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (ctrl->flags & VIDEO_CTRL_FLAG_READ_ONLY) {
|
|
LOG_ERR("Control id 0x%x is read-only\n", control->id);
|
|
return -EACCES;
|
|
}
|
|
|
|
if (ctrl->flags & VIDEO_CTRL_FLAG_INACTIVE) {
|
|
LOG_ERR("Control id 0x%x is inactive\n", control->id);
|
|
return -EACCES;
|
|
}
|
|
|
|
if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64
|
|
? !IN_RANGE(control->val64, ctrl->range.min64, ctrl->range.max64)
|
|
: !IN_RANGE(control->val, ctrl->range.min, ctrl->range.max)) {
|
|
LOG_ERR("Control value is invalid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* No new value */
|
|
if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64 ? ctrl->val64 == control->val64
|
|
: ctrl->val == control->val) {
|
|
return 0;
|
|
}
|
|
|
|
/* Backup the control's value then set it to the new value */
|
|
if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64) {
|
|
val64 = ctrl->val64;
|
|
ctrl->val64 = control->val64;
|
|
} else {
|
|
val = ctrl->val;
|
|
ctrl->val = control->val;
|
|
}
|
|
|
|
/*
|
|
* For auto-clusters having volatiles, before switching to manual mode, get the current
|
|
* volatile values since those will become the initial manual values after this switch.
|
|
*/
|
|
if (ctrl == ctrl->cluster && ctrl->is_auto && ctrl->has_volatiles &&
|
|
is_cluster_manual(ctrl)) {
|
|
|
|
if (DEVICE_API_GET(video, ctrl->vdev->dev)->get_volatile_ctrl == NULL) {
|
|
ret = -ENOSYS;
|
|
goto restore;
|
|
}
|
|
|
|
ret = DEVICE_API_GET(video, ctrl->vdev->dev)
|
|
->get_volatile_ctrl(ctrl->vdev->dev, ctrl->id);
|
|
if (ret) {
|
|
goto restore;
|
|
}
|
|
}
|
|
|
|
/* Call driver's set_ctrl */
|
|
if (DEVICE_API_GET(video, ctrl->vdev->dev)->set_ctrl) {
|
|
ret = DEVICE_API_GET(video, ctrl->vdev->dev)
|
|
->set_ctrl(ctrl->vdev->dev,
|
|
ctrl->cluster ? ctrl->cluster->id : ctrl->id);
|
|
if (ret) {
|
|
goto restore;
|
|
}
|
|
}
|
|
|
|
/* Update the manual controls' flags of the cluster */
|
|
if (ctrl->cluster && ctrl->cluster->is_auto) {
|
|
for (i = 1; i < ctrl->cluster_sz; i++) {
|
|
if (!is_cluster_manual(ctrl->cluster)) {
|
|
/* Automatic mode: set the inactive and volatile flags of the manual
|
|
* controls
|
|
*/
|
|
ctrl->cluster[i].flags |=
|
|
VIDEO_CTRL_FLAG_INACTIVE |
|
|
(ctrl->cluster->has_volatiles ? VIDEO_CTRL_FLAG_VOLATILE
|
|
: 0);
|
|
} else {
|
|
/* Manual mode: clear the inactive and volatile flags of the manual
|
|
* controls
|
|
*/
|
|
ctrl->cluster[i].flags &=
|
|
~(VIDEO_CTRL_FLAG_INACTIVE | VIDEO_CTRL_FLAG_VOLATILE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
restore:
|
|
/* Restore the old control's value */
|
|
if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64) {
|
|
ctrl->val64 = val64;
|
|
} else {
|
|
ctrl->val = val;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline const char *video_get_ctrl_name(uint32_t id)
|
|
{
|
|
switch (id) {
|
|
/* User controls */
|
|
case VIDEO_CID_BRIGHTNESS:
|
|
return "Brightness";
|
|
case VIDEO_CID_CONTRAST:
|
|
return "Contrast";
|
|
case VIDEO_CID_SATURATION:
|
|
return "Saturation";
|
|
case VIDEO_CID_HUE:
|
|
return "Hue";
|
|
case VIDEO_CID_AUTO_WHITE_BALANCE:
|
|
return "White Balance, Automatic";
|
|
case VIDEO_CID_RED_BALANCE:
|
|
return "Red Balance";
|
|
case VIDEO_CID_BLUE_BALANCE:
|
|
return "Blue Balance";
|
|
case VIDEO_CID_GAMMA:
|
|
return "Gamma";
|
|
case VIDEO_CID_EXPOSURE:
|
|
return "Exposure";
|
|
case VIDEO_CID_AUTOGAIN:
|
|
return "Gain, Automatic";
|
|
case VIDEO_CID_GAIN:
|
|
return "Gain";
|
|
case VIDEO_CID_ANALOGUE_GAIN:
|
|
return "Analogue Gain";
|
|
case VIDEO_CID_HFLIP:
|
|
return "Horizontal Flip";
|
|
case VIDEO_CID_VFLIP:
|
|
return "Vertical Flip";
|
|
case VIDEO_CID_POWER_LINE_FREQUENCY:
|
|
return "Power Line Frequency";
|
|
case VIDEO_CID_HUE_AUTO:
|
|
return "Hue, Automatic";
|
|
case VIDEO_CID_WHITE_BALANCE_TEMPERATURE:
|
|
return "White Balance Temperature";
|
|
case VIDEO_CID_SHARPNESS:
|
|
return "Sharpness";
|
|
case VIDEO_CID_BACKLIGHT_COMPENSATION:
|
|
return "Backlight Compensation";
|
|
case VIDEO_CID_COLORFX:
|
|
return "Color Effects";
|
|
case VIDEO_CID_AUTOBRIGHTNESS:
|
|
return "Brightness, Automatic";
|
|
case VIDEO_CID_BAND_STOP_FILTER:
|
|
return "Band-Stop Filter";
|
|
case VIDEO_CID_ALPHA_COMPONENT:
|
|
return "Alpha Component";
|
|
|
|
/* Camera controls */
|
|
case VIDEO_CID_EXPOSURE_AUTO:
|
|
return "Auto Exposure";
|
|
case VIDEO_CID_EXPOSURE_ABSOLUTE:
|
|
return "Exposure Time, Absolute";
|
|
case VIDEO_CID_EXPOSURE_AUTO_PRIORITY:
|
|
return "Exposure, Dynamic Framerate";
|
|
case VIDEO_CID_PAN_RELATIVE:
|
|
return "Pan, Relative";
|
|
case VIDEO_CID_TILT_RELATIVE:
|
|
return "Tilt, Reset";
|
|
case VIDEO_CID_PAN_ABSOLUTE:
|
|
return "Pan, Absolute";
|
|
case VIDEO_CID_TILT_ABSOLUTE:
|
|
return "Tilt, Absolute";
|
|
case VIDEO_CID_FOCUS_ABSOLUTE:
|
|
return "Focus, Absolute";
|
|
case VIDEO_CID_FOCUS_RELATIVE:
|
|
return "Focus, Relative";
|
|
case VIDEO_CID_FOCUS_AUTO:
|
|
return "Focus, Automatic Continuous";
|
|
case VIDEO_CID_ZOOM_ABSOLUTE:
|
|
return "Zoom, Absolute";
|
|
case VIDEO_CID_ZOOM_RELATIVE:
|
|
return "Zoom, Relative";
|
|
case VIDEO_CID_ZOOM_CONTINUOUS:
|
|
return "Zoom, Continuous";
|
|
case VIDEO_CID_IRIS_ABSOLUTE:
|
|
return "Iris, Absolute";
|
|
case VIDEO_CID_IRIS_RELATIVE:
|
|
return "Iris, Relative";
|
|
case VIDEO_CID_WIDE_DYNAMIC_RANGE:
|
|
return "Wide Dynamic Range";
|
|
case VIDEO_CID_PAN_SPEED:
|
|
return "Pan, Speed";
|
|
case VIDEO_CID_TILT_SPEED:
|
|
return "Tilt, Speed";
|
|
case VIDEO_CID_CAMERA_ORIENTATION:
|
|
return "Camera Orientation";
|
|
case VIDEO_CID_CAMERA_SENSOR_ROTATION:
|
|
return "Camera Sensor Rotation";
|
|
|
|
/* JPEG encoder controls */
|
|
case VIDEO_CID_JPEG_COMPRESSION_QUALITY:
|
|
return "Compression Quality";
|
|
|
|
/* Image processing controls */
|
|
case VIDEO_CID_PIXEL_RATE:
|
|
return "Pixel Rate";
|
|
case VIDEO_CID_TEST_PATTERN:
|
|
return "Test Pattern";
|
|
case VIDEO_CID_LINK_FREQ:
|
|
return "Link Frequency";
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
int video_query_ctrl(struct video_ctrl_query *cq)
|
|
{
|
|
int ret;
|
|
struct video_device *vdev;
|
|
struct video_ctrl *ctrl = NULL;
|
|
|
|
__ASSERT_NO_MSG(cq != NULL);
|
|
__ASSERT_NO_MSG(cq->dev != NULL);
|
|
|
|
if (cq->id & VIDEO_CTRL_FLAG_NEXT_CTRL) {
|
|
cq->id &= ~VIDEO_CTRL_FLAG_NEXT_CTRL;
|
|
vdev = video_find_vdev(cq->dev);
|
|
while (vdev != NULL) {
|
|
SYS_DLIST_FOR_EACH_CONTAINER(&vdev->ctrls, ctrl, node) {
|
|
if (ctrl->id > cq->id) {
|
|
goto fill_query;
|
|
}
|
|
}
|
|
cq->id = 0;
|
|
cq->dev = vdev->src_dev;
|
|
vdev = video_find_vdev(cq->dev);
|
|
}
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
ret = video_find_ctrl(cq->dev, cq->id, &ctrl);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
fill_query:
|
|
cq->id = ctrl->id;
|
|
cq->type = ctrl->type;
|
|
cq->flags = ctrl->flags;
|
|
cq->range = ctrl->range;
|
|
if (cq->type == VIDEO_CTRL_TYPE_MENU) {
|
|
cq->menu = ctrl->menu;
|
|
} else if (cq->type == VIDEO_CTRL_TYPE_INTEGER_MENU) {
|
|
cq->int_menu = ctrl->int_menu;
|
|
}
|
|
cq->name = video_get_ctrl_name(cq->id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void video_print_ctrl(const struct video_ctrl_query *const cq)
|
|
{
|
|
uint8_t i = 0;
|
|
const char *type = NULL;
|
|
char typebuf[8];
|
|
|
|
__ASSERT_NO_MSG(cq != NULL);
|
|
__ASSERT_NO_MSG(cq->dev != NULL);
|
|
|
|
/* Get type of the control */
|
|
switch (cq->type) {
|
|
case VIDEO_CTRL_TYPE_BOOLEAN:
|
|
type = "bool";
|
|
break;
|
|
case VIDEO_CTRL_TYPE_INTEGER:
|
|
type = "int";
|
|
break;
|
|
case VIDEO_CTRL_TYPE_INTEGER64:
|
|
type = "int64";
|
|
break;
|
|
case VIDEO_CTRL_TYPE_MENU:
|
|
type = "menu";
|
|
break;
|
|
case VIDEO_CTRL_TYPE_INTEGER_MENU:
|
|
type = "integer menu";
|
|
break;
|
|
case VIDEO_CTRL_TYPE_STRING:
|
|
type = "string";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
snprintf(typebuf, sizeof(typebuf), "(%s)", type);
|
|
|
|
/* Get current value of the control */
|
|
struct video_control vc = {.id = cq->id};
|
|
|
|
video_get_ctrl(cq->dev, &vc);
|
|
|
|
/* Print the control information */
|
|
if (cq->type == VIDEO_CTRL_TYPE_INTEGER64) {
|
|
LOG_INF("%32s 0x%08x %-8s (flags=0x%02x) : min=%lld max=%lld step=%lld "
|
|
"default=%lld value=%lld ",
|
|
cq->name, cq->id, typebuf, cq->flags, cq->range.min64, cq->range.max64,
|
|
cq->range.step64, cq->range.def64, vc.val64);
|
|
} else {
|
|
LOG_INF("%32s 0x%08x %-8s (flags=0x%02x) : min=%d max=%d step=%d default=%d "
|
|
"value=%d ",
|
|
cq->name, cq->id, typebuf, cq->flags, cq->range.min, cq->range.max,
|
|
cq->range.step, cq->range.def, vc.val);
|
|
}
|
|
|
|
if (cq->type == VIDEO_CTRL_TYPE_MENU && cq->menu) {
|
|
while (cq->menu[i]) {
|
|
LOG_INF("%*s %u: %s", 32, "", i, cq->menu[i]);
|
|
i++;
|
|
}
|
|
} else if (cq->type == VIDEO_CTRL_TYPE_INTEGER_MENU && cq->int_menu) {
|
|
while (cq->int_menu[i]) {
|
|
LOG_INF("%*s %u: %lld", 12, "", i, cq->int_menu[i]);
|
|
i++;
|
|
}
|
|
}
|
|
}
|