Replaced the term "master" by "primary" in video_ctrls.c/.h Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
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 primary control value is 0 */
|
|
static inline bool is_cluster_manual(const struct video_ctrl *primary)
|
|
{
|
|
return primary->type == VIDEO_CTRL_TYPE_INTEGER64 ? primary->val64 == 0 : primary->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 primary control, 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++;
|
|
}
|
|
}
|
|
}
|