From 33df1020c207e16fb4a200060f890f8258c44db3 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Wed, 19 Mar 2025 14:13:17 +0100 Subject: [PATCH 1/8] drivers: video: Introduce video device structure Introduce a new video device structure representing a device in a video pipeline. Each video device embeds a pointer to its "source" device and other "video" characteristics. This structure give the video framework an access to all video features of the device and a hierachical view of the video pipeline that the device belongs to. Signed-off-by: Phi Bang Nguyen --- cmake/linker_script/common/common-ram.cmake | 3 ++ drivers/video/CMakeLists.txt | 3 ++ drivers/video/gc2145.c | 5 +++ drivers/video/mt9m114.c | 5 +++ drivers/video/ov2640.c | 4 +++ drivers/video/ov5640.c | 6 +++- drivers/video/ov7670.c | 6 +++- drivers/video/ov7725.c | 4 +++ drivers/video/video.ld | 3 ++ drivers/video/video_device.c | 22 ++++++++++++ drivers/video/video_device.h | 25 +++++++++++++ drivers/video/video_emul_imager.c | 6 +++- drivers/video/video_emul_rx.c | 6 +++- drivers/video/video_esp32_dvp.c | 5 +++ drivers/video/video_mcux_csi.c | 5 +++ drivers/video/video_mcux_mipi_csi2rx.c | 7 +++- drivers/video/video_mcux_smartdma.c | 40 +++++++++++---------- drivers/video/video_stm32_dcmi.c | 4 +++ drivers/video/video_sw_generator.c | 4 +++ 19 files changed, 139 insertions(+), 24 deletions(-) create mode 100644 drivers/video/video.ld create mode 100644 drivers/video/video_device.c create mode 100644 drivers/video/video_device.h diff --git a/cmake/linker_script/common/common-ram.cmake b/cmake/linker_script/common/common-ram.cmake index 0dda1ee3c0a2..d46376edd03f 100644 --- a/cmake/linker_script/common/common-ram.cmake +++ b/cmake/linker_script/common/common-ram.cmake @@ -125,6 +125,9 @@ if(CONFIG_UVB) zephyr_iterable_section(NAME uvb_node GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN ${CONFIG_LINKER_ITERABLE_SUBALIGN}) endif() +if(CONFIG_VIDEO) + zephyr_iterable_section(NAME video_device GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN ${CONFIG_LINKER_ITERABLE_SUBALIGN}) +endif() if(CONFIG_LOG) zephyr_iterable_section(NAME log_mpsc_pbuf GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN ${CONFIG_LINKER_ITERABLE_SUBALIGN}) diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index 6210f8d881d0..c9c617f1a0bb 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -3,6 +3,7 @@ zephyr_library() zephyr_library_sources(video_common.c) +zephyr_library_sources(video_device.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_CSI video_mcux_csi.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_MIPI_CSI2RX video_mcux_mipi_csi2rx.c) @@ -18,3 +19,5 @@ zephyr_library_sources_ifdef(CONFIG_VIDEO_ESP32 video_esp32_dvp.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_SDMA video_mcux_smartdma.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_EMUL_IMAGER video_emul_imager.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_EMUL_RX video_emul_rx.c) + +zephyr_linker_sources(DATA_SECTIONS video.ld) diff --git a/drivers/video/gc2145.c b/drivers/video/gc2145.c index 8ac3cc24033c..676ba48d61ba 100644 --- a/drivers/video/gc2145.c +++ b/drivers/video/gc2145.c @@ -14,6 +14,9 @@ #include #include + +#include "video_device.h" + LOG_MODULE_REGISTER(video_gc2145, CONFIG_VIDEO_LOG_LEVEL); #define GC2145_REG_AMODE1 0x17 @@ -1208,3 +1211,5 @@ static int gc2145_init_0(const struct device *dev) DEVICE_DT_INST_DEFINE(0, &gc2145_init_0, NULL, &gc2145_data_0, &gc2145_cfg_0, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &gc2145_driver_api); + +VIDEO_DEVICE_DEFINE(gc2145, DEVICE_DT_INST_GET(0), NULL); diff --git a/drivers/video/mt9m114.c b/drivers/video/mt9m114.c index 663fa422201e..e7536a6d9519 100644 --- a/drivers/video/mt9m114.c +++ b/drivers/video/mt9m114.c @@ -15,6 +15,8 @@ #include #include +#include "video_device.h" + LOG_MODULE_REGISTER(video_mt9m114, CONFIG_VIDEO_LOG_LEVEL); #define MT9M114_CHIP_ID_VAL 0x2481 @@ -569,4 +571,7 @@ static int mt9m114_init_0(const struct device *dev) DEVICE_DT_INST_DEFINE(0, &mt9m114_init_0, NULL, &mt9m114_data_0, &mt9m114_cfg_0, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &mt9m114_driver_api); + +VIDEO_DEVICE_DEFINE(mt9m114, DEVICE_DT_INST_GET(0), NULL); + #endif diff --git a/drivers/video/ov2640.c b/drivers/video/ov2640.c index f89e73dbd9f8..128979864f56 100644 --- a/drivers/video/ov2640.c +++ b/drivers/video/ov2640.c @@ -14,6 +14,8 @@ #include #include +#include "video_device.h" + LOG_MODULE_REGISTER(video_ov2640, CONFIG_VIDEO_LOG_LEVEL); /* DSP register bank FF=0x00*/ @@ -1062,3 +1064,5 @@ DEVICE_DT_INST_DEFINE(0, &ov2640_init_0, NULL, &ov2640_data_0, &ov2640_cfg_0, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &ov2640_driver_api); + +VIDEO_DEVICE_DEFINE(ov2640, DEVICE_DT_INST_GET(0), NULL); diff --git a/drivers/video/ov5640.c b/drivers/video/ov5640.c index cb567bd48bc0..334c5e364549 100644 --- a/drivers/video/ov5640.c +++ b/drivers/video/ov5640.c @@ -19,6 +19,8 @@ #include #include +#include "video_device.h" + LOG_MODULE_REGISTER(video_ov5640, CONFIG_VIDEO_LOG_LEVEL); #define CHIP_ID_REG 0x300a @@ -1333,6 +1335,8 @@ static int ov5640_init(const struct device *dev) }; \ \ DEVICE_DT_INST_DEFINE(n, &ov5640_init, NULL, &ov5640_data_##n, &ov5640_cfg_##n, \ - POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &ov5640_driver_api); + POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &ov5640_driver_api); \ + \ + VIDEO_DEVICE_DEFINE(ov5640_##n, DEVICE_DT_INST_GET(n), NULL); DT_INST_FOREACH_STATUS_OKAY(OV5640_INIT) diff --git a/drivers/video/ov7670.c b/drivers/video/ov7670.c index 37be06a76258..0e37cdda3e47 100644 --- a/drivers/video/ov7670.c +++ b/drivers/video/ov7670.c @@ -12,6 +12,8 @@ #include #include +#include "video_device.h" + LOG_MODULE_REGISTER(video_ov7670, CONFIG_VIDEO_LOG_LEVEL); /* Initialization register structure */ @@ -598,6 +600,8 @@ static DEVICE_API(video, ov7670_api) = { struct ov7670_data ov7670_data_##inst; \ \ DEVICE_DT_INST_DEFINE(inst, ov7670_init, NULL, &ov7670_data_##inst, &ov7670_config_##inst, \ - POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &ov7670_api); + POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &ov7670_api); \ + \ + VIDEO_DEVICE_DEFINE(ov7670_##inst, DEVICE_DT_INST_GET(inst), NULL); DT_INST_FOREACH_STATUS_OKAY(OV7670_INIT) diff --git a/drivers/video/ov7725.c b/drivers/video/ov7725.c index 74d1d8340172..9297d4a401d5 100644 --- a/drivers/video/ov7725.c +++ b/drivers/video/ov7725.c @@ -14,6 +14,8 @@ #include #include +#include "video_device.h" + LOG_MODULE_REGISTER(video_ov7725, CONFIG_VIDEO_LOG_LEVEL); #define OV7725_REVISION 0x7721U @@ -639,3 +641,5 @@ DEVICE_DT_INST_DEFINE(0, &ov7725_init_0, NULL, &ov7725_data_0, &ov7725_cfg_0, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &ov7725_driver_api); + +VIDEO_DEVICE_DEFINE(ov7725, DEVICE_DT_INST_GET(0), NULL); diff --git a/drivers/video/video.ld b/drivers/video/video.ld new file mode 100644 index 000000000000..935ee1ed302b --- /dev/null +++ b/drivers/video/video.ld @@ -0,0 +1,3 @@ +#include + +ITERABLE_SECTION_RAM(video_device, Z_LINK_ITERABLE_SUBALIGN) diff --git a/drivers/video/video_device.c b/drivers/video/video_device.c new file mode 100644 index 000000000000..a895fed42f14 --- /dev/null +++ b/drivers/video/video_device.c @@ -0,0 +1,22 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "video_device.h" + +struct video_device *video_find_vdev(const struct device *dev) +{ + if (!dev) { + return NULL; + } + + STRUCT_SECTION_FOREACH(video_device, vdev) { + if (vdev->dev == dev) { + return vdev; + } + } + + return NULL; +} diff --git a/drivers/video/video_device.h b/drivers/video/video_device.h new file mode 100644 index 000000000000..130a35b8733d --- /dev/null +++ b/drivers/video/video_device.h @@ -0,0 +1,25 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_VIDEO_VIDEO_DEVICE_H_ +#define ZEPHYR_INCLUDE_DRIVERS_VIDEO_VIDEO_DEVICE_H_ + +#include + +struct video_device { + const struct device *dev; + const struct device *src_dev; +}; + +#define VIDEO_DEVICE_DEFINE(name, device, source) \ + static STRUCT_SECTION_ITERABLE(video_device, name) = { \ + .dev = device, \ + .src_dev = source, \ + } + +struct video_device *video_find_vdev(const struct device *dev); + +#endif /* ZEPHYR_INCLUDE_DRIVERS_VIDEO_VIDEO_DEVICE_H_ */ diff --git a/drivers/video/video_emul_imager.c b/drivers/video/video_emul_imager.c index c7e26a9ff54f..90cec728ae0e 100644 --- a/drivers/video/video_emul_imager.c +++ b/drivers/video/video_emul_imager.c @@ -17,6 +17,8 @@ #include #include +#include "video_device.h" + LOG_MODULE_REGISTER(video_emul_imager, CONFIG_VIDEO_LOG_LEVEL); #define EMUL_IMAGER_REG_SENSOR_ID 0x0000 @@ -427,6 +429,8 @@ int emul_imager_init(const struct device *dev) \ DEVICE_DT_INST_DEFINE(inst, &emul_imager_init, NULL, &emul_imager_data_##inst, \ &emul_imager_cfg_##inst, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \ - &emul_imager_driver_api); + &emul_imager_driver_api); \ + \ + VIDEO_DEVICE_DEFINE(emul_imager_##inst, DEVICE_DT_INST_GET(inst), NULL); DT_INST_FOREACH_STATUS_OKAY(EMUL_IMAGER_DEFINE) diff --git a/drivers/video/video_emul_rx.c b/drivers/video/video_emul_rx.c index 2abb6fef32aa..31d8c84d4505 100644 --- a/drivers/video/video_emul_rx.c +++ b/drivers/video/video_emul_rx.c @@ -15,6 +15,8 @@ #include #include +#include "video_device.h" + LOG_MODULE_REGISTER(video_emul_rx, CONFIG_VIDEO_LOG_LEVEL); struct emul_rx_config { @@ -296,6 +298,8 @@ int emul_rx_init(const struct device *dev) }; \ \ DEVICE_DT_INST_DEFINE(n, &emul_rx_init, NULL, &emul_rx_data_##n, &emul_rx_cfg_##n, \ - POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &emul_rx_driver_api); + POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &emul_rx_driver_api); \ + \ + VIDEO_DEVICE_DEFINE(emul_rx_##n, DEVICE_DT_INST_GET(n), emul_rx_cfg_##n.source_dev); DT_INST_FOREACH_STATUS_OKAY(EMUL_RX_DEFINE) diff --git a/drivers/video/video_esp32_dvp.c b/drivers/video/video_esp32_dvp.c index 4c93f24bcd12..94cd8266fe63 100644 --- a/drivers/video/video_esp32_dvp.c +++ b/drivers/video/video_esp32_dvp.c @@ -21,6 +21,9 @@ #include #include + +#include "video_device.h" + LOG_MODULE_REGISTER(video_esp32_lcd_cam, CONFIG_VIDEO_LOG_LEVEL); #define VIDEO_ESP32_DMA_BUFFER_MAX_SIZE 4095 @@ -463,6 +466,8 @@ static struct video_esp32_data esp32_data = {0}; DEVICE_DT_INST_DEFINE(0, video_esp32_init, NULL, &esp32_data, &esp32_config, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &esp32_driver_api); +VIDEO_DEVICE_DEFINE(esp32, DEVICE_DT_INST_GET(0), esp32_config.source_dev); + static int video_esp32_cam_init_master_clock(void) { int ret = 0; diff --git a/drivers/video/video_mcux_csi.c b/drivers/video/video_mcux_csi.c index 34f80b8e2c06..ba2d18cf08f1 100644 --- a/drivers/video/video_mcux_csi.c +++ b/drivers/video/video_mcux_csi.c @@ -18,6 +18,8 @@ #include #endif +#include "video_device.h" + struct video_mcux_csi_config { CSI_Type *base; const struct device *source_dev; @@ -506,4 +508,7 @@ static int video_mcux_csi_init_0(const struct device *dev) DEVICE_DT_INST_DEFINE(0, &video_mcux_csi_init_0, NULL, &video_mcux_csi_data_0, &video_mcux_csi_config_0, POST_KERNEL, CONFIG_VIDEO_MCUX_CSI_INIT_PRIORITY, &video_mcux_csi_driver_api); + +VIDEO_DEVICE_DEFINE(csi, DEVICE_DT_INST_GET(0), video_mcux_csi_config_0.source_dev); + #endif diff --git a/drivers/video/video_mcux_mipi_csi2rx.c b/drivers/video/video_mcux_mipi_csi2rx.c index 4c9dfa6135ee..275b16b2ea9d 100644 --- a/drivers/video/video_mcux_mipi_csi2rx.c +++ b/drivers/video/video_mcux_mipi_csi2rx.c @@ -15,6 +15,8 @@ #include +#include "video_device.h" + LOG_MODULE_REGISTER(video_mipi_csi2rx, CONFIG_VIDEO_LOG_LEVEL); #define MAX_SUPPORTED_PIXEL_RATE MHZ(96) @@ -352,6 +354,9 @@ static int mipi_csi2rx_init(const struct device *dev) \ DEVICE_DT_INST_DEFINE(n, &mipi_csi2rx_init, NULL, &mipi_csi2rx_data_##n, \ &mipi_csi2rx_config_##n, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \ - &mipi_csi2rx_driver_api); + &mipi_csi2rx_driver_api); \ + \ + VIDEO_DEVICE_DEFINE(mipi_csi2rx_##n, DEVICE_DT_INST_GET(n), \ + mipi_csi2rx_config_##n.sensor_dev); DT_INST_FOREACH_STATUS_OKAY(MIPI_CSI2RX_INIT) diff --git a/drivers/video/video_mcux_smartdma.c b/drivers/video/video_mcux_smartdma.c index d6f04ad81d36..9081bcb59674 100644 --- a/drivers/video/video_mcux_smartdma.c +++ b/drivers/video/video_mcux_smartdma.c @@ -18,6 +18,8 @@ #include LOG_MODULE_REGISTER(nxp_video_sdma); +#include "video_device.h" + struct nxp_video_sdma_config { const struct device *dma_dev; const struct device *sensor_dev; @@ -363,24 +365,24 @@ static DEVICE_API(video, nxp_video_sdma_api) = { .flush = nxp_video_sdma_flush }; -#define NXP_VIDEO_SDMA_INIT(inst) \ - PINCTRL_DT_INST_DEFINE(inst); \ - const struct nxp_video_sdma_config sdma_config_##inst = { \ - .dma_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ - .sensor_dev = DEVICE_DT_GET(DT_INST_PHANDLE(inst, sensor)), \ - .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ - .vsync_pin = DT_INST_PROP(inst, vsync_pin), \ - .hsync_pin = DT_INST_PROP(inst, hsync_pin), \ - .pclk_pin = DT_INST_PROP(inst, pclk_pin), \ - }; \ - struct nxp_video_sdma_data sdma_data_##inst = { \ - .config = &sdma_config_##inst, \ - }; \ - \ - DEVICE_DT_INST_DEFINE(inst, nxp_video_sdma_init, NULL, \ - &sdma_data_##inst, &sdma_config_##inst, \ - POST_KERNEL, \ - CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ - &nxp_video_sdma_api); +#define NXP_VIDEO_SDMA_INIT(inst) \ + PINCTRL_DT_INST_DEFINE(inst); \ + const struct nxp_video_sdma_config sdma_config_##inst = { \ + .dma_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ + .sensor_dev = DEVICE_DT_GET(DT_INST_PHANDLE(inst, sensor)), \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ + .vsync_pin = DT_INST_PROP(inst, vsync_pin), \ + .hsync_pin = DT_INST_PROP(inst, hsync_pin), \ + .pclk_pin = DT_INST_PROP(inst, pclk_pin), \ + }; \ + struct nxp_video_sdma_data sdma_data_##inst = { \ + .config = &sdma_config_##inst, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, nxp_video_sdma_init, NULL, &sdma_data_##inst, \ + &sdma_config_##inst, POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &nxp_video_sdma_api); \ + \ + VIDEO_DEVICE_DEFINE(sdma_##inst, DEVICE_DT_INST_GET(inst), sdma_config_##inst.sensor_dev); DT_INST_FOREACH_STATUS_OKAY(NXP_VIDEO_SDMA_INIT) diff --git a/drivers/video/video_stm32_dcmi.c b/drivers/video/video_stm32_dcmi.c index 7369b3a39f57..4f3415ca87f6 100644 --- a/drivers/video/video_stm32_dcmi.c +++ b/drivers/video/video_stm32_dcmi.c @@ -20,6 +20,8 @@ #include +#include "video_device.h" + LOG_MODULE_REGISTER(video_stm32_dcmi, CONFIG_VIDEO_LOG_LEVEL); #if CONFIG_VIDEO_BUFFER_POOL_NUM_MAX < 2 @@ -521,3 +523,5 @@ DEVICE_DT_INST_DEFINE(0, &video_stm32_dcmi_init, &video_stm32_dcmi_config_0, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &video_stm32_dcmi_driver_api); + +VIDEO_DEVICE_DEFINE(dcmi, DEVICE_DT_INST_GET(0), video_stm32_dcmi_config_0.sensor_dev); diff --git a/drivers/video/video_sw_generator.c b/drivers/video/video_sw_generator.c index d9f0d90e8e0e..b912dffa3dae 100644 --- a/drivers/video/video_sw_generator.c +++ b/drivers/video/video_sw_generator.c @@ -11,6 +11,8 @@ #include #include +#include "video_device.h" + LOG_MODULE_REGISTER(video_sw_generator, CONFIG_VIDEO_LOG_LEVEL); #define VIDEO_PATTERN_COLOR_BAR 0 @@ -372,3 +374,5 @@ static int video_sw_generator_init(const struct device *dev) DEVICE_DEFINE(video_sw_generator, "VIDEO_SW_GENERATOR", &video_sw_generator_init, NULL, &video_sw_generator_data_0, NULL, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &video_sw_generator_driver_api); + +VIDEO_DEVICE_DEFINE(video_sw_generator, DEVICE_GET(video_sw_generator), NULL); From c5b5e03311e83fc309bf944dccfee341792cc86e Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Wed, 19 Mar 2025 13:52:46 +0100 Subject: [PATCH 2/8] drivers: video: Implement video control framework Implement the video control framework with the following features: - Drivers initialize the control with a valid value range at boot which guides the application developer and the framework. Hence, the video framework could do all common works for drivers e.g., sanity check. - Controls need to be cached to memory. Video framework handles get_ctrl(), drivers don't need to implement this API. It is because reading control value directly from registers are not only inefficient but also sometimes impossible, e.g. controls that scatter through several registers. Only "volatile" control needs to be updated at runtime. - Only the devices (e.g., sensors) owning the controls need to implement set_ctrl(). Other devices of the pipeline do not need to propagate set_ctrl() and hence, do not need to implement this API Signed-off-by: Phi Bang Nguyen --- drivers/video/CMakeLists.txt | 1 + drivers/video/gc2145.c | 32 +++- drivers/video/mt9m114.c | 33 +++- drivers/video/ov2640.c | 171 +++++++++-------- drivers/video/ov5640.c | 143 +++++++++----- drivers/video/ov7670.c | 32 +++- drivers/video/video_ctrls.c | 175 ++++++++++++++++++ drivers/video/video_ctrls.h | 54 ++++++ drivers/video/video_device.h | 3 + drivers/video/video_emul_imager.c | 48 ++--- drivers/video/video_emul_rx.c | 18 -- drivers/video/video_esp32_dvp.c | 16 -- drivers/video/video_mcux_csi.c | 28 --- drivers/video/video_mcux_mipi_csi2rx.c | 53 ++---- drivers/video/video_stm32_dcmi.c | 24 --- drivers/video/video_sw_generator.c | 36 ++-- include/zephyr/drivers/video-controls.h | 26 +++ include/zephyr/drivers/video.h | 44 +---- samples/drivers/video/capture/src/main.c | 7 +- .../drivers/video/capture_to_lvgl/src/main.c | 20 +- tests/drivers/video/api/src/video_emul.c | 11 +- 21 files changed, 606 insertions(+), 369 deletions(-) create mode 100644 drivers/video/video_ctrls.c create mode 100644 drivers/video/video_ctrls.h diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index c9c617f1a0bb..102a841648e9 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -3,6 +3,7 @@ zephyr_library() zephyr_library_sources(video_common.c) +zephyr_library_sources(video_ctrls.c) zephyr_library_sources(video_device.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_CSI video_mcux_csi.c) diff --git a/drivers/video/gc2145.c b/drivers/video/gc2145.c index 676ba48d61ba..afd16d2acf23 100644 --- a/drivers/video/gc2145.c +++ b/drivers/video/gc2145.c @@ -15,6 +15,7 @@ #include +#include "video_ctrls.h" #include "video_device.h" LOG_MODULE_REGISTER(video_gc2145, CONFIG_VIDEO_LOG_LEVEL); @@ -692,7 +693,13 @@ struct gc2145_config { #endif }; +struct gc2145_ctrls { + struct video_ctrl hflip; + struct video_ctrl vflip; +}; + struct gc2145_data { + struct gc2145_ctrls ctrls; struct video_format fmt; }; @@ -1103,13 +1110,13 @@ static int gc2145_get_caps(const struct device *dev, enum video_endpoint_id ep, return 0; } -static int gc2145_set_ctrl(const struct device *dev, unsigned int cid, void *value) +static int gc2145_set_ctrl(const struct device *dev, struct video_control *ctrl) { - switch (cid) { + switch (ctrl->id) { case VIDEO_CID_HFLIP: - return gc2145_set_ctrl_hmirror(dev, (int)value); + return gc2145_set_ctrl_hmirror(dev, ctrl->val); case VIDEO_CID_VFLIP: - return gc2145_set_ctrl_vflip(dev, (int)value); + return gc2145_set_ctrl_vflip(dev, ctrl->val); default: return -ENOTSUP; } @@ -1123,6 +1130,20 @@ static DEVICE_API(video, gc2145_driver_api) = { .set_ctrl = gc2145_set_ctrl, }; +static int gc2145_init_controls(const struct device *dev) +{ + int ret; + struct gc2145_data *drv_data = dev->data; + struct gc2145_ctrls *ctrls = &drv_data->ctrls; + + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + if (ret) { + return ret; + } + + return video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, 0, 1, 1, 0); +} + static int gc2145_init(const struct device *dev) { struct video_format fmt; @@ -1169,7 +1190,8 @@ static int gc2145_init(const struct device *dev) return ret; } - return 0; + /* Initialize controls */ + return gc2145_init_controls(dev); } /* Unique Instance */ diff --git a/drivers/video/mt9m114.c b/drivers/video/mt9m114.c index e7536a6d9519..a72656ff0969 100644 --- a/drivers/video/mt9m114.c +++ b/drivers/video/mt9m114.c @@ -15,6 +15,7 @@ #include #include +#include "video_ctrls.h" #include "video_device.h" LOG_MODULE_REGISTER(video_mt9m114, CONFIG_VIDEO_LOG_LEVEL); @@ -65,7 +66,13 @@ struct mt9m114_config { struct i2c_dt_spec i2c; }; +struct mt9m114_ctrls { + struct video_ctrl hflip; + struct video_ctrl vflip; +}; + struct mt9m114_data { + struct mt9m114_ctrls ctrls; struct video_format fmt; }; @@ -466,20 +473,20 @@ static int mt9m114_get_caps(const struct device *dev, enum video_endpoint_id ep, return 0; } -static int mt9m114_set_ctrl(const struct device *dev, unsigned int cid, void *value) +static int mt9m114_set_ctrl(const struct device *dev, struct video_control *ctrl) { int ret = 0; - switch (cid) { + switch (ctrl->id) { case VIDEO_CID_HFLIP: ret = mt9m114_modify_reg(dev, MT9M114_CAM_SENSOR_CTRL_READ_MODE, 2, MT9M114_CAM_SENSOR_CTRL_HORZ_FLIP_EN, - (int)value ? MT9M114_CAM_SENSOR_CTRL_HORZ_FLIP_EN : 0); + ctrl->val ? MT9M114_CAM_SENSOR_CTRL_HORZ_FLIP_EN : 0); break; case VIDEO_CID_VFLIP: ret = mt9m114_modify_reg(dev, MT9M114_CAM_SENSOR_CTRL_READ_MODE, 2, MT9M114_CAM_SENSOR_CTRL_VERT_FLIP_EN, - (int)value ? MT9M114_CAM_SENSOR_CTRL_VERT_FLIP_EN : 0); + ctrl->val ? MT9M114_CAM_SENSOR_CTRL_VERT_FLIP_EN : 0); break; default: return -ENOTSUP; @@ -501,6 +508,21 @@ static DEVICE_API(video, mt9m114_driver_api) = { .set_ctrl = mt9m114_set_ctrl, }; +static int mt9m114_init_controls(const struct device *dev) +{ + int ret; + struct mt9m114_data *drv_data = dev->data; + struct mt9m114_ctrls *ctrls = &drv_data->ctrls; + + + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + if (ret) { + return ret; + } + + return video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, 0, 1, 1, 0); +} + static int mt9m114_init(const struct device *dev) { struct video_format fmt; @@ -546,7 +568,8 @@ static int mt9m114_init(const struct device *dev) /* Suspend any stream */ mt9m114_set_state(dev, MT9M114_SYS_STATE_ENTER_SUSPEND); - return 0; + /* Initialize controls */ + return mt9m114_init_controls(dev); } #if 1 /* Unique Instance */ diff --git a/drivers/video/ov2640.c b/drivers/video/ov2640.c index 128979864f56..660efd781bb1 100644 --- a/drivers/video/ov2640.c +++ b/drivers/video/ov2640.c @@ -14,6 +14,7 @@ #include #include +#include "video_ctrls.h" #include "video_device.h" LOG_MODULE_REGISTER(video_ov2640, CONFIG_VIDEO_LOG_LEVEL); @@ -441,7 +442,21 @@ struct ov2640_config { uint8_t clock_rate_control; }; +struct ov2640_ctrls { + struct video_ctrl hflip; + struct video_ctrl vflip; + struct video_ctrl ae; + struct video_ctrl awb; + struct video_ctrl gain; + struct video_ctrl contrast; + struct video_ctrl brightness; + struct video_ctrl saturation; + struct video_ctrl jpeg; + struct video_ctrl test_pattern; +}; + struct ov2640_data { + struct ov2640_ctrls ctrls; struct video_format fmt; }; @@ -565,10 +580,7 @@ static int ov2640_set_level(const struct device *dev, int level, int ret = 0; const struct ov2640_config *cfg = dev->config; - level += (max_level / 2 + 1); - if (level < 0 || level > max_level) { - return -ENOTSUP; - } + level += max_level / 2 + 1; /* Switch to DSP register bank */ ret |= ov2640_write_reg(&cfg->i2c, BANK_SEL, BANK_SEL_DSP); @@ -580,48 +592,6 @@ static int ov2640_set_level(const struct device *dev, int level, return ret; } -static int ov2640_set_brightness(const struct device *dev, int level) -{ - int ret = 0; - - ret = ov2640_set_level(dev, level, NUM_BRIGHTNESS_LEVELS, - ARRAY_SIZE(brightness_regs[0]), brightness_regs); - - if (ret == -ENOTSUP) { - LOG_ERR("Brightness level %d not supported", level); - } - - return ret; -} - -static int ov2640_set_saturation(const struct device *dev, int level) -{ - int ret = 0; - - ret = ov2640_set_level(dev, level, NUM_SATURATION_LEVELS, - ARRAY_SIZE(saturation_regs[0]), saturation_regs); - - if (ret == -ENOTSUP) { - LOG_ERR("Saturation level %d not supported", level); - } - - return ret; -} - -static int ov2640_set_contrast(const struct device *dev, int level) -{ - int ret = 0; - - ret = ov2640_set_level(dev, level, NUM_CONTRAST_LEVELS, - ARRAY_SIZE(contrast_regs[0]), contrast_regs); - - if (ret == -ENOTSUP) { - LOG_ERR("Contrast level %d not supported", level); - } - - return ret; -} - static int ov2640_set_output_format(const struct device *dev, int output_format) { @@ -925,47 +895,35 @@ static int ov2640_get_caps(const struct device *dev, return 0; } -static int ov2640_set_ctrl(const struct device *dev, - unsigned int cid, void *value) +static int ov2640_set_ctrl(const struct device *dev, struct video_control *ctrl) { - int ret = 0; - - switch (cid) { + switch (ctrl->id) { case VIDEO_CID_HFLIP: - ret |= ov2640_set_horizontal_mirror(dev, (int)value); - break; + return ov2640_set_horizontal_mirror(dev, ctrl->val); case VIDEO_CID_VFLIP: - ret |= ov2640_set_vertical_flip(dev, (int)value); - break; + return ov2640_set_vertical_flip(dev, ctrl->val); case VIDEO_CID_EXPOSURE: - ret |= ov2640_set_exposure_ctrl(dev, (int)value); - break; + return ov2640_set_exposure_ctrl(dev, ctrl->val); + case VIDEO_CID_WHITE_BALANCE_TEMPERATURE: + return ov2640_set_white_bal(dev, ctrl->val); case VIDEO_CID_GAIN: - ret |= ov2640_set_gain_ctrl(dev, (int)value); - break; + return ov2640_set_gain_ctrl(dev, ctrl->val); case VIDEO_CID_BRIGHTNESS: - ret |= ov2640_set_brightness(dev, (int)value); - break; - case VIDEO_CID_SATURATION: - ret |= ov2640_set_saturation(dev, (int)value); - break; - case VIDEO_CID_WHITE_BALANCE_TEMPERATURE: - ret |= ov2640_set_white_bal(dev, (int)value); - break; + return ov2640_set_level(dev, ctrl->val, NUM_BRIGHTNESS_LEVELS, + ARRAY_SIZE(brightness_regs[0]), brightness_regs); case VIDEO_CID_CONTRAST: - ret |= ov2640_set_contrast(dev, (int)value); - break; - case VIDEO_CID_TEST_PATTERN: - ret |= ov2640_set_colorbar(dev, (int)value); - break; + return ov2640_set_level(dev, ctrl->val, NUM_CONTRAST_LEVELS, + ARRAY_SIZE(contrast_regs[0]), contrast_regs); + case VIDEO_CID_SATURATION: + return ov2640_set_level(dev, ctrl->val, NUM_SATURATION_LEVELS, + ARRAY_SIZE(saturation_regs[0]), saturation_regs); case VIDEO_CID_JPEG_COMPRESSION_QUALITY: - ret |= ov2640_set_quality(dev, (int)value); - break; + return ov2640_set_quality(dev, ctrl->val); + case VIDEO_CID_TEST_PATTERN: + return ov2640_set_colorbar(dev, ctrl->val); default: return -ENOTSUP; } - - return ret; } static DEVICE_API(video, ov2640_driver_api) = { @@ -976,6 +934,60 @@ static DEVICE_API(video, ov2640_driver_api) = { .set_ctrl = ov2640_set_ctrl, }; +static int ov2640_init_controls(const struct device *dev) +{ + int ret; + struct ov2640_data *drv_data = dev->data; + struct ov2640_ctrls *ctrls = &drv_data->ctrls; + + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, 0, 1, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->ae, dev, VIDEO_CID_EXPOSURE, 0, 1, 1, 1); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->awb, dev, VIDEO_CID_WHITE_BALANCE_TEMPERATURE, 0, 1, 1, 1); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->gain, dev, VIDEO_CID_GAIN, 0, 1, 1, 1); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->brightness, dev, VIDEO_CID_BRIGHTNESS, -2, 2, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->contrast, dev, VIDEO_CID_CONTRAST, -2, 2, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->saturation, dev, VIDEO_CID_SATURATION, -2, 2, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->jpeg, dev, VIDEO_CID_JPEG_COMPRESSION_QUALITY, 5, 100, 1, 50); + if (ret) { + return ret; + } + + return video_init_ctrl(&ctrls->test_pattern, dev, VIDEO_CID_TEST_PATTERN, 0, 1, 1, 0); +} + static int ov2640_init(const struct device *dev) { struct video_format fmt; @@ -1020,7 +1032,12 @@ static int ov2640_init(const struct device *dev) ret |= ov2640_set_exposure_ctrl(dev, 1); ret |= ov2640_set_white_bal(dev, 1); - return ret; + if (ret) { + return ret; + } + + /* Initialize controls */ + return ov2640_init_controls(dev); } /* Unique Instance */ diff --git a/drivers/video/ov5640.c b/drivers/video/ov5640.c index 334c5e364549..79be179afe9b 100644 --- a/drivers/video/ov5640.c +++ b/drivers/video/ov5640.c @@ -19,6 +19,7 @@ #include #include +#include "video_ctrls.h" #include "video_device.h" LOG_MODULE_REGISTER(video_ov5640, CONFIG_VIDEO_LOG_LEVEL); @@ -143,7 +144,21 @@ struct ov5640_mode_config { uint16_t def_frmrate; }; +struct ov5640_ctrls { + struct video_ctrl gain; + struct video_ctrl brightness; + struct video_ctrl contrast; + struct video_ctrl hue; + struct video_ctrl saturation; + struct video_ctrl hflip; + struct video_ctrl vflip; + struct video_ctrl light_freq; + struct video_ctrl test_pattern; + struct video_ctrl pixel_rate; +}; + struct ov5640_data { + struct ov5640_ctrls ctrls; struct video_format fmt; uint64_t cur_pixrate; uint16_t cur_frmrate; @@ -795,6 +810,9 @@ static int ov5640_set_frmival(const struct device *dev, enum video_endpoint_id e drv_data->cur_frmrate = best_match; drv_data->cur_pixrate = drv_data->cur_mode->mipi_frmrate_config[ind].pixelrate; + /* Update pixerate control */ + drv_data->ctrls.pixel_rate.val = drv_data->cur_pixrate; + frmival->numerator = 1; frmival->denominator = best_match; @@ -938,10 +956,6 @@ static int ov5640_set_ctrl_test_pattern(const struct device *dev, int value) { const struct ov5640_config *cfg = dev->config; - if (!IN_RANGE(value, 0, ARRAY_SIZE(test_pattern_val) - 1)) { - return -EINVAL; - } - return ov5640_write_reg(&cfg->i2c, PRE_ISP_TEST_SET1, test_pattern_val[value]); } @@ -950,10 +964,6 @@ static int ov5640_set_ctrl_hue(const struct device *dev, int value) const struct ov5640_config *cfg = dev->config; int cos_coef, sin_coef, sign = 0; - if (!IN_RANGE(value, 0, 360)) { - return -EINVAL; - } - double rad_val = value; int ret = ov5640_modify_reg(&cfg->i2c, SDE_CTRL0_REG, BIT(0), BIT(0)); @@ -986,10 +996,6 @@ static int ov5640_set_ctrl_saturation(const struct device *dev, int value) { const struct ov5640_config *cfg = dev->config; - if (!IN_RANGE(value, 0, UINT8_MAX)) { - return -EINVAL; - } - struct ov5640_reg saturation_params[] = {{SDE_CTRL3_REG, value}, {SDE_CTRL4_REG, value}}; int ret = ov5640_modify_reg(&cfg->i2c, SDE_CTRL8_REG, BIT(6) | BIT(0), BIT(6) | BIT(0)); @@ -1004,10 +1010,6 @@ static int ov5640_set_ctrl_brightness(const struct device *dev, int value) { const struct ov5640_config *cfg = dev->config; - if (!IN_RANGE(value, -UINT8_MAX, UINT8_MAX)) { - return -EINVAL; - } - struct ov5640_reg brightness_params[] = {{SDE_CTRL8_REG, value >= 0 ? 0x01 : 0x09}, {SDE_CTRL7_REG, abs(value) & 0xff}}; int ret = ov5640_modify_reg(&cfg->i2c, SDE_CTRL0_REG, BIT(2), BIT(2)); @@ -1023,10 +1025,6 @@ static int ov5640_set_ctrl_contrast(const struct device *dev, int value) { const struct ov5640_config *cfg = dev->config; - if (!IN_RANGE(value, 0, UINT8_MAX)) { - return -EINVAL; - } - int ret = ov5640_modify_reg(&cfg->i2c, SDE_CTRL0_REG, BIT(2), BIT(2)); if (ret) { @@ -1040,10 +1038,6 @@ static int ov5640_set_ctrl_gain(const struct device *dev, int value) { const struct ov5640_config *cfg = dev->config; - if (!IN_RANGE(value, 0, UINT16_MAX)) { - return -EINVAL; - } - if (value) { int ret = ov5640_modify_reg(&cfg->i2c, AEC_PK_MANUAL, BIT(1), BIT(0)); @@ -1102,41 +1096,27 @@ static int ov5640_set_ctrl_power_line_freq(const struct device *dev, int value) return ov5640_modify_reg(&cfg->i2c, HZ5060_CTRL01_REG, BIT(7), BIT(7)); } -static int ov5640_set_ctrl(const struct device *dev, unsigned int cid, void *value) +static int ov5640_set_ctrl(const struct device *dev, struct video_control *ctrl) { - switch (cid) { + switch (ctrl->id) { case VIDEO_CID_TEST_PATTERN: - return ov5640_set_ctrl_test_pattern(dev, (int)value); + return ov5640_set_ctrl_test_pattern(dev, ctrl->val); case VIDEO_CID_HUE: - return ov5640_set_ctrl_hue(dev, (int)value); + return ov5640_set_ctrl_hue(dev, ctrl->val); case VIDEO_CID_SATURATION: - return ov5640_set_ctrl_saturation(dev, (int)(value)); + return ov5640_set_ctrl_saturation(dev, ctrl->val); case VIDEO_CID_BRIGHTNESS: - return ov5640_set_ctrl_brightness(dev, (int)(value)); + return ov5640_set_ctrl_brightness(dev, ctrl->val); case VIDEO_CID_CONTRAST: - return ov5640_set_ctrl_contrast(dev, (int)value); + return ov5640_set_ctrl_contrast(dev, ctrl->val); case VIDEO_CID_GAIN: - return ov5640_set_ctrl_gain(dev, (int)(value)); + return ov5640_set_ctrl_gain(dev, ctrl->val); case VIDEO_CID_HFLIP: - return ov5640_set_ctrl_hflip(dev, (int)(value)); + return ov5640_set_ctrl_hflip(dev, ctrl->val); case VIDEO_CID_VFLIP: - return ov5640_set_ctrl_vflip(dev, (int)(value)); + return ov5640_set_ctrl_vflip(dev, ctrl->val); case VIDEO_CID_POWER_LINE_FREQUENCY: - return ov5640_set_ctrl_power_line_freq(dev, (int)(value)); - default: - return -ENOTSUP; - } -} - -static inline int ov5640_get_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - struct ov5640_data *drv_data = dev->data; - - switch (cid) { - case VIDEO_CID_PIXEL_RATE: - *((uint64_t *)value) = drv_data->cur_pixrate; - - return 0; + return ov5640_set_ctrl_power_line_freq(dev, ctrl->val); default: return -ENOTSUP; } @@ -1191,12 +1171,72 @@ static DEVICE_API(video, ov5640_driver_api) = { .get_caps = ov5640_get_caps, .set_stream = ov5640_set_stream, .set_ctrl = ov5640_set_ctrl, - .get_ctrl = ov5640_get_ctrl, .set_frmival = ov5640_set_frmival, .get_frmival = ov5640_get_frmival, .enum_frmival = ov5640_enum_frmival, }; +static int ov5640_init_controls(const struct device *dev) +{ + int ret; + struct ov5640_data *drv_data = dev->data; + struct ov5640_ctrls *ctrls = &drv_data->ctrls; + + ret = video_init_ctrl(&ctrls->gain, dev, VIDEO_CID_GAIN, 0, 1023, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->brightness, dev, VIDEO_CID_BRIGHTNESS, -15, 15, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->contrast, dev, VIDEO_CID_CONTRAST, 0, 255, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->hue, dev, VIDEO_CID_HUE, 0, 359, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->saturation, dev, VIDEO_CID_SATURATION, 0, 255, 1, 64); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, 0, 1, 1, 0); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->light_freq, dev, VIDEO_CID_POWER_LINE_FREQUENCY, + VIDEO_CID_POWER_LINE_FREQUENCY_DISABLED, + VIDEO_CID_POWER_LINE_FREQUENCY_AUTO, 1, + VIDEO_CID_POWER_LINE_FREQUENCY_50HZ); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->test_pattern, dev, VIDEO_CID_TEST_PATTERN, 0, + ARRAY_SIZE(test_pattern_val) - 1, 1, 0); + if (ret) { + return ret; + } + + return video_init_ctrl( + &ctrls->pixel_rate, dev, VIDEO_CID_PIXEL_RATE, mipi_vga_frmrate_params[0].pixelrate, + mipi_hd_frmrate_params[ARRAY_SIZE(mipi_hd_frmrate_params) - 1].pixelrate, 1, + drv_data->cur_pixrate); +} + static int ov5640_init(const struct device *dev) { const struct ov5640_config *cfg = dev->config; @@ -1320,7 +1360,8 @@ static int ov5640_init(const struct device *dev) return -EIO; } - return 0; + /* Initialize controls */ + return ov5640_init_controls(dev); } #define OV5640_INIT(n) \ diff --git a/drivers/video/ov7670.c b/drivers/video/ov7670.c index 0e37cdda3e47..dd2765150099 100644 --- a/drivers/video/ov7670.c +++ b/drivers/video/ov7670.c @@ -12,6 +12,7 @@ #include #include +#include "video_ctrls.h" #include "video_device.h" LOG_MODULE_REGISTER(video_ov7670, CONFIG_VIDEO_LOG_LEVEL); @@ -32,7 +33,13 @@ struct ov7670_config { #endif }; +struct ov7670_ctrls { + struct video_ctrl hflip; + struct video_ctrl vflip; +}; + struct ov7670_data { + struct ov7670_ctrls ctrls; struct video_format fmt; }; @@ -451,6 +458,20 @@ static int ov7670_get_fmt(const struct device *dev, enum video_endpoint_id ep, return 0; } +static int ov7670_init_controls(const struct device *dev) +{ + int ret; + struct ov7670_data *drv_data = dev->data; + struct ov7670_ctrls *ctrls = &drv_data->ctrls; + + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + if (ret) { + return ret; + } + + return video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, 0, 1, 1, 0); +} + static int ov7670_init(const struct device *dev) { const struct ov7670_config *config = dev->config; @@ -549,7 +570,8 @@ static int ov7670_init(const struct device *dev) } } - return 0; + /* Initialize controls */ + return ov7670_init_controls(dev); } static int ov7670_set_stream(const struct device *dev, bool enable) @@ -557,17 +579,17 @@ static int ov7670_set_stream(const struct device *dev, bool enable) return 0; } -static int ov7670_set_ctrl(const struct device *dev, unsigned int cid, void *value) +static int ov7670_set_ctrl(const struct device *dev, struct video_control *ctrl) { const struct ov7670_config *config = dev->config; - switch (cid) { + switch (ctrl->id) { case VIDEO_CID_HFLIP: return i2c_reg_update_byte_dt(&config->bus, OV7670_MVFP, - OV7670_MVFP_HFLIP, ((int)value) ? OV7670_MVFP_HFLIP : 0); + OV7670_MVFP_HFLIP, ctrl->val ? OV7670_MVFP_HFLIP : 0); case VIDEO_CID_VFLIP: return i2c_reg_update_byte_dt(&config->bus, OV7670_MVFP, - OV7670_MVFP_VFLIP, ((int)value) ? OV7670_MVFP_VFLIP : 0); + OV7670_MVFP_VFLIP, ctrl->val ? OV7670_MVFP_VFLIP : 0); default: return -ENOTSUP; } diff --git a/drivers/video/video_ctrls.c b/drivers/video/video_ctrls.c new file mode 100644 index 000000000000..c77564b01b94 --- /dev/null +++ b/drivers/video/video_ctrls.c @@ -0,0 +1,175 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "video_ctrls.h" +#include "video_device.h" + +LOG_MODULE_REGISTER(video_ctrls, CONFIG_VIDEO_LOG_LEVEL); + +static inline int check_range(enum video_ctrl_type type, int32_t min, int32_t max, uint32_t step, + int32_t def) +{ + switch (type) { + case VIDEO_CTRL_TYPE_BOOLEAN: + if (step != 1 || max > 1 || min < 0) { + return -ERANGE; + } + return 0; + case VIDEO_CTRL_TYPE_INTEGER: + case VIDEO_CTRL_TYPE_INTEGER64: + if (step == 0 || min > max || !IN_RANGE(def, min, max)) { + return -ERANGE; + } + return 0; + case VIDEO_CTRL_TYPE_MENU: + if (!IN_RANGE(min, 0, max) || !IN_RANGE(def, min, 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_HFLIP: + case VIDEO_CID_VFLIP: + *type = VIDEO_CTRL_TYPE_BOOLEAN; + break; + case VIDEO_CID_POWER_LINE_FREQUENCY: + case VIDEO_CID_TEST_PATTERN: + *type = VIDEO_CTRL_TYPE_MENU; + break; + case VIDEO_CID_PIXEL_RATE: + *type = VIDEO_CTRL_TYPE_INTEGER64; + *flags |= VIDEO_CTRL_FLAG_READ_ONLY; + break; + default: + *type = VIDEO_CTRL_TYPE_INTEGER; + break; + } +} + +int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id, int32_t min, + int32_t max, uint32_t step, int32_t def) +{ + int ret; + uint32_t flags; + enum video_ctrl_type type; + struct video_device *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, min, max, step, def); + if (ret) { + return ret; + } + + ctrl->vdev = vdev; + ctrl->id = id; + ctrl->type = type; + ctrl->flags = flags; + ctrl->min = min; + ctrl->max = max; + ctrl->step = step; + ctrl->def = def; + ctrl->val = def; + + sys_dlist_append(&vdev->ctrls, &ctrl->node); + + return 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; + + 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; + } + + 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 = 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 (!IN_RANGE(control->val, ctrl->min, ctrl->max)) { + LOG_ERR("Control value is invalid\n"); + return -EINVAL; + } + + if (DEVICE_API_GET(video, ctrl->vdev->dev)->set_ctrl == NULL) { + goto update; + } + + /* Call driver's set_ctrl */ + ret = DEVICE_API_GET(video, ctrl->vdev->dev)->set_ctrl(ctrl->vdev->dev, control); + if (ret) { + return ret; + } + +update: + /* Only update the ctrl in memory once everything is OK */ + ctrl->val = control->val; + + return 0; +} diff --git a/drivers/video/video_ctrls.h b/drivers/video/video_ctrls.h new file mode 100644 index 000000000000..95c604148fcb --- /dev/null +++ b/drivers/video/video_ctrls.h @@ -0,0 +1,54 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DRIVERS_VIDEO_VIDEO_CTRLS_H_ +#define ZEPHYR_INCLUDE_DRIVERS_VIDEO_VIDEO_CTRLS_H_ + +#include +#include + +/** Control is read-only */ +#define VIDEO_CTRL_FLAG_READ_ONLY BIT(0) +/** Control is write-only */ +#define VIDEO_CTRL_FLAG_WRITE_ONLY BIT(1) +/** Control that needs a freshly read as constanly updated by HW */ +#define VIDEO_CTRL_FLAG_VOLATILE BIT(2) +/** Control is inactive, e.g. manual controls of an autocluster in automatic mode */ +#define VIDEO_CTRL_FLAG_INACTIVE BIT(3) +/** Control that affects other controls, e.g. the master control of a cluster */ +#define VIDEO_CTRL_FLAG_UPDATE BIT(4) + +enum video_ctrl_type { + /** Boolean type */ + VIDEO_CTRL_TYPE_BOOLEAN = 1, + /** Integer type */ + VIDEO_CTRL_TYPE_INTEGER = 2, + /** 64-bit integer type */ + VIDEO_CTRL_TYPE_INTEGER64 = 3, + /** Menu type, standard or driver-defined menu */ + VIDEO_CTRL_TYPE_MENU = 4, + /** String type */ + VIDEO_CTRL_TYPE_STRING = 5, +}; + +struct video_device; + +/** + * @see video_control for the struct used in public API + */ +struct video_ctrl { + const struct video_device *vdev; + uint32_t id; + enum video_ctrl_type type; + unsigned long flags; + int32_t min, max, def, val; + uint32_t step; + sys_dnode_t node; +}; + +int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id, int32_t min, + int32_t max, uint32_t step, int32_t def); + +#endif /* ZEPHYR_INCLUDE_DRIVERS_VIDEO_VIDEO_CTRLS_H_ */ diff --git a/drivers/video/video_device.h b/drivers/video/video_device.h index 130a35b8733d..3ba2ee3486cd 100644 --- a/drivers/video/video_device.h +++ b/drivers/video/video_device.h @@ -8,16 +8,19 @@ #define ZEPHYR_INCLUDE_DRIVERS_VIDEO_VIDEO_DEVICE_H_ #include +#include struct video_device { const struct device *dev; const struct device *src_dev; + sys_dlist_t ctrls; }; #define VIDEO_DEVICE_DEFINE(name, device, source) \ static STRUCT_SECTION_ITERABLE(video_device, name) = { \ .dev = device, \ .src_dev = source, \ + .ctrls = SYS_DLIST_STATIC_INIT(&name.ctrls), \ } struct video_device *video_find_vdev(const struct device *dev); diff --git a/drivers/video/video_emul_imager.c b/drivers/video/video_emul_imager.c index 90cec728ae0e..0aedb5f07c4a 100644 --- a/drivers/video/video_emul_imager.c +++ b/drivers/video/video_emul_imager.c @@ -17,6 +17,7 @@ #include #include +#include "video_ctrls.h" #include "video_device.h" LOG_MODULE_REGISTER(video_emul_imager, CONFIG_VIDEO_LOG_LEVEL); @@ -65,6 +66,10 @@ struct emul_imager_config { struct i2c_dt_spec i2c; }; +struct emul_imager_ctrls { + struct video_ctrl custom; +}; + struct emul_imager_data { /* First field is a line buffer for I/O emulation purpose */ uint8_t framebuffer[320 * sizeof(uint16_t)]; @@ -72,6 +77,7 @@ struct emul_imager_data { const struct emul_imager_mode *mode; enum emul_imager_fmt_id fmt_id; struct video_format fmt; + struct emul_imager_ctrls ctrls; }; /* All the I2C registers sent on various scenario */ @@ -165,17 +171,6 @@ static int emul_imager_read_reg(const struct device *const dev, uint8_t reg_addr return 0; } -/* Helper to read a full integer directly from a register */ -static int emul_imager_read_int(const struct device *const dev, uint8_t reg_addr, int *value) -{ - uint8_t val8; - int ret; - - ret = emul_imager_read_reg(dev, reg_addr, &val8); - *value = val8; - return ret; -} - /* Some sensors will need reg8 or reg16 variants. */ static int emul_imager_write_reg(const struct device *const dev, uint8_t reg_addr, uint8_t value) { @@ -198,24 +193,9 @@ static int emul_imager_write_multi(const struct device *const dev, return 0; } -static int emul_imager_set_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - switch (cid) { - case EMUL_IMAGER_CID_CUSTOM: - return emul_imager_write_reg(dev, EMUL_IMAGER_REG_CUSTOM, (int)value); - default: - return -ENOTSUP; - } -} - -static int emul_imager_get_ctrl(const struct device *dev, unsigned int cid, void *value) +static int emul_imager_set_ctrl(const struct device *dev, struct video_control *ctrl) { - switch (cid) { - case EMUL_IMAGER_CID_CUSTOM: - return emul_imager_read_int(dev, EMUL_IMAGER_REG_CUSTOM, value); - default: - return -ENOTSUP; - } + return emul_imager_write_reg(dev, EMUL_IMAGER_REG_CUSTOM, ctrl->val); } /* Customize this function according to your "struct emul_imager_mode". */ @@ -373,7 +353,6 @@ static int emul_imager_set_stream(const struct device *dev, bool enable) static DEVICE_API(video, emul_imager_driver_api) = { .set_ctrl = emul_imager_set_ctrl, - .get_ctrl = emul_imager_get_ctrl, .set_frmival = emul_imager_set_frmival, .get_frmival = emul_imager_get_frmival, .enum_frmival = emul_imager_enum_frmival, @@ -383,6 +362,14 @@ static DEVICE_API(video, emul_imager_driver_api) = { .set_stream = emul_imager_set_stream, }; +static int emul_imager_init_controls(const struct device *dev) +{ + struct emul_imager_data *drv_data = dev->data; + + return video_init_ctrl(&drv_data->ctrls.custom, dev, EMUL_IMAGER_CID_CUSTOM, 0, 255, 1, + 128); +} + int emul_imager_init(const struct device *dev) { struct video_format fmt; @@ -417,7 +404,8 @@ int emul_imager_init(const struct device *dev) fmt.pixelformat, fmt.width, fmt.height); } - return 0; + /* Initialize controls */ + return emul_imager_init_controls(dev); } #define EMUL_IMAGER_DEFINE(inst) \ diff --git a/drivers/video/video_emul_rx.c b/drivers/video/video_emul_rx.c index 31d8c84d4505..ba44a2c59ed6 100644 --- a/drivers/video/video_emul_rx.c +++ b/drivers/video/video_emul_rx.c @@ -31,22 +31,6 @@ struct emul_rx_data { struct k_fifo fifo_out; }; -static int emul_rx_set_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct emul_rx_config *cfg = dev->config; - - /* Forward all controls to the source */ - return video_set_ctrl(cfg->source_dev, cid, value); -} - -static int emul_rx_get_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct emul_rx_config *cfg = dev->config; - - /* Forward all controls to the source */ - return video_get_ctrl(cfg->source_dev, cid, value); -} - static int emul_rx_set_frmival(const struct device *dev, enum video_endpoint_id ep, struct video_frmival *frmival) { @@ -248,8 +232,6 @@ static int emul_rx_flush(const struct device *dev, enum video_endpoint_id ep, bo } static DEVICE_API(video, emul_rx_driver_api) = { - .set_ctrl = emul_rx_set_ctrl, - .get_ctrl = emul_rx_get_ctrl, .set_frmival = emul_rx_set_frmival, .get_frmival = emul_rx_get_frmival, .enum_frmival = emul_rx_enum_frmival, diff --git a/drivers/video/video_esp32_dvp.c b/drivers/video/video_esp32_dvp.c index 94cd8266fe63..5d1ec69f2421 100644 --- a/drivers/video/video_esp32_dvp.c +++ b/drivers/video/video_esp32_dvp.c @@ -328,20 +328,6 @@ static int video_esp32_dequeue(const struct device *dev, enum video_endpoint_id return 0; } -static int video_esp32_set_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct video_esp32_config *cfg = dev->config; - - return video_set_ctrl(cfg->source_dev, cid, value); -} - -static int video_esp32_get_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct video_esp32_config *cfg = dev->config; - - return video_get_ctrl(cfg->source_dev, cid, value); -} - static int video_esp32_flush(const struct device *dev, enum video_endpoint_id ep, bool cancel) { struct video_esp32_data *data = dev->data; @@ -435,8 +421,6 @@ static DEVICE_API(video, esp32_driver_api) = { .enqueue = video_esp32_enqueue, .dequeue = video_esp32_dequeue, .flush = video_esp32_flush, - .set_ctrl = video_esp32_set_ctrl, - .get_ctrl = video_esp32_get_ctrl, #ifdef CONFIG_POLL .set_signal = video_esp32_set_signal, #endif diff --git a/drivers/video/video_mcux_csi.c b/drivers/video/video_mcux_csi.c index ba2d18cf08f1..4b4a46dc2cbd 100644 --- a/drivers/video/video_mcux_csi.c +++ b/drivers/video/video_mcux_csi.c @@ -300,32 +300,6 @@ static int video_mcux_csi_dequeue(const struct device *dev, enum video_endpoint_ return 0; } -static inline int video_mcux_csi_set_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct video_mcux_csi_config *config = dev->config; - int ret = -ENOTSUP; - - /* Forward to source dev if any */ - if (config->source_dev) { - ret = video_set_ctrl(config->source_dev, cid, value); - } - - return ret; -} - -static inline int video_mcux_csi_get_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct video_mcux_csi_config *config = dev->config; - int ret = -ENOTSUP; - - /* Forward to source dev if any */ - if (config->source_dev) { - ret = video_get_ctrl(config->source_dev, cid, value); - } - - return ret; -} - static int video_mcux_csi_get_caps(const struct device *dev, enum video_endpoint_id ep, struct video_caps *caps) { @@ -470,8 +444,6 @@ static DEVICE_API(video, video_mcux_csi_driver_api) = { .flush = video_mcux_csi_flush, .enqueue = video_mcux_csi_enqueue, .dequeue = video_mcux_csi_dequeue, - .set_ctrl = video_mcux_csi_set_ctrl, - .get_ctrl = video_mcux_csi_get_ctrl, .get_caps = video_mcux_csi_get_caps, .set_frmival = video_mcux_csi_set_frmival, .get_frmival = video_mcux_csi_get_frmival, diff --git a/drivers/video/video_mcux_mipi_csi2rx.c b/drivers/video/video_mcux_mipi_csi2rx.c index 275b16b2ea9d..9ce88023aa72 100644 --- a/drivers/video/video_mcux_mipi_csi2rx.c +++ b/drivers/video/video_mcux_mipi_csi2rx.c @@ -53,10 +53,10 @@ static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endp const struct mipi_csi2rx_config *config = dev->config; struct mipi_csi2rx_data *drv_data = dev->data; uint8_t bpp; - uint64_t sensor_pixel_rate; uint32_t root_clk_rate, ui_clk_rate, sensor_byte_clk, best_match; int ret, ind = 0; struct video_format fmt; + struct video_control sensor_rate = {VIDEO_CID_PIXEL_RATE, -1}; ret = video_get_format(config->sensor_dev, ep, &fmt); if (ret) { @@ -64,19 +64,14 @@ static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endp return ret; } - ret = video_get_ctrl(config->sensor_dev, VIDEO_CID_PIXEL_RATE, &sensor_pixel_rate); - if (ret) { - LOG_ERR("Can not get sensor_dev pixel rate"); - return ret; - } - - if (sensor_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { + video_get_ctrl(config->sensor_dev, &sensor_rate); + if (!IN_RANGE(sensor_rate.val, 0, MAX_SUPPORTED_PIXEL_RATE)) { LOG_ERR("Sensor pixel rate is not supported"); return -ENOTSUP; } bpp = video_bits_per_pixel(fmt.pixelformat); - sensor_byte_clk = sensor_pixel_rate * bpp / drv_data->csi2rxConfig.laneNum / BITS_PER_BYTE; + sensor_byte_clk = sensor_rate.val * bpp / drv_data->csi2rxConfig.laneNum / BITS_PER_BYTE; ret = clock_control_get_rate(drv_data->clock_dev, drv_data->clock_root, &root_clk_rate); if (ret) { @@ -96,10 +91,10 @@ static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endp return ret; } - if (sensor_pixel_rate > ui_clk_rate) { + if (sensor_rate.val > ui_clk_rate) { ret = clock_control_set_rate( drv_data->clock_dev, drv_data->clock_ui, - (clock_control_subsys_rate_t)(uint32_t)sensor_pixel_rate); + (clock_control_subsys_rate_t)(uint32_t)sensor_rate.val); if (ret) { return ret; } @@ -108,7 +103,7 @@ static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endp /* Find the supported sensor_pixel_rate closest to the desired one */ best_match = tHsSettleEscClk_configs[ind].pixel_rate; for (uint8_t i = 0; i < ARRAY_SIZE(tHsSettleEscClk_configs); i++) { - if (ABS(tHsSettleEscClk_configs[i].pixel_rate, sensor_pixel_rate) < + if (ABS(tHsSettleEscClk_configs[i].pixel_rate, sensor_rate.val) < ABS(tHsSettleEscClk_configs[i].pixel_rate, best_match)) { best_match = tHsSettleEscClk_configs[i].pixel_rate; ind = i; @@ -182,17 +177,6 @@ static int mipi_csi2rx_get_caps(const struct device *dev, enum video_endpoint_id return video_get_caps(config->sensor_dev, ep, caps); } -static inline int mipi_csi2rx_set_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct mipi_csi2rx_config *config = dev->config; - - if (config->sensor_dev) { - return video_set_ctrl(config->sensor_dev, cid, value); - } - - return -ENOTSUP; -} - static int mipi_csi2rx_set_frmival(const struct device *dev, enum video_endpoint_id ep, struct video_frmival *frmival) { @@ -241,21 +225,16 @@ static int mipi_csi2rx_enum_frmival(const struct device *dev, enum video_endpoin const struct mipi_csi2rx_config *config = dev->config; struct mipi_csi2rx_data *drv_data = dev->data; int ret; - uint64_t cur_pixel_rate, est_pixel_rate; + uint64_t est_pixel_rate; struct video_frmival cur_frmival; struct video_format cur_fmt; + struct video_control sensor_rate = {VIDEO_CID_PIXEL_RATE, -1}; ret = video_enum_frmival(config->sensor_dev, ep, fie); if (ret) { return ret; } - ret = video_get_ctrl(config->sensor_dev, VIDEO_CID_PIXEL_RATE, &cur_pixel_rate); - if (ret) { - LOG_ERR("Cannot get sensor_dev pixel rate"); - return ret; - } - ret = video_get_frmival(config->sensor_dev, ep, &cur_frmival); if (ret) { LOG_ERR("Cannot get sensor_dev frame rate"); @@ -268,9 +247,14 @@ static int mipi_csi2rx_enum_frmival(const struct device *dev, enum video_endpoin return ret; } + ret = video_get_ctrl(config->sensor_dev, &sensor_rate); + if (ret) { + return ret; + } + if (fie->type == VIDEO_FRMIVAL_TYPE_DISCRETE) { est_pixel_rate = mipi_csi2rx_estimate_pixel_rate( - &cur_frmival, &fie->discrete, &cur_fmt, fie->format, cur_pixel_rate, + &cur_frmival, &fie->discrete, &cur_fmt, fie->format, sensor_rate.val, drv_data->csi2rxConfig.laneNum); if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { return -EINVAL; @@ -279,7 +263,7 @@ static int mipi_csi2rx_enum_frmival(const struct device *dev, enum video_endpoin } else { /* Check the lane rate of the lower bound framerate */ est_pixel_rate = mipi_csi2rx_estimate_pixel_rate( - &cur_frmival, &fie->stepwise.min, &cur_fmt, fie->format, cur_pixel_rate, + &cur_frmival, &fie->stepwise.min, &cur_fmt, fie->format, sensor_rate.val, drv_data->csi2rxConfig.laneNum); if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { return -EINVAL; @@ -287,13 +271,13 @@ static int mipi_csi2rx_enum_frmival(const struct device *dev, enum video_endpoin /* Check the lane rate of the upper bound framerate */ est_pixel_rate = mipi_csi2rx_estimate_pixel_rate( - &cur_frmival, &fie->stepwise.max, &cur_fmt, fie->format, cur_pixel_rate, + &cur_frmival, &fie->stepwise.max, &cur_fmt, fie->format, sensor_rate.val, drv_data->csi2rxConfig.laneNum); if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { fie->stepwise.max.denominator = (mipi_csi2rx_cal_frame_size(&cur_fmt) * MAX_SUPPORTED_PIXEL_RATE * cur_frmival.denominator) / - (mipi_csi2rx_cal_frame_size(fie->format) * cur_pixel_rate * + (mipi_csi2rx_cal_frame_size(fie->format) * sensor_rate.val * cur_frmival.numerator); fie->stepwise.max.numerator = 1; } @@ -307,7 +291,6 @@ static DEVICE_API(video, mipi_csi2rx_driver_api) = { .get_format = mipi_csi2rx_get_fmt, .set_format = mipi_csi2rx_set_fmt, .set_stream = mipi_csi2rx_set_stream, - .set_ctrl = mipi_csi2rx_set_ctrl, .set_frmival = mipi_csi2rx_set_frmival, .get_frmival = mipi_csi2rx_get_frmival, .enum_frmival = mipi_csi2rx_enum_frmival, diff --git a/drivers/video/video_stm32_dcmi.c b/drivers/video/video_stm32_dcmi.c index 4f3415ca87f6..35c55449a558 100644 --- a/drivers/video/video_stm32_dcmi.c +++ b/drivers/video/video_stm32_dcmi.c @@ -354,28 +354,6 @@ static int video_stm32_dcmi_get_caps(const struct device *dev, return ret; } -static inline int video_stm32_dcmi_set_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct video_stm32_dcmi_config *config = dev->config; - int ret; - - /* Forward to source dev if any */ - ret = video_set_ctrl(config->sensor_dev, cid, value); - - return ret; -} - -static inline int video_stm32_dcmi_get_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct video_stm32_dcmi_config *config = dev->config; - int ret; - - /* Forward to source dev if any */ - ret = video_get_ctrl(config->sensor_dev, cid, value); - - return ret; -} - static DEVICE_API(video, video_stm32_dcmi_driver_api) = { .set_format = video_stm32_dcmi_set_fmt, .get_format = video_stm32_dcmi_get_fmt, @@ -383,8 +361,6 @@ static DEVICE_API(video, video_stm32_dcmi_driver_api) = { .enqueue = video_stm32_dcmi_enqueue, .dequeue = video_stm32_dcmi_dequeue, .get_caps = video_stm32_dcmi_get_caps, - .set_ctrl = video_stm32_dcmi_set_ctrl, - .get_ctrl = video_stm32_dcmi_get_ctrl, }; static void video_stm32_dcmi_irq_config_func(const struct device *dev) diff --git a/drivers/video/video_sw_generator.c b/drivers/video/video_sw_generator.c index b912dffa3dae..81466e373c03 100644 --- a/drivers/video/video_sw_generator.c +++ b/drivers/video/video_sw_generator.c @@ -11,6 +11,7 @@ #include #include +#include "video_ctrls.h" #include "video_device.h" LOG_MODULE_REGISTER(video_sw_generator, CONFIG_VIDEO_LOG_LEVEL); @@ -26,16 +27,19 @@ LOG_MODULE_REGISTER(video_sw_generator, CONFIG_VIDEO_LOG_LEVEL); */ #define MAX_FRAME_RATE 60 +struct sw_ctrls { + struct video_ctrl hflip; +}; + struct video_sw_generator_data { const struct device *dev; + struct sw_ctrls ctrls; struct video_format fmt; struct k_fifo fifo_in; struct k_fifo fifo_out; struct k_work_delayable buf_work; struct k_work_sync work_sync; int pattern; - bool ctrl_hflip; - bool ctrl_vflip; struct k_poll_signal *signal; uint32_t frame_rate; }; @@ -127,7 +131,7 @@ static void __fill_buffer_colorbar(struct video_sw_generator_data *data, struct for (h = 0; h < data->fmt.height; h++) { for (w = 0; w < data->fmt.width; w++) { - int color_idx = data->ctrl_vflip ? 7 - w / bw : w / bw; + int color_idx = data->ctrls.hflip.val ? 7 - w / bw : w / bw; if (data->fmt.pixelformat == VIDEO_PIX_FMT_RGB565) { uint16_t *pixel = (uint16_t *)&vbuf->buffer[i]; *pixel = rgb565_colorbar_value[color_idx]; @@ -257,22 +261,6 @@ static int video_sw_generator_set_signal(const struct device *dev, enum video_en } #endif -static inline int video_sw_generator_set_ctrl(const struct device *dev, unsigned int cid, - void *value) -{ - struct video_sw_generator_data *data = dev->data; - - switch (cid) { - case VIDEO_CID_VFLIP: - data->ctrl_vflip = (bool)value; - break; - default: - return -ENOTSUP; - } - - return 0; -} - static int video_sw_generator_set_frmival(const struct device *dev, enum video_endpoint_id ep, struct video_frmival *frmival) { @@ -342,7 +330,6 @@ static DEVICE_API(video, video_sw_generator_driver_api) = { .enqueue = video_sw_generator_enqueue, .dequeue = video_sw_generator_dequeue, .get_caps = video_sw_generator_get_caps, - .set_ctrl = video_sw_generator_set_ctrl, .set_frmival = video_sw_generator_set_frmival, .get_frmival = video_sw_generator_get_frmival, .enum_frmival = video_sw_generator_enum_frmival, @@ -359,6 +346,13 @@ static struct video_sw_generator_data video_sw_generator_data_0 = { .frame_rate = DEFAULT_FRAME_RATE, }; +static int video_sw_generator_init_controls(const struct device *dev) +{ + struct video_sw_generator_data *data = dev->data; + + return video_init_ctrl(&data->ctrls.hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); +} + static int video_sw_generator_init(const struct device *dev) { struct video_sw_generator_data *data = dev->data; @@ -368,7 +362,7 @@ static int video_sw_generator_init(const struct device *dev) k_fifo_init(&data->fifo_out); k_work_init_delayable(&data->buf_work, __buffer_work); - return 0; + return video_sw_generator_init_controls(dev); } DEVICE_DEFINE(video_sw_generator, "VIDEO_SW_GENERATOR", &video_sw_generator_init, NULL, diff --git a/include/zephyr/drivers/video-controls.h b/include/zephyr/drivers/video-controls.h index b67e5a8771ab..292c1dadaf8e 100644 --- a/include/zephyr/drivers/video-controls.h +++ b/include/zephyr/drivers/video-controls.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2019 Linaro Limited. * Copyright (c) 2024 tinyVision.ai Inc. + * Copyright 2025 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -29,6 +30,8 @@ * @{ */ +#include + #ifdef __cplusplus extern "C" { #endif @@ -157,6 +160,29 @@ enum video_power_line_frequency { */ #define VIDEO_CID_PRIVATE_BASE 0x08000000 +/** + * @} + */ + +/** + * @name Public video control structures + * @{ + */ + +/** + * @struct video_control + * @brief Video control structure + * + * Used to get/set a video control. + * @see video_ctrl for the struct used in the driver implementation + */ +struct video_control { + /** control id */ + uint32_t id; + /** control value */ + int32_t val; +}; + /** * @} */ diff --git a/include/zephyr/drivers/video.h b/include/zephyr/drivers/video.h index f0632959e36e..79b59114c1ad 100644 --- a/include/zephyr/drivers/video.h +++ b/include/zephyr/drivers/video.h @@ -6,6 +6,7 @@ /* * Copyright (c) 2019 Linaro Limited. + * Copyright 2025 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -37,6 +38,8 @@ extern "C" { */ #define LINE_COUNT_HEIGHT (-1) +struct video_control; + /** * @struct video_format * @brief Video format structure @@ -320,15 +323,7 @@ typedef int (*video_api_set_stream_t)(const struct device *dev, bool enable); * * See video_set_ctrl() for argument descriptions. */ -typedef int (*video_api_set_ctrl_t)(const struct device *dev, unsigned int cid, void *value); - -/** - * @typedef video_api_get_ctrl_t - * @brief Get a video control value. - * - * See video_get_ctrl() for argument descriptions. - */ -typedef int (*video_api_get_ctrl_t)(const struct device *dev, unsigned int cid, void *value); +typedef int (*video_api_set_ctrl_t)(const struct device *dev, struct video_control *ctrl); /** * @typedef video_api_get_caps_t @@ -359,7 +354,6 @@ __subsystem struct video_driver_api { video_api_dequeue_t dequeue; video_api_flush_t flush; video_api_set_ctrl_t set_ctrl; - video_api_get_ctrl_t get_ctrl; video_api_set_signal_t set_signal; video_api_set_frmival_t set_frmival; video_api_get_frmival_t get_frmival; @@ -653,24 +647,14 @@ static inline int video_get_caps(const struct device *dev, enum video_endpoint_i * must be interpreted accordingly. * * @param dev Pointer to the device structure for the driver instance. - * @param cid Control ID. - * @param value Pointer to the control value. + * @param control Pointer to the video control struct. * * @retval 0 Is successful. * @retval -EINVAL If parameters are invalid. * @retval -ENOTSUP If format is not supported. * @retval -EIO General input / output error. */ -static inline int video_set_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct video_driver_api *api = (const struct video_driver_api *)dev->api; - - if (api->set_ctrl == NULL) { - return -ENOSYS; - } - - return api->set_ctrl(dev, cid, value); -} +int video_set_ctrl(const struct device *dev, struct video_control *control); /** * @brief Get the current value of a control. @@ -678,25 +662,15 @@ static inline int video_set_ctrl(const struct device *dev, unsigned int cid, voi * This retrieve the value of a video control, value type depends on control ID, * and must be interpreted accordingly. * - * @param dev Pointer to the device structure for the driver instance. - * @param cid Control ID. - * @param value Pointer to the control value. + * @param dev Pointer to the device structure. + * @param control Pointer to the video control struct. * * @retval 0 Is successful. * @retval -EINVAL If parameters are invalid. * @retval -ENOTSUP If format is not supported. * @retval -EIO General input / output error. */ -static inline int video_get_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - const struct video_driver_api *api = (const struct video_driver_api *)dev->api; - - if (api->get_ctrl == NULL) { - return -ENOSYS; - } - - return api->get_ctrl(dev, cid, value); -} +int video_get_ctrl(const struct device *dev, struct video_control *control); /** * @brief Register/Unregister k_poll signal for a video endpoint. diff --git a/samples/drivers/video/capture/src/main.c b/samples/drivers/video/capture/src/main.c index 22a1a743d7f2..8d2cd7eec597 100644 --- a/samples/drivers/video/capture/src/main.c +++ b/samples/drivers/video/capture/src/main.c @@ -175,12 +175,15 @@ int main(void) } /* Set controls */ + struct video_control ctrl = {VIDEO_CID_HFLIP, 1}; + if (IS_ENABLED(CONFIG_VIDEO_CTRL_HFLIP)) { - video_set_ctrl(video_dev, VIDEO_CID_HFLIP, (void *)1); + video_set_ctrl(video_dev, &ctrl); } #ifdef CONFIG_TEST - video_set_ctrl(video_dev, VIDEO_CID_TEST_PATTERN, (void *)1); + ctrl.id = VIDEO_CID_TEST_PATTERN; + video_set_ctrl(video_dev, &ctrl); #endif #if DT_HAS_CHOSEN(zephyr_display) diff --git a/samples/drivers/video/capture_to_lvgl/src/main.c b/samples/drivers/video/capture_to_lvgl/src/main.c index ab57c9a52a5e..51432d0e1559 100644 --- a/samples/drivers/video/capture_to_lvgl/src/main.c +++ b/samples/drivers/video/capture_to_lvgl/src/main.c @@ -106,21 +106,17 @@ int main(void) video_enqueue(video_dev, VIDEO_EP_OUT, buffers[i]); } -#ifdef CONFIG_VIDEO_HFLIP - /* Video flip image horizontally */ - if (video_set_ctrl(video_dev, VIDEO_CID_HFLIP, (void *)1)) { - LOG_ERR("Unable to set video control (HFLIP)"); - return 0; + /* Set controls */ + struct video_control ctrl = {VIDEO_CID_HFLIP, 1}; + + if (IS_ENABLED(CONFIG_VIDEO_HFLIP)) { + video_set_ctrl(video_dev, &ctrl); } -#endif -#ifdef CONFIG_VIDEO_VFLIP - /* Video flip image vertically */ - if (video_set_ctrl(video_dev, VIDEO_CID_VFLIP, (void *)1)) { - LOG_ERR("Unable to set video control (VFLIP)"); - return 0; + if (IS_ENABLED(CONFIG_VIDEO_VFLIP)) { + ctrl.id = VIDEO_CID_VFLIP; + video_set_ctrl(video_dev, &ctrl); } -#endif /* Start video capture */ if (video_stream_start(video_dev)) { diff --git a/tests/drivers/video/api/src/video_emul.c b/tests/drivers/video/api/src/video_emul.c index 141627dd414c..5e25f7ca9728 100644 --- a/tests/drivers/video/api/src/video_emul.c +++ b/tests/drivers/video/api/src/video_emul.c @@ -127,12 +127,13 @@ ZTEST(video_common, test_video_frmival) ZTEST(video_common, test_video_ctrl) { - int value; + struct video_control ctrl = {VIDEO_CID_PRIVATE_BASE + 0x01, 30}; - /* Exposure control, expected to be supported by all imagers */ - zexpect_ok(video_set_ctrl(imager_dev, VIDEO_CID_PRIVATE_BASE + 0x01, (void *)30)); - zexpect_ok(video_get_ctrl(imager_dev, VIDEO_CID_PRIVATE_BASE + 0x01, &value)); - zexpect_equal(value, 30); + /* Emulated vendor specific control, expected to be supported by all imagers */ + zexpect_ok(video_set_ctrl(imager_dev, &ctrl)); + ctrl.val = 0; + zexpect_ok(video_get_ctrl(imager_dev, &ctrl)); + zexpect_equal(ctrl.val, 30); } ZTEST(video_common, test_video_vbuf) From ba39d2883ffbe7020dad46212ba4c983c39e4a06 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Fri, 21 Mar 2025 13:47:14 +0100 Subject: [PATCH 3/8] drivers: video: Add video query control API Application can query information about a control given the control id, the framework fill the rest of the structure. Application can also enumerate all kinds of device's supported controls by iterating with VIDEO_CTRL_FLAG_NEXT_CTRL on the same API. Signed-off-by: Phi Bang Nguyen --- drivers/video/video_ctrls.c | 139 +++++++++++++++++++++++ include/zephyr/drivers/video-controls.h | 40 +++++++ include/zephyr/drivers/video.h | 35 ++++++ samples/drivers/video/capture/src/main.c | 11 ++ 4 files changed, 225 insertions(+) diff --git a/drivers/video/video_ctrls.c b/drivers/video/video_ctrls.c index c77564b01b94..c5caad2d85f0 100644 --- a/drivers/video/video_ctrls.c +++ b/drivers/video/video_ctrls.c @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include + #include #include #include @@ -67,6 +69,7 @@ int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t int ret; uint32_t flags; enum video_ctrl_type type; + struct video_ctrl *vc; struct video_device *vdev = video_find_vdev(dev); if (!vdev) { @@ -95,6 +98,14 @@ int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t ctrl->def = def; ctrl->val = 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; @@ -173,3 +184,131 @@ int video_set_ctrl(const struct device *dev, struct video_control *control) return 0; } + +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_EXPOSURE: + return "Exposure"; + case VIDEO_CID_GAIN: + return "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"; + + /* Camera controls */ + case VIDEO_CID_ZOOM_ABSOLUTE: + return "Zoom, Absolute"; + + /* 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"; + default: + return NULL; + } +} + +int video_query_ctrl(const struct device *dev, struct video_ctrl_query *cq) +{ + int ret; + struct video_device *vdev; + struct video_ctrl *ctrl = NULL; + + if (cq->id & VIDEO_CTRL_FLAG_NEXT_CTRL) { + vdev = video_find_vdev(dev); + cq->id &= ~VIDEO_CTRL_FLAG_NEXT_CTRL; + while (vdev) { + SYS_DLIST_FOR_EACH_CONTAINER(&vdev->ctrls, ctrl, node) { + if (ctrl->id > cq->id) { + goto fill_query; + } + } + vdev = video_find_vdev(vdev->src_dev); + } + return -ENOTSUP; + } + + ret = video_find_ctrl(dev, cq->id, &ctrl); + if (ret) { + return ret; + } + +fill_query: + cq->id = ctrl->id; + cq->type = ctrl->type; + cq->flags = ctrl->flags; + cq->min = ctrl->min; + cq->max = ctrl->max; + cq->step = ctrl->step; + cq->def = ctrl->def; + cq->name = video_get_ctrl_name(cq->id); + + return 0; +} + +void video_print_ctrl(const struct device *const dev, const struct video_ctrl_query *const cq) +{ + uint8_t i = 0; + const char *type = NULL; + char typebuf[8]; + + __ASSERT(dev && cq, "Invalid arguments"); + + /* 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_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(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); + } +} diff --git a/include/zephyr/drivers/video-controls.h b/include/zephyr/drivers/video-controls.h index 292c1dadaf8e..1749f5363e4c 100644 --- a/include/zephyr/drivers/video-controls.h +++ b/include/zephyr/drivers/video-controls.h @@ -78,6 +78,9 @@ enum video_power_line_frequency { /** Balance of colors in direction of blue (cold) or red (warm) */ #define VIDEO_CID_WHITE_BALANCE_TEMPERATURE (VIDEO_CID_BASE + 26) +/** Last base CID + 1 */ +#define VIDEO_CID_LASTP1 (VIDEO_CID_BASE + 44) + /** * @} */ @@ -160,6 +163,16 @@ enum video_power_line_frequency { */ #define VIDEO_CID_PRIVATE_BASE 0x08000000 +/** + * @} + */ + +/** + * @name Query flags, to be ORed with the control ID + * @{ + */ +#define VIDEO_CTRL_FLAG_NEXT_CTRL 0x80000000 + /** * @} */ @@ -183,6 +196,33 @@ struct video_control { int32_t val; }; +/** + * @struct video_control_query + * @brief Video control query structure + * + * Used to query information about a control. + */ +struct video_ctrl_query { + /** control id */ + uint32_t id; + /** control type */ + uint32_t type; + /** control name */ + const char *name; + /** control flags */ + uint32_t flags; + /** control minimum value, inclusive */ + int32_t min; + /** control maximum value, inclusive */ + int32_t max; + /** control value step */ + int32_t step; + /** control default value for VIDEO_CTRL_TYPE_INTEGER, _BOOLEAN, _MENU or + * _INTEGER_MENU, not valid for other types + */ + int32_t def; +}; + /** * @} */ diff --git a/include/zephyr/drivers/video.h b/include/zephyr/drivers/video.h index 79b59114c1ad..cabfdb9766cb 100644 --- a/include/zephyr/drivers/video.h +++ b/include/zephyr/drivers/video.h @@ -672,6 +672,41 @@ int video_set_ctrl(const struct device *dev, struct video_control *control); */ int video_get_ctrl(const struct device *dev, struct video_control *control); +struct video_ctrl_query; + +/** + * @brief Query information about a control. + * + * Applications set the id field of the query structure, the function fills the rest of this + * structure. It is possible to enumerate base class controls (i.e., VIDEO_CID_BASE + x) by calling + * this function with successive id values starting from VIDEO_CID_BASE up to and exclusive + * VIDEO_CID_LASTP1. The function may return -ENOTSUP if a control in this range is not supported. + * Applications can also enumerate private controls by starting at VIDEO_CID_PRIVATE_BASE and + * incrementing the id until the driver returns -ENOTSUP. For other control classes, it's a bit more + * difficult. Hence, the best way to enumerate all kinds of device's supported controls is to + * iterate with VIDEO_CTRL_FLAG_NEXT_CTRL. + * + * @param dev Pointer to the device structure. + * @param cq Pointer to the control query struct. + * + * @retval 0 If successful. + * @retval -EINVAL If the control id is invalid. + * @retval -ENOTSUP If the control id is not supported. + */ +int video_query_ctrl(const struct device *dev, struct video_ctrl_query *cq); + +/** + * @brief Print all the information of a control. + * + * Print all the information of a control including its name, type, flag, range, + * menu (if any) and current value, i.e. by invoking the video_get_ctrl(), in a + * human readble format. + * + * @param dev Pointer to the device structure. + * @param cq Pointer to the control query struct. + */ +void video_print_ctrl(const struct device *const dev, const struct video_ctrl_query *const cq); + /** * @brief Register/Unregister k_poll signal for a video endpoint. * diff --git a/samples/drivers/video/capture/src/main.c b/samples/drivers/video/capture/src/main.c index 8d2cd7eec597..946a1ba705f3 100644 --- a/samples/drivers/video/capture/src/main.c +++ b/samples/drivers/video/capture/src/main.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2019 Linaro Limited + * Copyright 2025 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -174,6 +175,16 @@ int main(void) fie.index++; } + /* Get supported controls */ + LOG_INF("- Supported controls:"); + + struct video_ctrl_query cq = {.id = VIDEO_CTRL_FLAG_NEXT_CTRL}; + + while (!video_query_ctrl(video_dev, &cq)) { + video_print_ctrl(video_dev, &cq); + cq.id |= VIDEO_CTRL_FLAG_NEXT_CTRL; + } + /* Set controls */ struct video_control ctrl = {VIDEO_CID_HFLIP, 1}; From 6f66e7ae0764b5d9198f781ce89a15e75fb9ba8b Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Fri, 28 Mar 2025 23:10:58 +0100 Subject: [PATCH 4/8] drivers: video: Support controls of 64-bit integer type Add supports for controls that need 64-bit integer such as pixel rate. Signed-off-by: Phi Bang Nguyen --- drivers/video/gc2145.c | 6 +- drivers/video/mt9m114.c | 7 ++- drivers/video/ov2640.c | 31 ++++++---- drivers/video/ov5640.c | 50 +++++++++++------ drivers/video/ov7670.c | 6 +- drivers/video/video_ctrls.c | 56 ++++++++++++------- drivers/video/video_ctrls.h | 11 ++-- drivers/video/video_emul_imager.c | 5 +- drivers/video/video_mcux_mipi_csi2rx.c | 24 ++++---- drivers/video/video_sw_generator.c | 3 +- include/zephyr/drivers/video-controls.h | 48 ++++++++++++---- samples/drivers/video/capture/src/main.c | 2 +- .../drivers/video/capture_to_lvgl/src/main.c | 2 +- tests/drivers/video/api/src/video_emul.c | 2 +- 14 files changed, 167 insertions(+), 86 deletions(-) diff --git a/drivers/video/gc2145.c b/drivers/video/gc2145.c index afd16d2acf23..22277679652c 100644 --- a/drivers/video/gc2145.c +++ b/drivers/video/gc2145.c @@ -1136,12 +1136,14 @@ static int gc2145_init_controls(const struct device *dev) struct gc2145_data *drv_data = dev->data; struct gc2145_ctrls *ctrls = &drv_data->ctrls; - ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); if (ret) { return ret; } - return video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, 0, 1, 1, 0); + return video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); } static int gc2145_init(const struct device *dev) diff --git a/drivers/video/mt9m114.c b/drivers/video/mt9m114.c index a72656ff0969..fc4435ccd3f5 100644 --- a/drivers/video/mt9m114.c +++ b/drivers/video/mt9m114.c @@ -514,13 +514,14 @@ static int mt9m114_init_controls(const struct device *dev) struct mt9m114_data *drv_data = dev->data; struct mt9m114_ctrls *ctrls = &drv_data->ctrls; - - ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); if (ret) { return ret; } - return video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, 0, 1, 1, 0); + return video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); } static int mt9m114_init(const struct device *dev) diff --git a/drivers/video/ov2640.c b/drivers/video/ov2640.c index 660efd781bb1..a9e50c7ab441 100644 --- a/drivers/video/ov2640.c +++ b/drivers/video/ov2640.c @@ -940,52 +940,63 @@ static int ov2640_init_controls(const struct device *dev) struct ov2640_data *drv_data = dev->data; struct ov2640_ctrls *ctrls = &drv_data->ctrls; - ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, 0, 1, 1, 0); + ret = video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->ae, dev, VIDEO_CID_EXPOSURE, 0, 1, 1, 1); + ret = video_init_ctrl(&ctrls->ae, dev, VIDEO_CID_EXPOSURE, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 1}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->awb, dev, VIDEO_CID_WHITE_BALANCE_TEMPERATURE, 0, 1, 1, 1); + ret = video_init_ctrl(&ctrls->awb, dev, VIDEO_CID_WHITE_BALANCE_TEMPERATURE, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 1}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->gain, dev, VIDEO_CID_GAIN, 0, 1, 1, 1); + ret = video_init_ctrl(&ctrls->gain, dev, VIDEO_CID_GAIN, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 1}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->brightness, dev, VIDEO_CID_BRIGHTNESS, -2, 2, 1, 0); + ret = video_init_ctrl(&ctrls->brightness, dev, VIDEO_CID_BRIGHTNESS, + (struct video_ctrl_range){.min = -2, .max = 2, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->contrast, dev, VIDEO_CID_CONTRAST, -2, 2, 1, 0); + ret = video_init_ctrl(&ctrls->contrast, dev, VIDEO_CID_CONTRAST, + (struct video_ctrl_range){.min = -2, .max = 2, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->saturation, dev, VIDEO_CID_SATURATION, -2, 2, 1, 0); + ret = video_init_ctrl(&ctrls->saturation, dev, VIDEO_CID_SATURATION, + (struct video_ctrl_range){.min = -2, .max = 2, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->jpeg, dev, VIDEO_CID_JPEG_COMPRESSION_QUALITY, 5, 100, 1, 50); + ret = video_init_ctrl( + &ctrls->jpeg, dev, VIDEO_CID_JPEG_COMPRESSION_QUALITY, + (struct video_ctrl_range){.min = 5, .max = 100, .step = 1, .def = 50}); if (ret) { return ret; } - return video_init_ctrl(&ctrls->test_pattern, dev, VIDEO_CID_TEST_PATTERN, 0, 1, 1, 0); + return video_init_ctrl(&ctrls->test_pattern, dev, VIDEO_CID_TEST_PATTERN, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); } static int ov2640_init(const struct device *dev) diff --git a/drivers/video/ov5640.c b/drivers/video/ov5640.c index 79be179afe9b..26263643b216 100644 --- a/drivers/video/ov5640.c +++ b/drivers/video/ov5640.c @@ -1182,59 +1182,77 @@ static int ov5640_init_controls(const struct device *dev) struct ov5640_data *drv_data = dev->data; struct ov5640_ctrls *ctrls = &drv_data->ctrls; - ret = video_init_ctrl(&ctrls->gain, dev, VIDEO_CID_GAIN, 0, 1023, 1, 0); + ret = video_init_ctrl( + &ctrls->gain, dev, VIDEO_CID_GAIN, + (struct video_ctrl_range){.min = 0, .max = 1023, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->brightness, dev, VIDEO_CID_BRIGHTNESS, -15, 15, 1, 0); + ret = video_init_ctrl( + &ctrls->brightness, dev, VIDEO_CID_BRIGHTNESS, + (struct video_ctrl_range){.min = -15, .max = 15, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->contrast, dev, VIDEO_CID_CONTRAST, 0, 255, 1, 0); + ret = video_init_ctrl(&ctrls->contrast, dev, VIDEO_CID_CONTRAST, + (struct video_ctrl_range){.min = 0, .max = 255, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->hue, dev, VIDEO_CID_HUE, 0, 359, 1, 0); + ret = video_init_ctrl(&ctrls->hue, dev, VIDEO_CID_HUE, + (struct video_ctrl_range){.min = 0, .max = 359, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->saturation, dev, VIDEO_CID_SATURATION, 0, 255, 1, 64); + ret = video_init_ctrl( + &ctrls->saturation, dev, VIDEO_CID_SATURATION, + (struct video_ctrl_range){.min = 0, .max = 255, .step = 1, .def = 64}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, 0, 1, 1, 0); + ret = video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->light_freq, dev, VIDEO_CID_POWER_LINE_FREQUENCY, - VIDEO_CID_POWER_LINE_FREQUENCY_DISABLED, - VIDEO_CID_POWER_LINE_FREQUENCY_AUTO, 1, - VIDEO_CID_POWER_LINE_FREQUENCY_50HZ); + ret = video_init_ctrl( + &ctrls->light_freq, dev, VIDEO_CID_POWER_LINE_FREQUENCY, + (struct video_ctrl_range){.min = VIDEO_CID_POWER_LINE_FREQUENCY_DISABLED, + .max = VIDEO_CID_POWER_LINE_FREQUENCY_AUTO, + .step = 1, + .def = VIDEO_CID_POWER_LINE_FREQUENCY_50HZ}); if (ret) { return ret; } - ret = video_init_ctrl(&ctrls->test_pattern, dev, VIDEO_CID_TEST_PATTERN, 0, - ARRAY_SIZE(test_pattern_val) - 1, 1, 0); + ret = video_init_ctrl( + &ctrls->test_pattern, dev, VIDEO_CID_TEST_PATTERN, + (struct video_ctrl_range){ + .min = 0, .max = ARRAY_SIZE(test_pattern_val) - 1, .step = 1, .def = 0}); if (ret) { return ret; } return video_init_ctrl( - &ctrls->pixel_rate, dev, VIDEO_CID_PIXEL_RATE, mipi_vga_frmrate_params[0].pixelrate, - mipi_hd_frmrate_params[ARRAY_SIZE(mipi_hd_frmrate_params) - 1].pixelrate, 1, - drv_data->cur_pixrate); + &ctrls->pixel_rate, dev, VIDEO_CID_PIXEL_RATE, + (struct video_ctrl_range){ + .min64 = mipi_qqvga_frmrate_params[0].pixelrate, + .max64 = mipi_hd_frmrate_params[ARRAY_SIZE(mipi_hd_frmrate_params) - 1] + .pixelrate, + .step64 = 1, + .def64 = mipi_hd_frmrate_params[1].pixelrate}); } static int ov5640_init(const struct device *dev) diff --git a/drivers/video/ov7670.c b/drivers/video/ov7670.c index dd2765150099..192a63a79084 100644 --- a/drivers/video/ov7670.c +++ b/drivers/video/ov7670.c @@ -464,12 +464,14 @@ static int ov7670_init_controls(const struct device *dev) struct ov7670_data *drv_data = dev->data; struct ov7670_ctrls *ctrls = &drv_data->ctrls; - ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); if (ret) { return ret; } - return video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, 0, 1, 1, 0); + return video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); } static int ov7670_init(const struct device *dev) diff --git a/drivers/video/video_ctrls.c b/drivers/video/video_ctrls.c index c5caad2d85f0..90f99640f86f 100644 --- a/drivers/video/video_ctrls.c +++ b/drivers/video/video_ctrls.c @@ -15,23 +15,29 @@ LOG_MODULE_REGISTER(video_ctrls, CONFIG_VIDEO_LOG_LEVEL); -static inline int check_range(enum video_ctrl_type type, int32_t min, int32_t max, uint32_t step, - int32_t def) +static inline int check_range(enum video_ctrl_type type, struct video_ctrl_range range) { switch (type) { case VIDEO_CTRL_TYPE_BOOLEAN: - if (step != 1 || max > 1 || min < 0) { + 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 (step == 0 || min > max || !IN_RANGE(def, min, max)) { + 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: - if (!IN_RANGE(min, 0, max) || !IN_RANGE(def, min, max)) { + if (!IN_RANGE(range.min, 0, range.max) || + !IN_RANGE(range.def, range.min, range.max)) { return -ERANGE; } return 0; @@ -63,8 +69,8 @@ static inline void set_type_flag(uint32_t id, enum video_ctrl_type *type, uint32 } } -int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id, int32_t min, - int32_t max, uint32_t step, int32_t def) +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; @@ -83,7 +89,7 @@ int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t set_type_flag(id, &type, &flags); - ret = check_range(type, min, max, step, def); + ret = check_range(type, range); if (ret) { return ret; } @@ -92,11 +98,13 @@ int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t ctrl->id = id; ctrl->type = type; ctrl->flags = flags; - ctrl->min = min; - ctrl->max = max; - ctrl->step = step; - ctrl->def = def; - ctrl->val = def; + 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) { @@ -143,7 +151,11 @@ int video_get_ctrl(const struct device *dev, struct video_control *control) return -EACCES; } - control->val = ctrl->val; + if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64) { + control->val64 = ctrl->val64; + } else { + control->val = ctrl->val; + } return 0; } @@ -163,7 +175,9 @@ int video_set_ctrl(const struct device *dev, struct video_control *control) return -EACCES; } - if (!IN_RANGE(control->val, ctrl->min, ctrl->max)) { + 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; } @@ -180,7 +194,11 @@ int video_set_ctrl(const struct device *dev, struct video_control *control) update: /* Only update the ctrl in memory once everything is OK */ - ctrl->val = control->val; + if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64) { + ctrl->val64 = control->val64; + } else { + ctrl->val = control->val; + } return 0; } @@ -255,10 +273,8 @@ int video_query_ctrl(const struct device *dev, struct video_ctrl_query *cq) cq->id = ctrl->id; cq->type = ctrl->type; cq->flags = ctrl->flags; - cq->min = ctrl->min; - cq->max = ctrl->max; - cq->step = ctrl->step; - cq->def = ctrl->def; + cq->range = ctrl->range; + cq->name = video_get_ctrl_name(cq->id); return 0; diff --git a/drivers/video/video_ctrls.h b/drivers/video/video_ctrls.h index 95c604148fcb..4d0262ea849b 100644 --- a/drivers/video/video_ctrls.h +++ b/drivers/video/video_ctrls.h @@ -43,12 +43,15 @@ struct video_ctrl { uint32_t id; enum video_ctrl_type type; unsigned long flags; - int32_t min, max, def, val; - uint32_t step; + struct video_ctrl_range range; + union { + int32_t val; + int64_t val64; + }; sys_dnode_t node; }; -int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id, int32_t min, - int32_t max, uint32_t step, int32_t def); +int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id, + struct video_ctrl_range range); #endif /* ZEPHYR_INCLUDE_DRIVERS_VIDEO_VIDEO_CTRLS_H_ */ diff --git a/drivers/video/video_emul_imager.c b/drivers/video/video_emul_imager.c index 0aedb5f07c4a..d9f659390705 100644 --- a/drivers/video/video_emul_imager.c +++ b/drivers/video/video_emul_imager.c @@ -366,8 +366,9 @@ static int emul_imager_init_controls(const struct device *dev) { struct emul_imager_data *drv_data = dev->data; - return video_init_ctrl(&drv_data->ctrls.custom, dev, EMUL_IMAGER_CID_CUSTOM, 0, 255, 1, - 128); + return video_init_ctrl( + &drv_data->ctrls.custom, dev, EMUL_IMAGER_CID_CUSTOM, + (struct video_ctrl_range){.min = 0, .max = 255, .step = 1, .def = 128}); } int emul_imager_init(const struct device *dev) diff --git a/drivers/video/video_mcux_mipi_csi2rx.c b/drivers/video/video_mcux_mipi_csi2rx.c index 9ce88023aa72..be6ff4595f3a 100644 --- a/drivers/video/video_mcux_mipi_csi2rx.c +++ b/drivers/video/video_mcux_mipi_csi2rx.c @@ -56,7 +56,7 @@ static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endp uint32_t root_clk_rate, ui_clk_rate, sensor_byte_clk, best_match; int ret, ind = 0; struct video_format fmt; - struct video_control sensor_rate = {VIDEO_CID_PIXEL_RATE, -1}; + struct video_control sensor_rate = {.id = VIDEO_CID_PIXEL_RATE, .val64 = -1}; ret = video_get_format(config->sensor_dev, ep, &fmt); if (ret) { @@ -65,13 +65,13 @@ static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endp } video_get_ctrl(config->sensor_dev, &sensor_rate); - if (!IN_RANGE(sensor_rate.val, 0, MAX_SUPPORTED_PIXEL_RATE)) { - LOG_ERR("Sensor pixel rate is not supported"); + if (!IN_RANGE(sensor_rate.val64, 0, MAX_SUPPORTED_PIXEL_RATE)) { + LOG_ERR("Sensor pixel rate is not supported %lld", sensor_rate.val64); return -ENOTSUP; } bpp = video_bits_per_pixel(fmt.pixelformat); - sensor_byte_clk = sensor_rate.val * bpp / drv_data->csi2rxConfig.laneNum / BITS_PER_BYTE; + sensor_byte_clk = sensor_rate.val64 * bpp / drv_data->csi2rxConfig.laneNum / BITS_PER_BYTE; ret = clock_control_get_rate(drv_data->clock_dev, drv_data->clock_root, &root_clk_rate); if (ret) { @@ -91,10 +91,10 @@ static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endp return ret; } - if (sensor_rate.val > ui_clk_rate) { + if (sensor_rate.val64 > ui_clk_rate) { ret = clock_control_set_rate( drv_data->clock_dev, drv_data->clock_ui, - (clock_control_subsys_rate_t)(uint32_t)sensor_rate.val); + (clock_control_subsys_rate_t)(uint32_t)sensor_rate.val64); if (ret) { return ret; } @@ -103,7 +103,7 @@ static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endp /* Find the supported sensor_pixel_rate closest to the desired one */ best_match = tHsSettleEscClk_configs[ind].pixel_rate; for (uint8_t i = 0; i < ARRAY_SIZE(tHsSettleEscClk_configs); i++) { - if (ABS(tHsSettleEscClk_configs[i].pixel_rate, sensor_rate.val) < + if (ABS(tHsSettleEscClk_configs[i].pixel_rate, sensor_rate.val64) < ABS(tHsSettleEscClk_configs[i].pixel_rate, best_match)) { best_match = tHsSettleEscClk_configs[i].pixel_rate; ind = i; @@ -228,7 +228,7 @@ static int mipi_csi2rx_enum_frmival(const struct device *dev, enum video_endpoin uint64_t est_pixel_rate; struct video_frmival cur_frmival; struct video_format cur_fmt; - struct video_control sensor_rate = {VIDEO_CID_PIXEL_RATE, -1}; + struct video_control sensor_rate = {.id = VIDEO_CID_PIXEL_RATE, .val64 = -1}; ret = video_enum_frmival(config->sensor_dev, ep, fie); if (ret) { @@ -254,7 +254,7 @@ static int mipi_csi2rx_enum_frmival(const struct device *dev, enum video_endpoin if (fie->type == VIDEO_FRMIVAL_TYPE_DISCRETE) { est_pixel_rate = mipi_csi2rx_estimate_pixel_rate( - &cur_frmival, &fie->discrete, &cur_fmt, fie->format, sensor_rate.val, + &cur_frmival, &fie->discrete, &cur_fmt, fie->format, sensor_rate.val64, drv_data->csi2rxConfig.laneNum); if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { return -EINVAL; @@ -263,7 +263,7 @@ static int mipi_csi2rx_enum_frmival(const struct device *dev, enum video_endpoin } else { /* Check the lane rate of the lower bound framerate */ est_pixel_rate = mipi_csi2rx_estimate_pixel_rate( - &cur_frmival, &fie->stepwise.min, &cur_fmt, fie->format, sensor_rate.val, + &cur_frmival, &fie->stepwise.min, &cur_fmt, fie->format, sensor_rate.val64, drv_data->csi2rxConfig.laneNum); if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { return -EINVAL; @@ -271,13 +271,13 @@ static int mipi_csi2rx_enum_frmival(const struct device *dev, enum video_endpoin /* Check the lane rate of the upper bound framerate */ est_pixel_rate = mipi_csi2rx_estimate_pixel_rate( - &cur_frmival, &fie->stepwise.max, &cur_fmt, fie->format, sensor_rate.val, + &cur_frmival, &fie->stepwise.max, &cur_fmt, fie->format, sensor_rate.val64, drv_data->csi2rxConfig.laneNum); if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { fie->stepwise.max.denominator = (mipi_csi2rx_cal_frame_size(&cur_fmt) * MAX_SUPPORTED_PIXEL_RATE * cur_frmival.denominator) / - (mipi_csi2rx_cal_frame_size(fie->format) * sensor_rate.val * + (mipi_csi2rx_cal_frame_size(fie->format) * sensor_rate.val64 * cur_frmival.numerator); fie->stepwise.max.numerator = 1; } diff --git a/drivers/video/video_sw_generator.c b/drivers/video/video_sw_generator.c index 81466e373c03..ed8ade5270ca 100644 --- a/drivers/video/video_sw_generator.c +++ b/drivers/video/video_sw_generator.c @@ -350,7 +350,8 @@ static int video_sw_generator_init_controls(const struct device *dev) { struct video_sw_generator_data *data = dev->data; - return video_init_ctrl(&data->ctrls.hflip, dev, VIDEO_CID_HFLIP, 0, 1, 1, 0); + return video_init_ctrl(&data->ctrls.hflip, dev, VIDEO_CID_HFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); } static int video_sw_generator_init(const struct device *dev) diff --git a/include/zephyr/drivers/video-controls.h b/include/zephyr/drivers/video-controls.h index 1749f5363e4c..e1b11a4b49fe 100644 --- a/include/zephyr/drivers/video-controls.h +++ b/include/zephyr/drivers/video-controls.h @@ -193,7 +193,41 @@ struct video_control { /** control id */ uint32_t id; /** control value */ - int32_t val; + union { + int32_t val; + int64_t val64; + }; +}; + +/** + * @struct video_control_range + * @brief Video control range structure + * + * Describe range of a control including min, max, step and default values + */ +struct video_ctrl_range { + /** control minimum value, inclusive */ + union { + int32_t min; + int64_t min64; + }; + /** control maximum value, inclusive */ + union { + int32_t max; + int64_t max64; + }; + /** control value step */ + union { + int32_t step; + int64_t step64; + }; + /** control default value for VIDEO_CTRL_TYPE_INTEGER, _BOOLEAN, _MENU or + * _INTEGER_MENU, not valid for other types + */ + union { + int32_t def; + int64_t def64; + }; }; /** @@ -211,16 +245,8 @@ struct video_ctrl_query { const char *name; /** control flags */ uint32_t flags; - /** control minimum value, inclusive */ - int32_t min; - /** control maximum value, inclusive */ - int32_t max; - /** control value step */ - int32_t step; - /** control default value for VIDEO_CTRL_TYPE_INTEGER, _BOOLEAN, _MENU or - * _INTEGER_MENU, not valid for other types - */ - int32_t def; + /** control range */ + struct video_ctrl_range range; }; /** diff --git a/samples/drivers/video/capture/src/main.c b/samples/drivers/video/capture/src/main.c index 946a1ba705f3..501ebd8794ab 100644 --- a/samples/drivers/video/capture/src/main.c +++ b/samples/drivers/video/capture/src/main.c @@ -186,7 +186,7 @@ int main(void) } /* Set controls */ - struct video_control ctrl = {VIDEO_CID_HFLIP, 1}; + struct video_control ctrl = {.id = VIDEO_CID_HFLIP, .val = 1}; if (IS_ENABLED(CONFIG_VIDEO_CTRL_HFLIP)) { video_set_ctrl(video_dev, &ctrl); diff --git a/samples/drivers/video/capture_to_lvgl/src/main.c b/samples/drivers/video/capture_to_lvgl/src/main.c index 51432d0e1559..201e0a8bed90 100644 --- a/samples/drivers/video/capture_to_lvgl/src/main.c +++ b/samples/drivers/video/capture_to_lvgl/src/main.c @@ -107,7 +107,7 @@ int main(void) } /* Set controls */ - struct video_control ctrl = {VIDEO_CID_HFLIP, 1}; + struct video_control ctrl = {.id = VIDEO_CID_HFLIP, .val = 1}; if (IS_ENABLED(CONFIG_VIDEO_HFLIP)) { video_set_ctrl(video_dev, &ctrl); diff --git a/tests/drivers/video/api/src/video_emul.c b/tests/drivers/video/api/src/video_emul.c index 5e25f7ca9728..cd58bbf0506b 100644 --- a/tests/drivers/video/api/src/video_emul.c +++ b/tests/drivers/video/api/src/video_emul.c @@ -127,7 +127,7 @@ ZTEST(video_common, test_video_frmival) ZTEST(video_common, test_video_ctrl) { - struct video_control ctrl = {VIDEO_CID_PRIVATE_BASE + 0x01, 30}; + struct video_control ctrl = {.id = VIDEO_CID_PRIVATE_BASE + 0x01, .val = 30}; /* Emulated vendor specific control, expected to be supported by all imagers */ zexpect_ok(video_set_ctrl(imager_dev, &ctrl)); From 1ff8fa5c221408db095283876b0f9de737bc6063 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Fri, 28 Mar 2025 23:37:52 +0100 Subject: [PATCH 5/8] drivers: video: Add get_volatile_ctrl driver's API Add get_volatile_ctrl() driver's API to retrieve the current value of a control marked as volatile, e.g. gain, exposure. This function triggers a hardware register reading instead of returning a cached value to ensure that users always get a fresh value which is constantly updated by the HW. Note that the driver is responsible for marking a control as volatile by setting VIDEO_CTRL_FLAG_VOLATILE when registering a control because not all hardwares work the same way for the same control. Signed-off-by: Phi Bang Nguyen --- drivers/video/video_ctrls.c | 11 +++++++++++ include/zephyr/drivers/video.h | 11 ++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/video/video_ctrls.c b/drivers/video/video_ctrls.c index 90f99640f86f..0dc99820ba5d 100644 --- a/drivers/video/video_ctrls.c +++ b/drivers/video/video_ctrls.c @@ -151,6 +151,17 @@ int video_get_ctrl(const struct device *dev, struct video_control *control) 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 */ + return DEVICE_API_GET(video, ctrl->vdev->dev) + ->get_volatile_ctrl(ctrl->vdev->dev, control); + } + + /* Read control value in cache memory */ if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64) { control->val64 = ctrl->val64; } else { diff --git a/include/zephyr/drivers/video.h b/include/zephyr/drivers/video.h index cabfdb9766cb..184492d1952c 100644 --- a/include/zephyr/drivers/video.h +++ b/include/zephyr/drivers/video.h @@ -318,12 +318,12 @@ typedef int (*video_api_flush_t)(const struct device *dev, enum video_endpoint_i typedef int (*video_api_set_stream_t)(const struct device *dev, bool enable); /** - * @typedef video_api_set_ctrl_t - * @brief Set a video control value. + * @typedef video_api_ctrl_t + * @brief Set/Get a video control value. * - * See video_set_ctrl() for argument descriptions. + * See video_set_ctrl() or video_get_ctrl() for argument descriptions. */ -typedef int (*video_api_set_ctrl_t)(const struct device *dev, struct video_control *ctrl); +typedef int (*video_api_ctrl_t)(const struct device *dev, struct video_control *ctrl); /** * @typedef video_api_get_caps_t @@ -353,7 +353,8 @@ __subsystem struct video_driver_api { video_api_enqueue_t enqueue; video_api_dequeue_t dequeue; video_api_flush_t flush; - video_api_set_ctrl_t set_ctrl; + video_api_ctrl_t set_ctrl; + video_api_ctrl_t get_volatile_ctrl; video_api_set_signal_t set_signal; video_api_set_frmival_t set_frmival; video_api_get_frmival_t get_frmival; From 2c46c9f6e5fa943741304e0fade36ea8791f940d Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Sat, 5 Apr 2025 00:28:53 +0200 Subject: [PATCH 6/8] drivers: video: Add support for composite controls For controls that are dependent from others, we need to "cluster" them. Whenever one or more controls of the same cluster are set or gotten, only the callback of the 1st control of the cluster, i.e. the master control, is called. The master control is the one that represents the whole cluster. A common type of control cluster is "auto"-cluster, e.g. auto_gain/gain, auto_exposure/exposure, auto_white_balance/red_balance/blue_balance, etc. If the cluster is in automatic mode, then the manual controls are marked inactive and volatile which are read via get_volatile_ctrl(). If the cluster is put in manual mode, then the manual controls should become active again and the volatile flag is cleared. Re-implement the ov5640's autogain/analogue_gain controls with the new auto cluster mechanism so that it work correctly and fully. Signed-off-by: Phi Bang Nguyen --- drivers/video/gc2145.c | 10 +- drivers/video/mt9m114.c | 19 +-- drivers/video/ov2640.c | 27 +++-- drivers/video/ov5640.c | 101 ++++++++++++---- drivers/video/ov7670.c | 14 ++- drivers/video/video_ctrls.c | 147 +++++++++++++++++++++--- drivers/video/video_ctrls.h | 10 ++ drivers/video/video_emul_imager.c | 6 +- include/zephyr/drivers/video-controls.h | 11 +- include/zephyr/drivers/video.h | 5 +- 10 files changed, 278 insertions(+), 72 deletions(-) diff --git a/drivers/video/gc2145.c b/drivers/video/gc2145.c index 22277679652c..3b6596453ee2 100644 --- a/drivers/video/gc2145.c +++ b/drivers/video/gc2145.c @@ -1110,13 +1110,15 @@ static int gc2145_get_caps(const struct device *dev, enum video_endpoint_id ep, return 0; } -static int gc2145_set_ctrl(const struct device *dev, struct video_control *ctrl) +static int gc2145_set_ctrl(const struct device *dev, uint32_t id) { - switch (ctrl->id) { + struct gc2145_data *drv_data = dev->data; + + switch (id) { case VIDEO_CID_HFLIP: - return gc2145_set_ctrl_hmirror(dev, ctrl->val); + return gc2145_set_ctrl_hmirror(dev, drv_data->ctrls.hflip.val); case VIDEO_CID_VFLIP: - return gc2145_set_ctrl_vflip(dev, ctrl->val); + return gc2145_set_ctrl_vflip(dev, drv_data->ctrls.vflip.val); default: return -ENOTSUP; } diff --git a/drivers/video/mt9m114.c b/drivers/video/mt9m114.c index fc4435ccd3f5..16d4e61bb391 100644 --- a/drivers/video/mt9m114.c +++ b/drivers/video/mt9m114.c @@ -473,20 +473,23 @@ static int mt9m114_get_caps(const struct device *dev, enum video_endpoint_id ep, return 0; } -static int mt9m114_set_ctrl(const struct device *dev, struct video_control *ctrl) +static int mt9m114_set_ctrl(const struct device *dev, uint32_t id) { int ret = 0; + struct mt9m114_data *drv_data = dev->data; - switch (ctrl->id) { + switch (id) { case VIDEO_CID_HFLIP: - ret = mt9m114_modify_reg(dev, MT9M114_CAM_SENSOR_CTRL_READ_MODE, 2, - MT9M114_CAM_SENSOR_CTRL_HORZ_FLIP_EN, - ctrl->val ? MT9M114_CAM_SENSOR_CTRL_HORZ_FLIP_EN : 0); + ret = mt9m114_modify_reg( + dev, MT9M114_CAM_SENSOR_CTRL_READ_MODE, 2, + MT9M114_CAM_SENSOR_CTRL_HORZ_FLIP_EN, + drv_data->ctrls.hflip.val ? MT9M114_CAM_SENSOR_CTRL_HORZ_FLIP_EN : 0); break; case VIDEO_CID_VFLIP: - ret = mt9m114_modify_reg(dev, MT9M114_CAM_SENSOR_CTRL_READ_MODE, 2, - MT9M114_CAM_SENSOR_CTRL_VERT_FLIP_EN, - ctrl->val ? MT9M114_CAM_SENSOR_CTRL_VERT_FLIP_EN : 0); + ret = mt9m114_modify_reg( + dev, MT9M114_CAM_SENSOR_CTRL_READ_MODE, 2, + MT9M114_CAM_SENSOR_CTRL_VERT_FLIP_EN, + drv_data->ctrls.vflip.val ? MT9M114_CAM_SENSOR_CTRL_VERT_FLIP_EN : 0); break; default: return -ENOTSUP; diff --git a/drivers/video/ov2640.c b/drivers/video/ov2640.c index a9e50c7ab441..5da0c2d504e1 100644 --- a/drivers/video/ov2640.c +++ b/drivers/video/ov2640.c @@ -895,32 +895,35 @@ static int ov2640_get_caps(const struct device *dev, return 0; } -static int ov2640_set_ctrl(const struct device *dev, struct video_control *ctrl) +static int ov2640_set_ctrl(const struct device *dev, uint32_t id) { - switch (ctrl->id) { + struct ov2640_data *drv_data = dev->data; + struct ov2640_ctrls *ctrls = &drv_data->ctrls; + + switch (id) { case VIDEO_CID_HFLIP: - return ov2640_set_horizontal_mirror(dev, ctrl->val); + return ov2640_set_horizontal_mirror(dev, ctrls->hflip.val); case VIDEO_CID_VFLIP: - return ov2640_set_vertical_flip(dev, ctrl->val); + return ov2640_set_vertical_flip(dev, ctrls->vflip.val); case VIDEO_CID_EXPOSURE: - return ov2640_set_exposure_ctrl(dev, ctrl->val); + return ov2640_set_exposure_ctrl(dev, ctrls->ae.val); case VIDEO_CID_WHITE_BALANCE_TEMPERATURE: - return ov2640_set_white_bal(dev, ctrl->val); + return ov2640_set_white_bal(dev, ctrls->awb.val); case VIDEO_CID_GAIN: - return ov2640_set_gain_ctrl(dev, ctrl->val); + return ov2640_set_gain_ctrl(dev, ctrls->gain.val); case VIDEO_CID_BRIGHTNESS: - return ov2640_set_level(dev, ctrl->val, NUM_BRIGHTNESS_LEVELS, + return ov2640_set_level(dev, ctrls->brightness.val, NUM_BRIGHTNESS_LEVELS, ARRAY_SIZE(brightness_regs[0]), brightness_regs); case VIDEO_CID_CONTRAST: - return ov2640_set_level(dev, ctrl->val, NUM_CONTRAST_LEVELS, + return ov2640_set_level(dev, ctrls->contrast.val, NUM_CONTRAST_LEVELS, ARRAY_SIZE(contrast_regs[0]), contrast_regs); case VIDEO_CID_SATURATION: - return ov2640_set_level(dev, ctrl->val, NUM_SATURATION_LEVELS, + return ov2640_set_level(dev, ctrls->saturation.val, NUM_SATURATION_LEVELS, ARRAY_SIZE(saturation_regs[0]), saturation_regs); case VIDEO_CID_JPEG_COMPRESSION_QUALITY: - return ov2640_set_quality(dev, ctrl->val); + return ov2640_set_quality(dev, ctrls->jpeg.val); case VIDEO_CID_TEST_PATTERN: - return ov2640_set_colorbar(dev, ctrl->val); + return ov2640_set_colorbar(dev, ctrls->test_pattern.val); default: return -ENOTSUP; } diff --git a/drivers/video/ov5640.c b/drivers/video/ov5640.c index 26263643b216..71957326364d 100644 --- a/drivers/video/ov5640.c +++ b/drivers/video/ov5640.c @@ -145,7 +145,11 @@ struct ov5640_mode_config { }; struct ov5640_ctrls { - struct video_ctrl gain; + /* gain auto-cluster */ + struct { + struct video_ctrl auto_gain; + struct video_ctrl gain; + }; struct video_ctrl brightness; struct video_ctrl contrast; struct video_ctrl hue; @@ -1034,24 +1038,30 @@ static int ov5640_set_ctrl_contrast(const struct device *dev, int value) return ov5640_write_reg(&cfg->i2c, SDE_CTRL6_REG, value & 0xff); } -static int ov5640_set_ctrl_gain(const struct device *dev, int value) +static int ov5640_set_ctrl_gain(const struct device *dev) { const struct ov5640_config *cfg = dev->config; + struct ov5640_data *drv_data = dev->data; + struct ov5640_ctrls *ctrls = &drv_data->ctrls; - if (value) { - int ret = ov5640_modify_reg(&cfg->i2c, AEC_PK_MANUAL, BIT(1), BIT(0)); + int ret = ov5640_modify_reg(&cfg->i2c, AEC_PK_MANUAL, BIT(1), + ctrls->auto_gain.val ? 0 : BIT(1)); + + if (ret) { + return ret; + } + if (!ctrls->auto_gain.val) { + ret = ov5640_modify_reg(&cfg->i2c, AEC_PK_REAL_GAIN, 0x03, + (ctrls->gain.val >> 8) & 0x03); if (ret) { return ret; } - struct ov5640_reg gain_params[] = {{AEC_PK_REAL_GAIN, value >> 8}, - {AEC_PK_REAL_GAIN + 1, value & 0xff}}; - - return ov5640_write_multi_regs(&cfg->i2c, gain_params, ARRAY_SIZE(gain_params)); - } else { - return ov5640_write_reg(&cfg->i2c, AEC_PK_MANUAL, 0); + ret = ov5640_write_reg(&cfg->i2c, AEC_PK_REAL_GAIN + 1, ctrls->gain.val & 0xff); } + + return ret; } static int ov5640_set_ctrl_hflip(const struct device *dev, int value) @@ -1096,32 +1106,68 @@ static int ov5640_set_ctrl_power_line_freq(const struct device *dev, int value) return ov5640_modify_reg(&cfg->i2c, HZ5060_CTRL01_REG, BIT(7), BIT(7)); } -static int ov5640_set_ctrl(const struct device *dev, struct video_control *ctrl) +static int ov5640_set_ctrl(const struct device *dev, uint32_t id) { - switch (ctrl->id) { + struct ov5640_data *drv_data = dev->data; + struct ov5640_ctrls *ctrls = &drv_data->ctrls; + + switch (id) { case VIDEO_CID_TEST_PATTERN: - return ov5640_set_ctrl_test_pattern(dev, ctrl->val); + return ov5640_set_ctrl_test_pattern(dev, ctrls->test_pattern.val); case VIDEO_CID_HUE: - return ov5640_set_ctrl_hue(dev, ctrl->val); + return ov5640_set_ctrl_hue(dev, ctrls->hue.val); case VIDEO_CID_SATURATION: - return ov5640_set_ctrl_saturation(dev, ctrl->val); + return ov5640_set_ctrl_saturation(dev, ctrls->saturation.val); case VIDEO_CID_BRIGHTNESS: - return ov5640_set_ctrl_brightness(dev, ctrl->val); + return ov5640_set_ctrl_brightness(dev, ctrls->brightness.val); case VIDEO_CID_CONTRAST: - return ov5640_set_ctrl_contrast(dev, ctrl->val); - case VIDEO_CID_GAIN: - return ov5640_set_ctrl_gain(dev, ctrl->val); + return ov5640_set_ctrl_contrast(dev, ctrls->contrast.val); + case VIDEO_CID_AUTOGAIN: + return ov5640_set_ctrl_gain(dev); case VIDEO_CID_HFLIP: - return ov5640_set_ctrl_hflip(dev, ctrl->val); + return ov5640_set_ctrl_hflip(dev, ctrls->hflip.val); case VIDEO_CID_VFLIP: - return ov5640_set_ctrl_vflip(dev, ctrl->val); + return ov5640_set_ctrl_vflip(dev, ctrls->vflip.val); case VIDEO_CID_POWER_LINE_FREQUENCY: - return ov5640_set_ctrl_power_line_freq(dev, ctrl->val); + return ov5640_set_ctrl_power_line_freq(dev, ctrls->light_freq.val); default: return -ENOTSUP; } } +static int ov5640_get_gain(const struct device *dev) +{ + int ret; + uint16_t gain; + const struct ov5640_config *cfg = dev->config; + + ret = ov5640_read_reg(&cfg->i2c, AEC_PK_REAL_GAIN, &gain, sizeof(gain)); + if (ret) { + return ret; + } + + return gain & 0x3ff; +} + +static int ov5640_get_volatile_ctrl(const struct device *dev, uint32_t id) +{ + int val; + struct ov5640_data *drv_data = dev->data; + struct ov5640_ctrls *ctrls = &drv_data->ctrls; + + switch (id) { + case VIDEO_CID_AUTOGAIN: + val = ov5640_get_gain(dev); + if (val < 0) { + return val; + } + ctrls->gain.val = val; + break; + } + + return 0; +} + static int ov5640_get_frmival(const struct device *dev, enum video_endpoint_id ep, struct video_frmival *frmival) { @@ -1171,6 +1217,7 @@ static DEVICE_API(video, ov5640_driver_api) = { .get_caps = ov5640_get_caps, .set_stream = ov5640_set_stream, .set_ctrl = ov5640_set_ctrl, + .get_volatile_ctrl = ov5640_get_volatile_ctrl, .set_frmival = ov5640_set_frmival, .get_frmival = ov5640_get_frmival, .enum_frmival = ov5640_enum_frmival, @@ -1182,13 +1229,21 @@ static int ov5640_init_controls(const struct device *dev) struct ov5640_data *drv_data = dev->data; struct ov5640_ctrls *ctrls = &drv_data->ctrls; + ret = video_init_ctrl(&ctrls->auto_gain, dev, VIDEO_CID_AUTOGAIN, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 1}); + if (ret) { + return ret; + } + ret = video_init_ctrl( - &ctrls->gain, dev, VIDEO_CID_GAIN, + &ctrls->gain, dev, VIDEO_CID_ANALOGUE_GAIN, (struct video_ctrl_range){.min = 0, .max = 1023, .step = 1, .def = 0}); if (ret) { return ret; } + video_auto_cluster_ctrl(&ctrls->auto_gain, 2, true); + ret = video_init_ctrl( &ctrls->brightness, dev, VIDEO_CID_BRIGHTNESS, (struct video_ctrl_range){.min = -15, .max = 15, .step = 1, .def = 0}); diff --git a/drivers/video/ov7670.c b/drivers/video/ov7670.c index 192a63a79084..17fce01cd5a1 100644 --- a/drivers/video/ov7670.c +++ b/drivers/video/ov7670.c @@ -581,17 +581,19 @@ static int ov7670_set_stream(const struct device *dev, bool enable) return 0; } -static int ov7670_set_ctrl(const struct device *dev, struct video_control *ctrl) +static int ov7670_set_ctrl(const struct device *dev, uint32_t id) { const struct ov7670_config *config = dev->config; + struct ov7670_data *drv_data = dev->data; + struct ov7670_ctrls *ctrls = &drv_data->ctrls; - switch (ctrl->id) { + switch (id) { case VIDEO_CID_HFLIP: - return i2c_reg_update_byte_dt(&config->bus, OV7670_MVFP, - OV7670_MVFP_HFLIP, ctrl->val ? OV7670_MVFP_HFLIP : 0); + return i2c_reg_update_byte_dt(&config->bus, OV7670_MVFP, OV7670_MVFP_HFLIP, + ctrls->hflip.val ? OV7670_MVFP_HFLIP : 0); case VIDEO_CID_VFLIP: - return i2c_reg_update_byte_dt(&config->bus, OV7670_MVFP, - OV7670_MVFP_VFLIP, ctrl->val ? OV7670_MVFP_VFLIP : 0); + return i2c_reg_update_byte_dt(&config->bus, OV7670_MVFP, OV7670_MVFP_VFLIP, + ctrls->vflip.val ? OV7670_MVFP_VFLIP : 0); default: return -ENOTSUP; } diff --git a/drivers/video/video_ctrls.c b/drivers/video/video_ctrls.c index 0dc99820ba5d..ffc84fc94148 100644 --- a/drivers/video/video_ctrls.c +++ b/drivers/video/video_ctrls.c @@ -94,6 +94,10 @@ int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t return ret; } + ctrl->cluster_sz = 0; + ctrl->cluster = NULL; + ctrl->is_auto = false; + ctrl->has_volatiles = false; ctrl->vdev = vdev; ctrl->id = id; ctrl->type = type; @@ -119,6 +123,50 @@ int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t 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); @@ -157,11 +205,15 @@ int video_get_ctrl(const struct device *dev, struct video_control *control) } /* Call driver's get_volatile_ctrl */ - return DEVICE_API_GET(video, ctrl->vdev->dev) - ->get_volatile_ctrl(ctrl->vdev->dev, control); + 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; + } } - /* Read control value in cache memory */ + /* Give the control's current value to user */ if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64) { control->val64 = ctrl->val64; } else { @@ -174,8 +226,10 @@ int video_get_ctrl(const struct device *dev, struct video_control *control) int video_set_ctrl(const struct device *dev, struct video_control *control) { struct video_ctrl *ctrl = NULL; - int ret = video_find_ctrl(dev, control->id, &ctrl); + uint8_t i = 0; + int32_t val = 0; + int64_t val64 = 0; if (ret) { return ret; @@ -186,6 +240,11 @@ int video_set_ctrl(const struct device *dev, struct video_control *control) 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)) { @@ -193,25 +252,81 @@ int video_set_ctrl(const struct device *dev, struct video_control *control) return -EINVAL; } - if (DEVICE_API_GET(video, ctrl->vdev->dev)->set_ctrl == NULL) { - goto update; - } - - /* Call driver's set_ctrl */ - ret = DEVICE_API_GET(video, ctrl->vdev->dev)->set_ctrl(ctrl->vdev->dev, control); - if (ret) { - return ret; + /* No new value */ + if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64 ? ctrl->val64 == control->val64 + : ctrl->val == control->val) { + return 0; } -update: - /* Only update the ctrl in memory once everything is OK */ + /* 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) @@ -228,8 +343,12 @@ static inline const char *video_get_ctrl_name(uint32_t id) return "Hue"; 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: diff --git a/drivers/video/video_ctrls.h b/drivers/video/video_ctrls.h index 4d0262ea849b..b154ad3431af 100644 --- a/drivers/video/video_ctrls.h +++ b/drivers/video/video_ctrls.h @@ -39,6 +39,12 @@ struct video_device; * @see video_control for the struct used in public API */ struct video_ctrl { + /* Fields should not touched by drivers, used only for the 1st control of a cluster */ + struct video_ctrl *cluster; + uint8_t cluster_sz; + bool is_auto; + bool has_volatiles; + const struct video_device *vdev; uint32_t id; enum video_ctrl_type type; @@ -54,4 +60,8 @@ struct video_ctrl { int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id, struct video_ctrl_range range); +void video_cluster_ctrl(struct video_ctrl *ctrls, uint8_t sz); + +void video_auto_cluster_ctrl(struct video_ctrl *ctrls, uint8_t sz, bool set_volatile); + #endif /* ZEPHYR_INCLUDE_DRIVERS_VIDEO_VIDEO_CTRLS_H_ */ diff --git a/drivers/video/video_emul_imager.c b/drivers/video/video_emul_imager.c index d9f659390705..8dffbba11f6b 100644 --- a/drivers/video/video_emul_imager.c +++ b/drivers/video/video_emul_imager.c @@ -193,9 +193,11 @@ static int emul_imager_write_multi(const struct device *const dev, return 0; } -static int emul_imager_set_ctrl(const struct device *dev, struct video_control *ctrl) +static int emul_imager_set_ctrl(const struct device *dev, uint32_t id) { - return emul_imager_write_reg(dev, EMUL_IMAGER_REG_CUSTOM, ctrl->val); + struct emul_imager_data *data = dev->data; + + return emul_imager_write_reg(dev, EMUL_IMAGER_REG_CUSTOM, data->ctrls.custom.val); } /* Customize this function according to your "struct emul_imager_mode". */ diff --git a/include/zephyr/drivers/video-controls.h b/include/zephyr/drivers/video-controls.h index e1b11a4b49fe..73937ba1958e 100644 --- a/include/zephyr/drivers/video-controls.h +++ b/include/zephyr/drivers/video-controls.h @@ -57,7 +57,13 @@ extern "C" { /** Amount of time an image sensor is exposed to light, affecting the brightness */ #define VIDEO_CID_EXPOSURE (VIDEO_CID_BASE + 17) -/** Amount of amplification performed to each pixel electrical signal, affecting the brightness */ +/** Automatic gain control */ +#define VIDEO_CID_AUTOGAIN (VIDEO_CID_BASE + 18) + +/** Gain control. Most devices control only digital gain with this control. + * Devices that recognise the difference between digital and analogue gain use + * VIDEO_CID_DIGITAL_GAIN and VIDEO_CID_ANALOGUE_GAIN. + */ #define VIDEO_CID_GAIN (VIDEO_CID_BASE + 19) /** Flip the image horizontally: the left side becomes the right side */ @@ -137,6 +143,9 @@ enum video_power_line_frequency { */ #define VIDEO_CID_IMAGE_SOURCE_CLASS_BASE 0x009e0900 +/** Analogue gain control. */ +#define VIDEO_CID_ANALOGUE_GAIN (VIDEO_CID_IMAGE_SOURCE_CLASS_BASE + 3) + /** * @} */ diff --git a/include/zephyr/drivers/video.h b/include/zephyr/drivers/video.h index 184492d1952c..c0b814932f74 100644 --- a/include/zephyr/drivers/video.h +++ b/include/zephyr/drivers/video.h @@ -321,9 +321,10 @@ typedef int (*video_api_set_stream_t)(const struct device *dev, bool enable); * @typedef video_api_ctrl_t * @brief Set/Get a video control value. * - * See video_set_ctrl() or video_get_ctrl() for argument descriptions. + * @param dev Pointer to the device structure. + * @param cid Id of the control to set/get its value. */ -typedef int (*video_api_ctrl_t)(const struct device *dev, struct video_control *ctrl); +typedef int (*video_api_ctrl_t)(const struct device *dev, uint32_t cid); /** * @typedef video_api_get_caps_t From 0cd91686a50a03e3e657a22aebb42cb50d07cbc9 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Thu, 10 Apr 2025 19:50:23 +0200 Subject: [PATCH 7/8] drivers: video: Add support for controls of menu types Add support for controls of menu types, standard menu and drivers' defined menu. Rework the ov5640's test pattern and power line frequency controls using this new support. Signed-off-by: Phi Bang Nguyen --- drivers/video/ov5640.c | 23 +++++----- drivers/video/video_ctrls.c | 56 ++++++++++++++++++++++++- drivers/video/video_ctrls.h | 4 ++ include/zephyr/drivers/video-controls.h | 11 +++++ 4 files changed, 83 insertions(+), 11 deletions(-) diff --git a/drivers/video/ov5640.c b/drivers/video/ov5640.c index 71957326364d..c70908a24348 100644 --- a/drivers/video/ov5640.c +++ b/drivers/video/ov5640.c @@ -956,6 +956,15 @@ static const uint8_t test_pattern_val[] = { TEST_PATTERN_ENABLE | TEST_PATTERN_SQUARE | TEST_PATTERN_ROLLING, }; +static const char *const test_pattern_menu[] = { + "Disabled", + "Color bars", + "Color bars with rolling bar", + "Color squares", + "Color squares with rolling bar", + NULL +}; + static int ov5640_set_ctrl_test_pattern(const struct device *dev, int value) { const struct ov5640_config *cfg = dev->config; @@ -1282,20 +1291,14 @@ static int ov5640_init_controls(const struct device *dev) return ret; } - ret = video_init_ctrl( - &ctrls->light_freq, dev, VIDEO_CID_POWER_LINE_FREQUENCY, - (struct video_ctrl_range){.min = VIDEO_CID_POWER_LINE_FREQUENCY_DISABLED, - .max = VIDEO_CID_POWER_LINE_FREQUENCY_AUTO, - .step = 1, - .def = VIDEO_CID_POWER_LINE_FREQUENCY_50HZ}); + ret = video_init_menu_ctrl(&ctrls->light_freq, dev, VIDEO_CID_POWER_LINE_FREQUENCY, + VIDEO_CID_POWER_LINE_FREQUENCY_50HZ, NULL); if (ret) { return ret; } - ret = video_init_ctrl( - &ctrls->test_pattern, dev, VIDEO_CID_TEST_PATTERN, - (struct video_ctrl_range){ - .min = 0, .max = ARRAY_SIZE(test_pattern_val) - 1, .step = 1, .def = 0}); + ret = video_init_menu_ctrl(&ctrls->test_pattern, dev, VIDEO_CID_TEST_PATTERN, 0, + test_pattern_menu); if (ret) { return ret; } diff --git a/drivers/video/video_ctrls.c b/drivers/video/video_ctrls.c index ffc84fc94148..25906f13c31c 100644 --- a/drivers/video/video_ctrls.c +++ b/drivers/video/video_ctrls.c @@ -15,6 +15,24 @@ LOG_MODULE_REGISTER(video_ctrls, CONFIG_VIDEO_LOG_LEVEL); +static inline const char *const *video_get_std_menu_ctrl(uint32_t id) +{ + static const char *const camera_power_line_frequency[] = {"Disabled", "50 Hz", "60 Hz", + "Auto", NULL}; + static const char *const camera_exposure_auto[] = {"Auto Mode", "Manual Mode", + "Shutter Priority Mode", + "Aperture Priority Mode", NULL}; + + switch (id) { + case VIDEO_CID_POWER_LINE_FREQUENCY: + return camera_power_line_frequency; + case VIDEO_CID_EXPOSURE_AUTO: + return camera_exposure_auto; + default: + return NULL; + } +} + static inline int check_range(enum video_ctrl_type type, struct video_ctrl_range range) { switch (type) { @@ -98,6 +116,7 @@ int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t ctrl->cluster = NULL; ctrl->is_auto = false; ctrl->has_volatiles = false; + ctrl->menu = NULL; ctrl->vdev = vdev; ctrl->id = id; ctrl->type = type; @@ -123,6 +142,34 @@ int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t 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; +} + /* 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) { @@ -404,7 +451,7 @@ int video_query_ctrl(const struct device *dev, struct video_ctrl_query *cq) cq->type = ctrl->type; cq->flags = ctrl->flags; cq->range = ctrl->range; - + cq->menu = ctrl->menu; cq->name = video_get_ctrl_name(cq->id); return 0; @@ -457,4 +504,11 @@ void video_print_ctrl(const struct device *const dev, const struct video_ctrl_qu cq->name, cq->id, typebuf, cq->flags, cq->range.min, cq->range.max, cq->range.step, cq->range.def, vc.val); } + + if (cq->menu) { + while (cq->menu[i]) { + LOG_INF("%*s %u: %s", 32, "", i, cq->menu[i]); + i++; + } + } } diff --git a/drivers/video/video_ctrls.h b/drivers/video/video_ctrls.h index b154ad3431af..07c6c5575f3a 100644 --- a/drivers/video/video_ctrls.h +++ b/drivers/video/video_ctrls.h @@ -54,12 +54,16 @@ struct video_ctrl { int32_t val; int64_t val64; }; + const char *const *menu; sys_dnode_t node; }; int video_init_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id, struct video_ctrl_range range); +int video_init_menu_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id, + uint8_t def, const char *const menu[]); + void video_cluster_ctrl(struct video_ctrl *ctrls, uint8_t sz); void video_auto_cluster_ctrl(struct video_ctrl *ctrls, uint8_t sz, bool set_volatile); diff --git a/include/zephyr/drivers/video-controls.h b/include/zephyr/drivers/video-controls.h index 73937ba1958e..4304316c1818 100644 --- a/include/zephyr/drivers/video-controls.h +++ b/include/zephyr/drivers/video-controls.h @@ -107,6 +107,15 @@ enum video_power_line_frequency { */ #define VIDEO_CID_CAMERA_CLASS_BASE 0x009a0900 +/** Adjustments of exposure time and/or iris aperture. */ +#define VIDEO_CID_EXPOSURE_AUTO (VIDEO_CID_CAMERA_CLASS_BASE + 1) +enum video_exposure_auto_type { + VIDEO_EXPOSURE_AUTO = 0, + VIDEO_EXPOSURE_MANUAL = 1, + VIDEO_EXPOSURE_SHUTTER_PRIORITY = 2, + VIDEO_EXPOSURE_APERTURE_PRIORITY = 3 +}; + /** Amount of optical zoom applied through to the camera optics */ #define VIDEO_CID_ZOOM_ABSOLUTE (VIDEO_CID_CAMERA_CLASS_BASE + 13) @@ -256,6 +265,8 @@ struct video_ctrl_query { uint32_t flags; /** control range */ struct video_ctrl_range range; + /** menu if control is of menu type */ + const char *const *menu; }; /** From 4c5e6bea599ba20f8e889b4629c2e4459bdced82 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Tue, 15 Apr 2025 20:24:43 +0200 Subject: [PATCH 8/8] doc: release-notes-4-2: Add changes related to video control Add changes related to the new video control framework Signed-off-by: Phi Bang Nguyen --- doc/releases/release-notes-4.2.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/releases/release-notes-4.2.rst b/doc/releases/release-notes-4.2.rst index 6aed4f0820dc..bbc24f3b84d7 100644 --- a/doc/releases/release-notes-4.2.rst +++ b/doc/releases/release-notes-4.2.rst @@ -69,6 +69,8 @@ Removed APIs and options * Removed :dtcompatible:`meas,ms5837` and replaced with :dtcompatible:`meas,ms5837-30ba` and :dtcompatible:`meas,ms5837-02ba`. +* Removed the ``get_ctrl`` video driver API + Deprecated APIs and options =========================== @@ -179,6 +181,12 @@ New APIs and options * LoRaWAN * :c:func:`lorawan_request_link_check` +* Video + + * :c:func:`video_api_ctrl_t` + * :c:func:`video_query_ctrl` + * :c:func:`video_print_ctrl` + New Boards **********