diff --git a/cmake/linker_script/common/common-ram.cmake b/cmake/linker_script/common/common-ram.cmake index 0dda1ee3c0a2c..d46376edd03fb 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/doc/releases/release-notes-4.2.rst b/doc/releases/release-notes-4.2.rst index 6aed4f0820dc9..bbc24f3b84d72 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 ********** diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index 6210f8d881d07..102a841648e96 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -3,6 +3,8 @@ 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) zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_MIPI_CSI2RX video_mcux_mipi_csi2rx.c) @@ -18,3 +20,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 8ac3cc24033c1..3b6596453ee22 100644 --- a/drivers/video/gc2145.c +++ b/drivers/video/gc2145.c @@ -14,6 +14,10 @@ #include #include + +#include "video_ctrls.h" +#include "video_device.h" + LOG_MODULE_REGISTER(video_gc2145, CONFIG_VIDEO_LOG_LEVEL); #define GC2145_REG_AMODE1 0x17 @@ -689,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; }; @@ -1100,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, unsigned int cid, void *value) +static int gc2145_set_ctrl(const struct device *dev, uint32_t id) { - switch (cid) { + struct gc2145_data *drv_data = dev->data; + + switch (id) { case VIDEO_CID_HFLIP: - return gc2145_set_ctrl_hmirror(dev, (int)value); + return gc2145_set_ctrl_hmirror(dev, drv_data->ctrls.hflip.val); case VIDEO_CID_VFLIP: - return gc2145_set_ctrl_vflip(dev, (int)value); + return gc2145_set_ctrl_vflip(dev, drv_data->ctrls.vflip.val); default: return -ENOTSUP; } @@ -1120,6 +1132,22 @@ 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, + (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, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); +} + static int gc2145_init(const struct device *dev) { struct video_format fmt; @@ -1166,7 +1194,8 @@ static int gc2145_init(const struct device *dev) return ret; } - return 0; + /* Initialize controls */ + return gc2145_init_controls(dev); } /* Unique Instance */ @@ -1208,3 +1237,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 663fa422201e7..16d4e61bb3911 100644 --- a/drivers/video/mt9m114.c +++ b/drivers/video/mt9m114.c @@ -15,6 +15,9 @@ #include #include +#include "video_ctrls.h" +#include "video_device.h" + LOG_MODULE_REGISTER(video_mt9m114, CONFIG_VIDEO_LOG_LEVEL); #define MT9M114_CHIP_ID_VAL 0x2481 @@ -63,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; }; @@ -464,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, unsigned int cid, void *value) +static int mt9m114_set_ctrl(const struct device *dev, uint32_t id) { int ret = 0; + struct mt9m114_data *drv_data = dev->data; - switch (cid) { + 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, - (int)value ? 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, - (int)value ? 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; @@ -499,6 +511,22 @@ 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, + (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, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); +} + static int mt9m114_init(const struct device *dev) { struct video_format fmt; @@ -544,7 +572,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 */ @@ -569,4 +598,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 f89e73dbd9f83..5da0c2d504e1e 100644 --- a/drivers/video/ov2640.c +++ b/drivers/video/ov2640.c @@ -14,6 +14,9 @@ #include #include +#include "video_ctrls.h" +#include "video_device.h" + LOG_MODULE_REGISTER(video_ov2640, CONFIG_VIDEO_LOG_LEVEL); /* DSP register bank FF=0x00*/ @@ -439,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; }; @@ -563,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); @@ -578,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) { @@ -923,47 +895,38 @@ 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, uint32_t id) { - int ret = 0; + struct ov2640_data *drv_data = dev->data; + struct ov2640_ctrls *ctrls = &drv_data->ctrls; - switch (cid) { + switch (id) { case VIDEO_CID_HFLIP: - ret |= ov2640_set_horizontal_mirror(dev, (int)value); - break; + return ov2640_set_horizontal_mirror(dev, ctrls->hflip.val); case VIDEO_CID_VFLIP: - ret |= ov2640_set_vertical_flip(dev, (int)value); - break; + return ov2640_set_vertical_flip(dev, ctrls->vflip.val); case VIDEO_CID_EXPOSURE: - ret |= ov2640_set_exposure_ctrl(dev, (int)value); - break; + return ov2640_set_exposure_ctrl(dev, ctrls->ae.val); + case VIDEO_CID_WHITE_BALANCE_TEMPERATURE: + return ov2640_set_white_bal(dev, ctrls->awb.val); case VIDEO_CID_GAIN: - ret |= ov2640_set_gain_ctrl(dev, (int)value); - break; + return ov2640_set_gain_ctrl(dev, ctrls->gain.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, ctrls->brightness.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, ctrls->contrast.val, NUM_CONTRAST_LEVELS, + ARRAY_SIZE(contrast_regs[0]), contrast_regs); + case VIDEO_CID_SATURATION: + return ov2640_set_level(dev, ctrls->saturation.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, ctrls->jpeg.val); + case VIDEO_CID_TEST_PATTERN: + return ov2640_set_colorbar(dev, ctrls->test_pattern.val); default: return -ENOTSUP; } - - return ret; } static DEVICE_API(video, ov2640_driver_api) = { @@ -974,6 +937,71 @@ 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, + (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, + (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, + (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, + (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, + (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, + (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, + (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, + (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, + (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, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); +} + static int ov2640_init(const struct device *dev) { struct video_format fmt; @@ -1018,7 +1046,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 */ @@ -1062,3 +1095,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 cb567bd48bc02..c70908a243481 100644 --- a/drivers/video/ov5640.c +++ b/drivers/video/ov5640.c @@ -19,6 +19,9 @@ #include #include +#include "video_ctrls.h" +#include "video_device.h" + LOG_MODULE_REGISTER(video_ov5640, CONFIG_VIDEO_LOG_LEVEL); #define CHIP_ID_REG 0x300a @@ -141,7 +144,25 @@ struct ov5640_mode_config { uint16_t def_frmrate; }; +struct ov5640_ctrls { + /* 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; + 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; @@ -793,6 +814,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; @@ -932,14 +956,19 @@ 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; - 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]); } @@ -948,10 +977,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)); @@ -984,10 +1009,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)); @@ -1002,10 +1023,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)); @@ -1021,10 +1038,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) { @@ -1034,28 +1047,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 (!IN_RANGE(value, 0, UINT16_MAX)) { - return -EINVAL; - } + int ret = ov5640_modify_reg(&cfg->i2c, AEC_PK_MANUAL, BIT(1), + ctrls->auto_gain.val ? 0 : BIT(1)); - if (value) { - int ret = ov5640_modify_reg(&cfg->i2c, AEC_PK_MANUAL, BIT(1), BIT(0)); + 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) @@ -1100,44 +1115,66 @@ 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, uint32_t id) { - switch (cid) { + 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, (int)value); + return ov5640_set_ctrl_test_pattern(dev, ctrls->test_pattern.val); case VIDEO_CID_HUE: - return ov5640_set_ctrl_hue(dev, (int)value); + return ov5640_set_ctrl_hue(dev, ctrls->hue.val); case VIDEO_CID_SATURATION: - return ov5640_set_ctrl_saturation(dev, (int)(value)); + return ov5640_set_ctrl_saturation(dev, ctrls->saturation.val); case VIDEO_CID_BRIGHTNESS: - return ov5640_set_ctrl_brightness(dev, (int)(value)); + return ov5640_set_ctrl_brightness(dev, ctrls->brightness.val); case VIDEO_CID_CONTRAST: - return ov5640_set_ctrl_contrast(dev, (int)value); - case VIDEO_CID_GAIN: - return ov5640_set_ctrl_gain(dev, (int)(value)); + 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, (int)(value)); + return ov5640_set_ctrl_hflip(dev, ctrls->hflip.val); case VIDEO_CID_VFLIP: - return ov5640_set_ctrl_vflip(dev, (int)(value)); + return ov5640_set_ctrl_vflip(dev, ctrls->vflip.val); case VIDEO_CID_POWER_LINE_FREQUENCY: - return ov5640_set_ctrl_power_line_freq(dev, (int)(value)); + return ov5640_set_ctrl_power_line_freq(dev, ctrls->light_freq.val); default: return -ENOTSUP; } } -static inline int ov5640_get_ctrl(const struct device *dev, unsigned int cid, void *value) +static int ov5640_get_gain(const struct device *dev) { - struct ov5640_data *drv_data = dev->data; + int ret; + uint16_t gain; + const struct ov5640_config *cfg = dev->config; - switch (cid) { - case VIDEO_CID_PIXEL_RATE: - *((uint64_t *)value) = drv_data->cur_pixrate; + ret = ov5640_read_reg(&cfg->i2c, AEC_PK_REAL_GAIN, &gain, sizeof(gain)); + if (ret) { + return ret; + } - return 0; - default: - return -ENOTSUP; + 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, @@ -1189,12 +1226,93 @@ 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, + .get_volatile_ctrl = ov5640_get_volatile_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->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_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}); + if (ret) { + return ret; + } + + 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, + (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, + (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, + (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, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); + if (ret) { + return ret; + } + + 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_menu_ctrl(&ctrls->test_pattern, dev, VIDEO_CID_TEST_PATTERN, 0, + test_pattern_menu); + if (ret) { + return ret; + } + + return video_init_ctrl( + &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) { const struct ov5640_config *cfg = dev->config; @@ -1318,7 +1436,8 @@ static int ov5640_init(const struct device *dev) return -EIO; } - return 0; + /* Initialize controls */ + return ov5640_init_controls(dev); } #define OV5640_INIT(n) \ @@ -1333,6 +1452,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 37be06a762589..17fce01cd5a1a 100644 --- a/drivers/video/ov7670.c +++ b/drivers/video/ov7670.c @@ -12,6 +12,9 @@ #include #include +#include "video_ctrls.h" +#include "video_device.h" + LOG_MODULE_REGISTER(video_ov7670, CONFIG_VIDEO_LOG_LEVEL); /* Initialization register structure */ @@ -30,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; }; @@ -449,6 +458,22 @@ 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, + (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, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); +} + static int ov7670_init(const struct device *dev) { const struct ov7670_config *config = dev->config; @@ -547,7 +572,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) @@ -555,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, unsigned int cid, void *value) +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 (cid) { + switch (id) { case VIDEO_CID_HFLIP: - return i2c_reg_update_byte_dt(&config->bus, OV7670_MVFP, - OV7670_MVFP_HFLIP, ((int)value) ? 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, ((int)value) ? 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; } @@ -598,6 +626,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 74d1d83401724..9297d4a401d5d 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 0000000000000..935ee1ed302b3 --- /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_ctrls.c b/drivers/video/video_ctrls.c new file mode 100644 index 0000000000000..25906f13c31c6 --- /dev/null +++ b/drivers/video/video_ctrls.c @@ -0,0 +1,514 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +#include "video_ctrls.h" +#include "video_device.h" + +LOG_MODULE_REGISTER(video_ctrls, CONFIG_VIDEO_LOG_LEVEL); + +static inline const char *const *video_get_std_menu_ctrl(uint32_t id) +{ + static 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) { + case VIDEO_CTRL_TYPE_BOOLEAN: + if (range.step != 1 || range.max > 1 || range.min < 0) { + return -ERANGE; + } + return 0; + case VIDEO_CTRL_TYPE_INTEGER: + if (range.step == 0 || range.min > range.max || + !IN_RANGE(range.def, range.min, range.max)) { + return -ERANGE; + } + return 0; + case VIDEO_CTRL_TYPE_INTEGER64: + if (range.step64 == 0 || range.min64 > range.max64 || + !IN_RANGE(range.def64, range.min64, range.max64)) { + return -ERANGE; + } + return 0; + case VIDEO_CTRL_TYPE_MENU: + if (!IN_RANGE(range.min, 0, range.max) || + !IN_RANGE(range.def, range.min, range.max)) { + return -ERANGE; + } + return 0; + default: + return 0; + } +} + +static inline void set_type_flag(uint32_t id, enum video_ctrl_type *type, uint32_t *flags) +{ + *flags = 0; + + switch (id) { + case VIDEO_CID_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, + struct video_ctrl_range range) +{ + int ret; + uint32_t flags; + enum video_ctrl_type type; + struct video_ctrl *vc; + 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, range); + if (ret) { + return ret; + } + + ctrl->cluster_sz = 0; + ctrl->cluster = NULL; + ctrl->is_auto = false; + ctrl->has_volatiles = false; + ctrl->menu = NULL; + ctrl->vdev = vdev; + ctrl->id = id; + ctrl->type = type; + ctrl->flags = flags; + ctrl->range = range; + + if (type == VIDEO_CTRL_TYPE_INTEGER64) { + ctrl->val64 = range.def64; + } else { + ctrl->val = range.def; + } + + /* Insert in an ascending order of ctrl's id */ + SYS_DLIST_FOR_EACH_CONTAINER(&vdev->ctrls, vc, node) { + if (vc->id > ctrl->id) { + sys_dlist_insert(&vc->node, &ctrl->node); + return 0; + } + } + + sys_dlist_append(&vdev->ctrls, &ctrl->node); + + return 0; +} + +int video_init_menu_ctrl(struct video_ctrl *ctrl, const struct device *dev, uint32_t id, + uint8_t def, const char *const menu[]) +{ + int ret; + uint8_t sz = 0; + const char *const *_menu = menu ? menu : video_get_std_menu_ctrl(id); + + if (!_menu) { + return -EINVAL; + } + + while (_menu[sz]) { + sz++; + } + + ret = video_init_ctrl( + ctrl, dev, id, + (struct video_ctrl_range){.min = 0, .max = sz - 1, .step = 1, .def = def}); + + if (ret) { + return ret; + } + + ctrl->menu = _menu; + + return 0; +} + +/* By definition, the cluster is in manual mode if the master control value is 0 */ +static inline bool is_cluster_manual(const struct video_ctrl *master) +{ + return master->type == VIDEO_CTRL_TYPE_INTEGER64 ? master->val64 == 0 : master->val == 0; +} + +void video_cluster_ctrl(struct video_ctrl *ctrls, uint8_t sz) +{ + bool has_volatiles = false; + + __ASSERT(!sz && !ctrls, "The 1st control, i.e. the master, must not be NULL"); + + for (uint8_t i = 0; i < sz; i++) { + ctrls[i].cluster_sz = sz; + ctrls[i].cluster = ctrls; + if (ctrls[i].flags & VIDEO_CTRL_FLAG_VOLATILE) { + has_volatiles = true; + } + } + + ctrls->has_volatiles = has_volatiles; +} + +void video_auto_cluster_ctrl(struct video_ctrl *ctrls, uint8_t sz, bool set_volatile) +{ + video_cluster_ctrl(ctrls, sz); + + __ASSERT(sz > 1, "Control auto cluster size must be > 1"); + __ASSERT(!(set_volatile && !DEVICE_API_GET(video, ctrls->vdev->dev)->get_volatile_ctrl), + "Volatile is set but no ops"); + + ctrls->is_auto = true; + ctrls->has_volatiles = set_volatile; + ctrls->flags |= VIDEO_CTRL_FLAG_UPDATE; + + /* If the cluster is in automatic mode, mark all manual controls inactive and volatile */ + for (uint8_t i = 1; i < sz; i++) { + if (!is_cluster_manual(ctrls)) { + ctrls[i].flags |= VIDEO_CTRL_FLAG_INACTIVE | + (set_volatile ? VIDEO_CTRL_FLAG_VOLATILE : 0); + } + } +} + +static int video_find_ctrl(const struct device *dev, uint32_t id, struct video_ctrl **ctrl) +{ + struct video_device *vdev = video_find_vdev(dev); + + while (vdev) { + SYS_DLIST_FOR_EACH_CONTAINER(&vdev->ctrls, *ctrl, node) { + if ((*ctrl)->id == id) { + return 0; + } + } + + vdev = video_find_vdev(vdev->src_dev); + } + + return -ENOTSUP; +} + +int video_get_ctrl(const struct device *dev, struct video_control *control) +{ + struct video_ctrl *ctrl = NULL; + + int ret = video_find_ctrl(dev, control->id, &ctrl); + + if (ret) { + return ret; + } + + if (ctrl->flags & VIDEO_CTRL_FLAG_WRITE_ONLY) { + LOG_ERR("Control id 0x%x is write-only\n", control->id); + return -EACCES; + } + + if (ctrl->flags & VIDEO_CTRL_FLAG_VOLATILE) { + if (DEVICE_API_GET(video, ctrl->vdev->dev)->get_volatile_ctrl == NULL) { + return -ENOSYS; + } + + /* Call driver's get_volatile_ctrl */ + ret = DEVICE_API_GET(video, ctrl->vdev->dev) + ->get_volatile_ctrl(ctrl->vdev->dev, + ctrl->cluster ? ctrl->cluster->id : ctrl->id); + if (ret) { + return ret; + } + } + + /* Give the control's current value to user */ + if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64) { + control->val64 = ctrl->val64; + } else { + control->val = ctrl->val; + } + + return 0; +} + +int video_set_ctrl(const struct device *dev, struct video_control *control) +{ + struct video_ctrl *ctrl = NULL; + int ret = video_find_ctrl(dev, control->id, &ctrl); + uint8_t i = 0; + int32_t val = 0; + int64_t val64 = 0; + + if (ret) { + return ret; + } + + if (ctrl->flags & VIDEO_CTRL_FLAG_READ_ONLY) { + LOG_ERR("Control id 0x%x is read-only\n", control->id); + return -EACCES; + } + + if (ctrl->flags & VIDEO_CTRL_FLAG_INACTIVE) { + LOG_ERR("Control id 0x%x is inactive\n", control->id); + return -EACCES; + } + + if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64 + ? !IN_RANGE(control->val64, ctrl->range.min64, ctrl->range.max64) + : !IN_RANGE(control->val, ctrl->range.min, ctrl->range.max)) { + LOG_ERR("Control value is invalid\n"); + return -EINVAL; + } + + /* No new value */ + if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64 ? ctrl->val64 == control->val64 + : ctrl->val == control->val) { + return 0; + } + + /* Backup the control's value then set it to the new value */ + if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64) { + val64 = ctrl->val64; + ctrl->val64 = control->val64; + } else { + val = ctrl->val; + ctrl->val = control->val; + } + + /* + * For auto-clusters having volatiles, before switching to manual mode, get the current + * volatile values since those will become the initial manual values after this switch. + */ + if (ctrl == ctrl->cluster && ctrl->is_auto && ctrl->has_volatiles && + is_cluster_manual(ctrl)) { + + if (DEVICE_API_GET(video, ctrl->vdev->dev)->get_volatile_ctrl == NULL) { + ret = -ENOSYS; + goto restore; + } + + ret = DEVICE_API_GET(video, ctrl->vdev->dev) + ->get_volatile_ctrl(ctrl->vdev->dev, ctrl->id); + if (ret) { + goto restore; + } + } + + /* Call driver's set_ctrl */ + if (DEVICE_API_GET(video, ctrl->vdev->dev)->set_ctrl) { + ret = DEVICE_API_GET(video, ctrl->vdev->dev) + ->set_ctrl(ctrl->vdev->dev, ctrl->cluster ? ctrl->cluster->id : ctrl->id); + if (ret) { + goto restore; + } + } + + /* Update the manual controls' flags of the cluster */ + if (ctrl->cluster && ctrl->cluster->is_auto) { + for (i = 1; i < ctrl->cluster_sz; i++) { + if (!is_cluster_manual(ctrl->cluster)) { + /* Automatic mode: set the inactive and volatile flags of the manual + * controls + */ + ctrl->cluster[i].flags |= + VIDEO_CTRL_FLAG_INACTIVE | + (ctrl->cluster->has_volatiles ? VIDEO_CTRL_FLAG_VOLATILE + : 0); + } else { + /* Manual mode: clear the inactive and volatile flags of the manual + * controls + */ + ctrl->cluster[i].flags &= + ~(VIDEO_CTRL_FLAG_INACTIVE | VIDEO_CTRL_FLAG_VOLATILE); + } + } + } + + return 0; + +restore: + /* Restore the old control's value */ + if (ctrl->type == VIDEO_CTRL_TYPE_INTEGER64) { + ctrl->val64 = val64; + } else { + ctrl->val = val; + } + + return ret; +} + +static inline const char *video_get_ctrl_name(uint32_t id) +{ + switch (id) { + /* User controls */ + case VIDEO_CID_BRIGHTNESS: + return "Brightness"; + case VIDEO_CID_CONTRAST: + return "Contrast"; + case VIDEO_CID_SATURATION: + return "Saturation"; + case VIDEO_CID_HUE: + return "Hue"; + case VIDEO_CID_EXPOSURE: + return "Exposure"; + case VIDEO_CID_AUTOGAIN: + return "Gain, Automatic"; + case VIDEO_CID_GAIN: + return "Gain"; + case VIDEO_CID_ANALOGUE_GAIN: + return "Analogue Gain"; + case VIDEO_CID_HFLIP: + return "Horizontal Flip"; + case VIDEO_CID_VFLIP: + return "Vertical Flip"; + case VIDEO_CID_POWER_LINE_FREQUENCY: + return "Power Line Frequency"; + + /* 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->range = ctrl->range; + cq->menu = ctrl->menu; + 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); + } + + 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 new file mode 100644 index 0000000000000..07c6c5575f3a1 --- /dev/null +++ b/drivers/video/video_ctrls.h @@ -0,0 +1,71 @@ +/* + * 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 { + /* 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; + unsigned long flags; + struct video_ctrl_range range; + union { + 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); + +#endif /* ZEPHYR_INCLUDE_DRIVERS_VIDEO_VIDEO_CTRLS_H_ */ diff --git a/drivers/video/video_device.c b/drivers/video/video_device.c new file mode 100644 index 0000000000000..a895fed42f144 --- /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 0000000000000..3ba2ee3486cda --- /dev/null +++ b/drivers/video/video_device.h @@ -0,0 +1,28 @@ +/* + * 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 +#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); + +#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 c7e26a9ff54f6..8dffbba11f6bc 100644 --- a/drivers/video/video_emul_imager.c +++ b/drivers/video/video_emul_imager.c @@ -17,6 +17,9 @@ #include #include +#include "video_ctrls.h" +#include "video_device.h" + LOG_MODULE_REGISTER(video_emul_imager, CONFIG_VIDEO_LOG_LEVEL); #define EMUL_IMAGER_REG_SENSOR_ID 0x0000 @@ -63,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)]; @@ -70,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 */ @@ -163,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) { @@ -196,24 +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, unsigned int cid, void *value) +static int emul_imager_set_ctrl(const struct device *dev, uint32_t id) { - switch (cid) { - case EMUL_IMAGER_CID_CUSTOM: - return emul_imager_write_reg(dev, EMUL_IMAGER_REG_CUSTOM, (int)value); - default: - return -ENOTSUP; - } -} + struct emul_imager_data *data = dev->data; -static int emul_imager_get_ctrl(const struct device *dev, unsigned int cid, void *value) -{ - 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, data->ctrls.custom.val); } /* Customize this function according to your "struct emul_imager_mode". */ @@ -371,7 +355,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, @@ -381,6 +364,15 @@ 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, + (struct video_ctrl_range){.min = 0, .max = 255, .step = 1, .def = 128}); +} + int emul_imager_init(const struct device *dev) { struct video_format fmt; @@ -415,7 +407,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) \ @@ -427,6 +420,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 2abb6fef32aa3..ba44a2c59ed66 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 { @@ -29,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) { @@ -246,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, @@ -296,6 +280,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 4c93f24bcd12b..5d1ec69f24218 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 @@ -325,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; @@ -432,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 @@ -463,6 +450,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 34f80b8e2c06f..4b4a46dc2cbd3 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; @@ -298,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) { @@ -468,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, @@ -506,4 +480,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 4c9dfa6135ee7..be6ff4595f3af 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) @@ -51,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 = {.id = VIDEO_CID_PIXEL_RATE, .val64 = -1}; ret = video_get_format(config->sensor_dev, ep, &fmt); if (ret) { @@ -62,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) { - LOG_ERR("Sensor pixel rate is not supported"); + video_get_ctrl(config->sensor_dev, &sensor_rate); + 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_pixel_rate * 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) { @@ -94,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.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_pixel_rate); + (clock_control_subsys_rate_t)(uint32_t)sensor_rate.val64); if (ret) { return ret; } @@ -106,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.val64) < ABS(tHsSettleEscClk_configs[i].pixel_rate, best_match)) { best_match = tHsSettleEscClk_configs[i].pixel_rate; ind = i; @@ -180,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) { @@ -239,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 = {.id = VIDEO_CID_PIXEL_RATE, .val64 = -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"); @@ -266,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.val64, drv_data->csi2rxConfig.laneNum); if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { return -EINVAL; @@ -277,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.val64, drv_data->csi2rxConfig.laneNum); if (est_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { return -EINVAL; @@ -285,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.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) * cur_pixel_rate * + (mipi_csi2rx_cal_frame_size(fie->format) * sensor_rate.val64 * cur_frmival.numerator); fie->stepwise.max.numerator = 1; } @@ -305,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, @@ -352,6 +337,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 d6f04ad81d368..9081bcb596741 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 7369b3a39f577..35c55449a5580 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 @@ -352,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, @@ -381,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) @@ -521,3 +499,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 d9f0d90e8e0ee..ed8ade5270ca5 100644 --- a/drivers/video/video_sw_generator.c +++ b/drivers/video/video_sw_generator.c @@ -11,6 +11,9 @@ #include #include +#include "video_ctrls.h" +#include "video_device.h" + LOG_MODULE_REGISTER(video_sw_generator, CONFIG_VIDEO_LOG_LEVEL); #define VIDEO_PATTERN_COLOR_BAR 0 @@ -24,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; }; @@ -125,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]; @@ -255,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) { @@ -340,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, @@ -357,6 +346,14 @@ 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, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); +} + static int video_sw_generator_init(const struct device *dev) { struct video_sw_generator_data *data = dev->data; @@ -366,9 +363,11 @@ 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, &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); diff --git a/include/zephyr/drivers/video-controls.h b/include/zephyr/drivers/video-controls.h index b67e5a8771abd..4304316c18189 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 @@ -54,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 */ @@ -75,6 +84,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) + /** * @} */ @@ -95,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) @@ -131,6 +152,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) + /** * @} */ @@ -157,6 +181,94 @@ 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 + +/** + * @} + */ + +/** + * @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 */ + 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; + }; +}; + +/** + * @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 range */ + struct video_ctrl_range range; + /** menu if control is of menu type */ + const char *const *menu; +}; + /** * @} */ diff --git a/include/zephyr/drivers/video.h b/include/zephyr/drivers/video.h index f0632959e36e5..c0b814932f74c 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 @@ -315,20 +318,13 @@ 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. - * - * 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. + * @typedef video_api_ctrl_t + * @brief Set/Get a video control value. * - * See 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_get_ctrl_t)(const struct device *dev, unsigned int cid, void *value); +typedef int (*video_api_ctrl_t)(const struct device *dev, uint32_t cid); /** * @typedef video_api_get_caps_t @@ -358,8 +354,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_get_ctrl_t get_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; @@ -653,24 +649,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 +664,50 @@ 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; +int video_get_ctrl(const struct device *dev, struct video_control *control); - if (api->get_ctrl == NULL) { - return -ENOSYS; - } +struct video_ctrl_query; - return api->get_ctrl(dev, cid, value); -} +/** + * @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 22a1a743d7f2c..501ebd8794ab5 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,13 +175,26 @@ 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 = {.id = VIDEO_CID_HFLIP, .val = 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 ab57c9a52a5ef..201e0a8bed904 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 = {.id = VIDEO_CID_HFLIP, .val = 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 141627dd414c8..cd58bbf0506b6 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 = {.id = VIDEO_CID_PRIVATE_BASE + 0x01, .val = 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)