diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index e678071bdeca..d9087e5c82eb 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -2,7 +2,10 @@ zephyr_library() +zephyr_library_sources(video_async.c) 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) diff --git a/drivers/video/ov5640.c b/drivers/video/ov5640.c index a1d93ab67e89..cdc390de52a5 100644 --- a/drivers/video/ov5640.c +++ b/drivers/video/ov5640.c @@ -17,6 +17,9 @@ #include #include +#include "video_async.h" +#include "video_ctrls.h" + LOG_MODULE_REGISTER(video_ov5640, CONFIG_VIDEO_LOG_LEVEL); #define CHIP_ID_REG 0x300a @@ -137,7 +140,22 @@ struct ov5640_mode_config { uint16_t def_frmrate; }; +struct ov5640_ctrls { + struct video_ctrl_handler handler; + 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 video_notifier notifier; + struct ov5640_ctrls ctrls; struct video_format fmt; uint64_t cur_pixrate; uint16_t cur_frmrate; @@ -543,6 +561,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; @@ -677,10 +698,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]); } @@ -689,10 +706,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)); @@ -725,10 +738,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)); @@ -743,12 +752,8 @@ 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}}; + {SDE_CTRL7_REG, (abs(value) << 4) & 0xf0}}; int ret = ov5640_modify_reg(&cfg->i2c, SDE_CTRL0_REG, BIT(2), BIT(2)); if (ret) { @@ -762,10 +767,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) { @@ -841,41 +842,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; } @@ -916,6 +903,67 @@ static int ov5640_enum_frmival(const struct device *dev, enum video_endpoint_id return 0; } +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; + + video_ctrl_handler_init(&ctrls->handler, dev); + + ret = video_ctrl_init(&ctrls->brightness, &ctrls->handler, VIDEO_CID_BRIGHTNESS, -15, 15, 1, + 0); + if (ret) { + return ret; + } + + ret = video_ctrl_init(&ctrls->contrast, &ctrls->handler, VIDEO_CID_CONTRAST, 0, 255, 1, 0); + if (ret) { + return ret; + } + + ret = video_ctrl_init(&ctrls->hue, &ctrls->handler, VIDEO_CID_HUE, 0, 359, 1, 0); + if (ret) { + return ret; + } + + ret = video_ctrl_init(&ctrls->saturation, &ctrls->handler, VIDEO_CID_SATURATION, 0, 255, 1, + 64); + if (ret) { + return ret; + } + + ret = video_ctrl_init(&ctrls->hflip, &ctrls->handler, VIDEO_CID_HFLIP, 0, 1, 1, 0); + if (ret) { + return ret; + } + + ret = video_ctrl_init(&ctrls->vflip, &ctrls->handler, VIDEO_CID_VFLIP, 0, 1, 1, 0); + if (ret) { + return ret; + } + + ret = video_ctrl_init(&ctrls->light_freq, &ctrls->handler, 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_ctrl_init(&ctrls->test_pattern, &ctrls->handler, VIDEO_CID_TEST_PATTERN, 0, + ARRAY_SIZE(test_pattern_val) - 1, 1, 0); + if (ret) { + return ret; + } + + return video_ctrl_init( + &ctrls->pixel_rate, &ctrls->handler, 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 const struct video_driver_api ov5640_driver_api = { .set_format = ov5640_set_fmt, .get_format = ov5640_get_fmt, @@ -923,12 +971,20 @@ static const struct video_driver_api ov5640_driver_api = { .stream_start = ov5640_stream_start, .stream_stop = ov5640_stream_stop, .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_async_register(const struct device *dev) +{ + struct ov5640_data *drv_data = dev->data; + + video_async_init(&drv_data->notifier, dev, NULL, &drv_data->ctrls.handler, NULL, 0); + + return video_async_register(&drv_data->notifier); +} + static int ov5640_init(const struct device *dev) { const struct ov5640_config *cfg = dev->config; @@ -1026,7 +1082,15 @@ static int ov5640_init(const struct device *dev) return -EIO; } - return 0; + /* Initialize controls */ + ret = ov5640_init_controls(dev); + if (ret) { + LOG_ERR("Unable to init controls"); + return ret; + } + + /* Register async */ + return ov5640_async_register(dev); } #define OV5640_INIT(n) \ diff --git a/drivers/video/video_async.c b/drivers/video/video_async.c new file mode 100644 index 000000000000..916282727366 --- /dev/null +++ b/drivers/video/video_async.c @@ -0,0 +1,182 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "video_async.h" +#include "video_ctrls.h" +#include "video_device.h" + +#include +#include +#include + +LOG_MODULE_REGISTER(video_async, CONFIG_VIDEO_LOG_LEVEL); + +static sys_slist_t notifiers_list; + +/* Find a registered notifier associated with this device */ +struct video_notifier *video_async_find_notifier(const struct device *dev) +{ + struct video_notifier *nf; + + SYS_SLIST_FOR_EACH_CONTAINER(¬ifiers_list, nf, node) { + if (nf->dev == dev) { + return nf; + } + } + + return NULL; +} + +/* Find the video device of the notifier tree that this notifier is part of */ +static struct video_device *video_async_find_vdev(struct video_notifier *notifier) +{ + if (!notifier) { + return NULL; + } + + while (notifier->parent) { + notifier = notifier->parent; + } + + return notifier->vdev; +} + +/* Add all descendants' control handlers of this notifier to the video device */ +static void add_children_ctrl_handlers_to_vdev(struct video_device *vdev, struct video_notifier *nf) +{ + if (!vdev || !nf) { + return; + } + + nf->vdev = vdev; + + video_ctrl_handler_add(&vdev->ctrl_handler, nf->ctrl_handler); + + for (uint8_t i = 0; i < nf->children_num; ++i) { + struct video_notifier *nf_child = video_async_find_notifier(nf->children_devs[i]); + + if (!nf_child) { + continue; + } + + nf_child->vdev = vdev; + + video_ctrl_handler_add(&vdev->ctrl_handler, nf_child->ctrl_handler); + + add_children_ctrl_handlers_to_vdev(vdev, nf_child); + } +} + +/* Remove all descendants' control handlers of this notifier from the video device */ +static void remove_children_ctrl_handlers_from_vdev(struct video_device *vdev, + struct video_notifier *nf) +{ + if (!vdev || !nf) { + return; + } + + nf->vdev = NULL; + + video_ctrl_handler_remove(&vdev->ctrl_handler, nf->ctrl_handler); + + for (uint8_t i = 0; i < nf->children_num; ++i) { + struct video_notifier *nf_child = video_async_find_notifier(nf->children_devs[i]); + + if (!nf_child) { + continue; + } + + nf_child->vdev = NULL; + + video_ctrl_handler_remove(&vdev->ctrl_handler, nf_child->ctrl_handler); + + remove_children_ctrl_handlers_from_vdev(vdev, nf_child); + } +} + +void video_async_init(struct video_notifier *notifier, const struct device *dev, + struct video_device *vdev, struct video_ctrl_handler *hdl, + const struct device **children_devs, uint8_t children_num) +{ + if (!notifier) { + return; + } + + notifier->dev = dev; + notifier->vdev = vdev; + notifier->ctrl_handler = hdl; + notifier->children_devs = (const struct device **)children_devs; + notifier->children_num = children_num; +} + +int video_async_register(struct video_notifier *notifier) +{ + uint8_t i = 0; + bool found_parent = false; + struct video_notifier *nf; + struct video_device *vdev = NULL; + + if (!notifier) { + return -EINVAL; + } + + if (sys_slist_find(¬ifiers_list, ¬ifier->node, NULL)) { + LOG_ERR("Notifier of device %s is already registered", notifier->dev->name); + return -EALREADY; + } + + SYS_SLIST_FOR_EACH_CONTAINER(¬ifiers_list, nf, node) { + /* Find the parent notifier in the global notifiers registry */ + if (!found_parent) { + for (i = 0; i < nf->children_num; ++i) { + if (nf->children_devs[i] == notifier->dev) { + notifier->parent = nf; + + vdev = video_async_find_vdev(notifier); + if (vdev) { + notifier->vdev = vdev; + video_ctrl_handler_add(&vdev->ctrl_handler, + notifier->ctrl_handler); + } + + found_parent = true; + break; + } + } + } + + /* Find all children notifiers in the notifiers registry */ + for (i = 0; i < notifier->children_num; ++i) { + if (notifier->children_devs[i] == nf->dev) { + nf->parent = notifier; + + add_children_ctrl_handlers_to_vdev(vdev, nf); + + break; + } + } + } + + /* Add the notifier to the notifiers registry */ + sys_slist_append(¬ifiers_list, ¬ifier->node); + + LOG_DBG("Notifier of device %s is registered", notifier->dev->name); + + return 0; +} + +void video_async_unregister(struct video_notifier *notifier) +{ + if (!notifier) { + return; + } + + if (sys_slist_find_and_remove(¬ifiers_list, ¬ifier->node)) { + struct video_device *vdev = video_async_find_vdev(notifier); + + remove_children_ctrl_handlers_from_vdev(vdev, notifier); + } +} diff --git a/drivers/video/video_async.h b/drivers/video/video_async.h new file mode 100644 index 000000000000..2fb2472a255f --- /dev/null +++ b/drivers/video/video_async.h @@ -0,0 +1,37 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_VIDEO_ASYNC_H_ +#define ZEPHYR_INCLUDE_VIDEO_ASYNC_H_ + +#include +#include + +struct video_device; + +struct video_ctrl_handler; + +struct video_notifier { + struct video_device *vdev; + const struct device *dev; + struct video_ctrl_handler *ctrl_handler; + struct video_notifier *parent; + const struct device **children_devs; + uint8_t children_num; + sys_snode_t node; +}; + +void video_async_init(struct video_notifier *notifier, const struct device *dev, + struct video_device *vdev, struct video_ctrl_handler *hdl, + const struct device **children_devs, uint8_t children_num); + +int video_async_register(struct video_notifier *notifier); + +void video_async_unregister(struct video_notifier *notifier); + +struct video_notifier *video_async_find_notifier(const struct device *dev); + +#endif /* ZEPHYR_INCLUDE_VIDEO_ASYNC_H_ */ diff --git a/drivers/video/video_ctrls.c b/drivers/video/video_ctrls.c new file mode 100644 index 000000000000..e2b69c06b537 --- /dev/null +++ b/drivers/video/video_ctrls.c @@ -0,0 +1,241 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "video_async.h" +#include "video_ctrls.h" +#include "video_device.h" + +LOG_MODULE_REGISTER(video_ctrls, CONFIG_VIDEO_LOG_LEVEL); + +void video_ctrl_handler_init(struct video_ctrl_handler *hdl, const struct device *dev) +{ + if (!hdl) { + return; + } + + hdl->dev = dev; + + sys_slist_init(&hdl->list); +} + +void video_ctrl_handler_free(struct video_ctrl_handler *hdl) +{ + if (!hdl) { + return; + } + + hdl->dev = NULL; + + while (sys_slist_get(&hdl->list)) { + } +} + +void video_ctrl_handler_add(struct video_ctrl_handler *hdl, struct video_ctrl_handler *added) +{ + if (!hdl || !added || hdl == added || sys_slist_is_empty(&added->list)) { + return; + } + + sys_slist_append_list(&hdl->list, sys_slist_peek_head(&added->list), + sys_slist_peek_tail(&added->list)); +} + +void video_ctrl_handler_remove(struct video_ctrl_handler *hdl, struct video_ctrl_handler *removed) +{ + sys_snode_t *node; + + if (!hdl || !removed || sys_slist_is_empty(&hdl->list) || + sys_slist_is_empty(&removed->list)) { + return; + } + + SYS_SLIST_FOR_EACH_NODE(&removed->list, node) { + sys_slist_find_and_remove(&hdl->list, node); + } +} + +static inline int check_range(enum video_ctrl_type type, int32_t min, int32_t max, uint32_t step, + uint32_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 || def < min || def > max) { + return -ERANGE; + } + return 0; + case VIDEO_CTRL_TYPE_MENU: + case VIDEO_CTRL_TYPE_INTEGER_MENU: + if (min > max || def < min || def > max || min < 0) { + 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_ctrl_init(struct video_ctrl *ctrl, struct video_ctrl_handler *hdl, uint32_t id, + int32_t min, int32_t max, uint32_t step, int32_t def) +{ + int ret; + enum video_ctrl_type type; + uint32_t flags; + + set_type_flag(id, &type, &flags); + + /* Sanity checks */ + if (id == 0 || id >= VIDEO_CID_PRIVATE_BASE) { + return -EINVAL; + } + + ret = check_range(type, min, max, step, def); + if (ret) { + return ret; + } + + ctrl->handler = hdl; + + 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_slist_append(&hdl->list, &ctrl->node); + + return 0; +} + +struct video_ctrl *video_ctrl_find(struct video_ctrl_handler *hdl, uint32_t id) +{ + struct video_ctrl *ctrl; + + SYS_SLIST_FOR_EACH_CONTAINER(&hdl->list, ctrl, node) { + if (ctrl->id == id) { + return ctrl; + } + } + + return NULL; +} + +static int video_ctrl_find_by_root_dev(const struct device *dev, uint32_t id, + struct video_ctrl **ctrl) +{ + struct video_device *vdev = video_find_vdev(dev); + + if (!vdev) { + return -EINVAL; + } + + struct video_ctrl_handler *hdl = &vdev->ctrl_handler; + + if (!hdl) { + LOG_ERR("Control handler not found\n"); + return -ENOTSUP; + } + + *ctrl = video_ctrl_find(hdl, id); + if (!*ctrl) { + LOG_ERR("Control id 0x%x not found\n", id); + return -ENOTSUP; + } + + return 0; +} + +int video_get_ctrl(const struct device *dev, struct video_control *control) +{ + struct video_ctrl *ctrl; + + int ret = video_ctrl_find_by_root_dev(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; + + int ret = video_ctrl_find_by_root_dev(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 (control->val < ctrl->min || control->val > ctrl->max) { + LOG_ERR("Control value is invalid\n"); + return -EINVAL; + } + + const struct video_driver_api *api = (const struct video_driver_api *)dev->api; + + if (api->set_ctrl == NULL) { + return -ENOSYS; + } + + ret = api->set_ctrl(dev, control); + if (ret) { + return ret; + } + + /* 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..42701904cd93 --- /dev/null +++ b/drivers/video/video_ctrls.h @@ -0,0 +1,60 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_VIDEO_CTRLS_H_ +#define ZEPHYR_INCLUDE_VIDEO_CTRLS_H_ + +#include +#include + +/* Control flags */ +#define VIDEO_CTRL_FLAG_READ_ONLY 0x0004 +#define VIDEO_CTRL_FLAG_WRITE_ONLY 0x0040 +#define VIDEO_CTRL_FLAG_VOLATILE 0x0080 + +/* Control types */ +enum video_ctrl_type { + VIDEO_CTRL_TYPE_INTEGER = 1, + VIDEO_CTRL_TYPE_BOOLEAN = 2, + VIDEO_CTRL_TYPE_MENU = 3, + VIDEO_CTRL_TYPE_BUTTON = 4, + VIDEO_CTRL_TYPE_INTEGER64 = 5, + VIDEO_CTRL_TYPE_CTRL_CLASS = 6, + VIDEO_CTRL_TYPE_STRING = 7, + VIDEO_CTRL_TYPE_BITMASK = 8, + VIDEO_CTRL_TYPE_INTEGER_MENU = 9, +}; + +struct video_ctrl_handler { + const struct device *dev; + sys_slist_t list; +}; + +struct video_ctrl { + struct video_ctrl_handler *handler; + + uint32_t id; + enum video_ctrl_type type; + unsigned long flags; + int32_t min, max, def, val; + uint32_t step; + + sys_snode_t node; +}; + +void video_ctrl_handler_init(struct video_ctrl_handler *hdl, const struct device *dev); + +void video_ctrl_handler_free(struct video_ctrl_handler *hdl); + +void video_ctrl_handler_add(struct video_ctrl_handler *hdl, struct video_ctrl_handler *added); + +void video_ctrl_handler_remove(struct video_ctrl_handler *hdl, struct video_ctrl_handler *removed); + +int video_ctrl_init(struct video_ctrl *ctrl, struct video_ctrl_handler *hdl, uint32_t id, + int32_t min, int32_t max, uint32_t step, int32_t def); + +struct video_ctrl *video_ctrl_find(struct video_ctrl_handler *hdl, uint32_t id); + +#endif /* ZEPHYR_INCLUDE_VIDEO_CTRLS_H_ */ diff --git a/drivers/video/video_device.c b/drivers/video/video_device.c new file mode 100644 index 000000000000..5c7ff64647c8 --- /dev/null +++ b/drivers/video/video_device.c @@ -0,0 +1,84 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "video_device.h" + +#include + +LOG_MODULE_REGISTER(video_device, CONFIG_VIDEO_LOG_LEVEL); + +#define VIDEO_NUM_DEVICES 256 + +static struct video_device *video_devices[VIDEO_NUM_DEVICES]; + +static bool video_is_vdev_registered(struct video_device *vdev) +{ + for (uint8_t i = 0; i < VIDEO_NUM_DEVICES; ++i) { + if (!video_devices[i] && video_devices[i] == vdev) { + return true; + } + } + + return false; +} + +int video_register_vdev(struct video_device *vdev, const struct device *dev) +{ + uint8_t i; + + if (!vdev) { + return -EINVAL; + } + + if (video_is_vdev_registered(vdev)) { + LOG_ERR("Video device %s is already registered at index %d", vdev->dev->name, + vdev->ind); + return -EALREADY; + } + + vdev->dev = dev; + video_ctrl_handler_init(&vdev->ctrl_handler, dev); + + for (i = 0; i < VIDEO_NUM_DEVICES; ++i) { + if (!video_devices[i]) { + video_devices[i] = vdev; + vdev->ind = i; + break; + } + } + + if (i == VIDEO_NUM_DEVICES) { + LOG_ERR("No space left to register a new video device"); + vdev->ind = -1; + return -ENOSPC; + } + + return 0; +} + +void video_unregister_vdev(struct video_device *vdev) +{ + if (!video_is_vdev_registered(vdev)) { + LOG_ERR("Video device %s, index = %d is not registered", vdev->dev->name, + vdev->ind); + return; + } + + video_devices[vdev->ind] = NULL; + vdev->ind = -1; + video_ctrl_handler_free(&vdev->ctrl_handler); +} + +struct video_device *video_find_vdev(const struct device *dev) +{ + for (uint8_t i = 0; i < VIDEO_NUM_DEVICES; ++i) { + if (video_devices[i] != NULL && video_devices[i]->dev == dev) { + return video_devices[i]; + } + } + + return NULL; +} diff --git a/drivers/video/video_device.h b/drivers/video/video_device.h new file mode 100644 index 000000000000..a32b59c7dc0f --- /dev/null +++ b/drivers/video/video_device.h @@ -0,0 +1,26 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_VIDEO_DEVICE_H_ +#define ZEPHYR_INCLUDE_VIDEO_DEVICE_H_ + +#include + +#include "video_ctrls.h" + +struct video_device { + int ind; + const struct device *dev; + struct video_ctrl_handler ctrl_handler; +}; + +int video_register_vdev(struct video_device *vdev, const struct device *dev); + +void video_unregister_vdev(struct video_device *vdev); + +struct video_device *video_find_vdev(const struct device *dev); + +#endif /* ZEPHYR_INCLUDE_VIDEO_DEVICE_H_ */ diff --git a/drivers/video/video_mcux_csi.c b/drivers/video/video_mcux_csi.c index 244dc61ab6a6..173748c88820 100644 --- a/drivers/video/video_mcux_csi.c +++ b/drivers/video/video_mcux_csi.c @@ -18,23 +18,18 @@ #include #endif -#if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) -#define DEVICE_DT_INST_GET_SOURCE_DEV(n) \ - DEVICE_DT_GET(DT_PARENT(DT_GPARENT(DT_NODELABEL(DT_STRING_TOKEN( \ - DT_CHILD(DT_INST_CHILD(n, port), endpoint), remote_endpoint_label))))) -#else -#define DEVICE_DT_INST_GET_SOURCE_DEV(n) \ - DEVICE_DT_GET(DT_GPARENT(DT_NODELABEL(DT_STRING_TOKEN( \ - DT_CHILD(DT_INST_CHILD(n, port), endpoint), remote_endpoint_label)))) -#endif +#include "video_async.h" +#include "video_device.h" struct video_mcux_csi_config { CSI_Type *base; - const struct device *source_dev; + const struct device **source_devs; const struct pinctrl_dev_config *pincfg; }; struct video_mcux_csi_data { + struct video_notifier notifier; + struct video_device vdev; const struct device *dev; csi_config_t csi_config; csi_handle_t csi_handle; @@ -177,7 +172,7 @@ static int video_mcux_csi_set_fmt(const struct device *dev, enum video_endpoint_ return -EIO; } - if (config->source_dev && video_set_format(config->source_dev, ep, &format)) { + if (*config->source_devs && video_set_format(*config->source_devs, ep, &format)) { return -EIO; } @@ -193,7 +188,7 @@ static int video_mcux_csi_get_fmt(const struct device *dev, enum video_endpoint_ return -EINVAL; } - if (config->source_dev && !video_get_format(config->source_dev, ep, fmt)) { + if (*config->source_devs && !video_get_format(*config->source_devs, ep, fmt)) { #if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) video_pix_fmt_convert(fmt, true); #endif @@ -215,7 +210,7 @@ static int video_mcux_csi_stream_start(const struct device *dev) return -EIO; } - if (config->source_dev && video_stream_start(config->source_dev)) { + if (*config->source_devs && video_stream_start(*config->source_devs)) { return -EIO; } @@ -228,7 +223,7 @@ static int video_mcux_csi_stream_stop(const struct device *dev) struct video_mcux_csi_data *data = dev->data; status_t ret; - if (config->source_dev && video_stream_stop(config->source_dev)) { + if (*config->source_devs && video_stream_stop(*config->source_devs)) { return -EIO; } @@ -315,27 +310,14 @@ 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) +static inline int video_mcux_csi_set_ctrl(const struct device *dev, struct video_control *ctrl) { 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); + if (*config->source_devs) { + ret = video_set_ctrl(*config->source_devs, ctrl); } return ret; @@ -352,8 +334,8 @@ static int video_mcux_csi_get_caps(const struct device *dev, enum video_endpoint } /* Just forward to source dev for now */ - if (config->source_dev) { - err = video_get_caps(config->source_dev, ep, caps); + if (*config->source_devs) { + err = video_get_caps(*config->source_devs, ep, caps); #if defined(CONFIG_VIDEO_MCUX_MIPI_CSI2RX) /* * On i.MX RT11xx SoCs which have MIPI CSI-2 Rx, image data from the camera sensor @@ -400,12 +382,24 @@ static void video_mcux_csi_isr(const void *p) CSI_DriverIRQHandler(); } +static int video_mcux_csi_async_register(const struct device *dev) +{ + struct video_mcux_csi_data *data = dev->data; + const struct video_mcux_csi_config *config = dev->config; + + video_async_init(&data->notifier, dev, &data->vdev, NULL, config->source_devs, 1); + + return video_async_register(&data->notifier); +} + static int video_mcux_csi_init(const struct device *dev) { const struct video_mcux_csi_config *config = dev->config; struct video_mcux_csi_data *data = dev->data; int err; + data->dev = dev; + k_fifo_init(&data->fifo_in); k_fifo_init(&data->fifo_out); @@ -414,7 +408,7 @@ static int video_mcux_csi_init(const struct device *dev) /* check if there is any source device (video ctrl device) * the device is not yet initialized so we only check if it exists */ - if (config->source_dev == NULL) { + if (*config->source_devs == NULL) { return -ENODEV; } @@ -423,7 +417,12 @@ static int video_mcux_csi_init(const struct device *dev) return err; } - return 0; + err = video_register_vdev(&data->vdev, dev); + if (err) { + return err; + } + + return video_mcux_csi_async_register(dev); } #ifdef CONFIG_POLL @@ -447,7 +446,7 @@ static int video_mcux_csi_set_frmival(const struct device *dev, enum video_endpo { const struct video_mcux_csi_config *config = dev->config; - return video_set_frmival(config->source_dev, ep, frmival); + return video_set_frmival(*config->source_devs, ep, frmival); } static int video_mcux_csi_get_frmival(const struct device *dev, enum video_endpoint_id ep, @@ -455,7 +454,7 @@ static int video_mcux_csi_get_frmival(const struct device *dev, enum video_endpo { const struct video_mcux_csi_config *config = dev->config; - return video_get_frmival(config->source_dev, ep, frmival); + return video_get_frmival(*config->source_devs, ep, frmival); } static int video_mcux_csi_enum_frmival(const struct device *dev, enum video_endpoint_id ep, @@ -472,7 +471,7 @@ static int video_mcux_csi_enum_frmival(const struct device *dev, enum video_endp fie->format = &converted_fmt; #endif - ret = video_enum_frmival(config->source_dev, ep, fie); + ret = video_enum_frmival(*config->source_devs, ep, fie); fie->format = fie_fmt; return ret; @@ -487,7 +486,6 @@ static const struct video_driver_api video_mcux_csi_driver_api = { .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, @@ -502,7 +500,8 @@ PINCTRL_DT_INST_DEFINE(0); static const struct video_mcux_csi_config video_mcux_csi_config_0 = { .base = (CSI_Type *)DT_INST_REG_ADDR(0), - .source_dev = DEVICE_DT_INST_GET_SOURCE_DEV(0), + .source_devs = (const struct device *[]){DEVICE_DT_GET_REMOTE_DEVICE( + DT_INST_ENDPOINT_BY_ID(0, 0, 0))}, .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), }; @@ -510,14 +509,10 @@ static struct video_mcux_csi_data video_mcux_csi_data_0; static int video_mcux_csi_init_0(const struct device *dev) { - struct video_mcux_csi_data *data = dev->data; - IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), video_mcux_csi_isr, NULL, 0); irq_enable(DT_INST_IRQN(0)); - data->dev = dev; - return video_mcux_csi_init(dev); } diff --git a/drivers/video/video_mcux_mipi_csi2rx.c b/drivers/video/video_mcux_mipi_csi2rx.c index a73c17efa07f..19b1baca9984 100644 --- a/drivers/video/video_mcux_mipi_csi2rx.c +++ b/drivers/video/video_mcux_mipi_csi2rx.c @@ -15,23 +15,22 @@ #include +#include "video_async.h" +#include "video_ctrls.h" + LOG_MODULE_REGISTER(video_mipi_csi2rx, CONFIG_VIDEO_LOG_LEVEL); #define MAX_SUPPORTED_PIXEL_RATE MHZ(96) #define ABS(a, b) (a > b ? a - b : b - a) -#define DEVICE_DT_INST_GET_SENSOR_DEV(n) \ - DEVICE_DT_GET(DT_GPARENT(DT_NODELABEL( \ - DT_STRING_TOKEN(DT_CHILD(DT_CHILD(DT_INST_CHILD(n, ports), port_1), endpoint), \ - remote_endpoint_label)))) - struct mipi_csi2rx_config { const MIPI_CSI2RX_Type *base; - const struct device *sensor_dev; + const struct device **sensor_devs; }; struct mipi_csi2rx_data { + struct video_notifier notifier; csi2rx_config_t csi2rxConfig; const struct device *clock_dev; clock_control_subsys_t clock_root; @@ -51,6 +50,25 @@ const struct mipi_csi2rx_tHsSettleEscClk_config tHsSettleEscClk_configs[] = { {MHZ(96), 0x09}, }; +static int32_t get_pixel_rate_from_sensor(const struct device *sensor) +{ + struct video_notifier *notifier = video_async_find_notifier(sensor); + + if (!notifier) { + LOG_ERR("Sensor device is not registered"); + return -ENODEV; + } + + struct video_ctrl *ctrl = video_ctrl_find(notifier->ctrl_handler, VIDEO_CID_PIXEL_RATE); + + if (!ctrl) { + LOG_ERR("Sensor device does not support VIDEO_CID_PIXEL_RATE"); + return -ENOTSUP; + } + + return ctrl->val; +} + static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endpoint_id ep) { const struct mipi_csi2rx_config *config = dev->config; @@ -61,17 +79,13 @@ static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endp int ret, ind = 0; struct video_format fmt; - ret = video_get_format(config->sensor_dev, ep, &fmt); + ret = video_get_format(*config->sensor_devs, ep, &fmt); if (ret) { LOG_ERR("Cannot get sensor_dev pixel format"); 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; - } + sensor_pixel_rate = get_pixel_rate_from_sensor(*config->sensor_devs); if (sensor_pixel_rate > MAX_SUPPORTED_PIXEL_RATE) { LOG_ERR("Sensor pixel rate is not supported"); @@ -128,7 +142,7 @@ static int mipi_csi2rx_set_fmt(const struct device *dev, enum video_endpoint_id { const struct mipi_csi2rx_config *config = dev->config; - if (video_set_format(config->sensor_dev, ep, fmt)) { + if (video_set_format(*config->sensor_devs, ep, fmt)) { return -EIO; } @@ -144,7 +158,7 @@ static int mipi_csi2rx_get_fmt(const struct device *dev, enum video_endpoint_id return -EINVAL; } - if (video_get_format(config->sensor_dev, ep, fmt)) { + if (video_get_format(*config->sensor_devs, ep, fmt)) { return -EIO; } @@ -158,7 +172,7 @@ static int mipi_csi2rx_stream_start(const struct device *dev) CSI2RX_Init((MIPI_CSI2RX_Type *)config->base, &drv_data->csi2rxConfig); - if (video_stream_start(config->sensor_dev)) { + if (video_stream_start(*config->sensor_devs)) { return -EIO; } @@ -169,7 +183,7 @@ static int mipi_csi2rx_stream_stop(const struct device *dev) { const struct mipi_csi2rx_config *config = dev->config; - if (video_stream_stop(config->sensor_dev)) { + if (video_stream_stop(*config->sensor_devs)) { return -EIO; } @@ -188,15 +202,15 @@ static int mipi_csi2rx_get_caps(const struct device *dev, enum video_endpoint_id } /* Just forward to sensor dev for now */ - return video_get_caps(config->sensor_dev, ep, caps); + return video_get_caps(*config->sensor_devs, ep, caps); } -static inline int mipi_csi2rx_set_ctrl(const struct device *dev, unsigned int cid, void *value) +static inline int mipi_csi2rx_set_ctrl(const struct device *dev, struct video_control *control) { const struct mipi_csi2rx_config *config = dev->config; - if (config->sensor_dev) { - return video_set_ctrl(config->sensor_dev, cid, value); + if (*config->sensor_devs) { + return video_set_ctrl(*config->sensor_devs, control); } return -ENOTSUP; @@ -208,7 +222,7 @@ static int mipi_csi2rx_set_frmival(const struct device *dev, enum video_endpoint const struct mipi_csi2rx_config *config = dev->config; int ret; - ret = video_set_frmival(config->sensor_dev, ep, frmival); + ret = video_set_frmival(*config->sensor_devs, ep, frmival); if (ret) { LOG_ERR("Cannot set sensor_dev frmival"); return ret; @@ -224,7 +238,7 @@ static int mipi_csi2rx_get_frmival(const struct device *dev, enum video_endpoint { const struct mipi_csi2rx_config *config = dev->config; - return video_get_frmival(config->sensor_dev, ep, frmival); + return video_get_frmival(*config->sensor_devs, ep, frmival); } static uint64_t mipi_csi2rx_cal_frame_size(const struct video_format *fmt) @@ -254,24 +268,20 @@ static int mipi_csi2rx_enum_frmival(const struct device *dev, enum video_endpoin struct video_frmival cur_frmival; struct video_format cur_fmt; - ret = video_enum_frmival(config->sensor_dev, ep, fie); + ret = video_enum_frmival(*config->sensor_devs, 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; - } + cur_pixel_rate = get_pixel_rate_from_sensor(*config->sensor_devs); - ret = video_get_frmival(config->sensor_dev, ep, &cur_frmival); + ret = video_get_frmival(*config->sensor_devs, ep, &cur_frmival); if (ret) { LOG_ERR("Cannot get sensor_dev frame rate"); return ret; } - ret = video_get_format(config->sensor_dev, ep, &cur_fmt); + ret = video_get_format(*config->sensor_devs, ep, &cur_fmt); if (ret) { LOG_ERR("Cannot get sensor_dev format"); return ret; @@ -323,6 +333,16 @@ static const struct video_driver_api mipi_csi2rx_driver_api = { .enum_frmival = mipi_csi2rx_enum_frmival, }; +static int mipi_csi2rx_async_register(const struct device *dev) +{ + const struct mipi_csi2rx_config *config = dev->config; + struct mipi_csi2rx_data *drv_data = dev->data; + + video_async_init(&drv_data->notifier, dev, NULL, NULL, config->sensor_devs, 1); + + return video_async_register(&drv_data->notifier); +} + static int mipi_csi2rx_init(const struct device *dev) { const struct mipi_csi2rx_config *config = dev->config; @@ -330,7 +350,7 @@ static int mipi_csi2rx_init(const struct device *dev) int ret; /* Check if there is any sensor device */ - if (!device_is_ready(config->sensor_dev)) { + if (!device_is_ready(*config->sensor_devs)) { return -ENODEV; } @@ -344,14 +364,17 @@ static int mipi_csi2rx_init(const struct device *dev) return ret; } - return mipi_csi2rx_update_settings(dev, VIDEO_EP_ALL); + ret = mipi_csi2rx_update_settings(dev, VIDEO_EP_ALL); + if (ret) { + return ret; + } + + return mipi_csi2rx_async_register(dev); } #define MIPI_CSI2RX_INIT(n) \ static struct mipi_csi2rx_data mipi_csi2rx_data_##n = { \ - .csi2rxConfig.laneNum = \ - DT_PROP_LEN(DT_CHILD(DT_CHILD(DT_INST_CHILD(n, ports), port_1), endpoint), \ - data_lanes), \ + .csi2rxConfig.laneNum = DT_PROP_LEN(DT_INST_ENDPOINT_BY_ID(n, 1, 0), data_lanes), \ .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ .clock_root = (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_IDX(n, 0, name), \ .clock_ui = (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_IDX(n, 1, name), \ @@ -360,7 +383,8 @@ static int mipi_csi2rx_init(const struct device *dev) \ static const struct mipi_csi2rx_config mipi_csi2rx_config_##n = { \ .base = (MIPI_CSI2RX_Type *)DT_INST_REG_ADDR(n), \ - .sensor_dev = DEVICE_DT_INST_GET_SENSOR_DEV(n), \ + .sensor_devs = (const struct device *[]){DEVICE_DT_GET_REMOTE_DEVICE( \ + DT_INST_ENDPOINT_BY_ID(n, 1, 0))}, \ }; \ \ DEVICE_DT_INST_DEFINE(n, &mipi_csi2rx_init, NULL, &mipi_csi2rx_data_##n, \ diff --git a/drivers/video/video_sw_generator.c b/drivers/video/video_sw_generator.c index 845834d5a844..3802b3e3158e 100644 --- a/drivers/video/video_sw_generator.c +++ b/drivers/video/video_sw_generator.c @@ -11,6 +11,9 @@ #include #include +#include "video_async.h" +#include "video_device.h" + LOG_MODULE_REGISTER(video_sw_generator, CONFIG_VIDEO_LOG_LEVEL); #define VIDEO_PATTERN_COLOR_BAR 0 @@ -24,8 +27,16 @@ LOG_MODULE_REGISTER(video_sw_generator, CONFIG_VIDEO_LOG_LEVEL); */ #define MAX_FRAME_RATE 60 +struct sw_ctrls { + struct video_ctrl_handler handler; + struct video_ctrl vflip; +}; + struct video_sw_generator_data { + struct video_notifier notifier; + struct video_device vdev; const struct device *dev; + struct sw_ctrls ctrls; struct video_format fmt; struct k_fifo fifo_in; struct k_fifo fifo_out; @@ -260,14 +271,13 @@ 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) +static inline int video_sw_generator_set_ctrl(const struct device *dev, struct video_control *ctrl) { struct video_sw_generator_data *data = dev->data; - switch (cid) { + switch (ctrl->id) { case VIDEO_CID_VFLIP: - data->ctrl_vflip = (bool)value; + data->ctrl_vflip = (bool)ctrl->val; break; default: return -ENOTSUP; @@ -363,8 +373,28 @@ 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; + struct sw_ctrls *ctrls = &data->ctrls; + + video_ctrl_handler_init(&ctrls->handler, dev); + + return video_ctrl_init(&ctrls->vflip, &ctrls->handler, VIDEO_CID_VFLIP, 0, 1, 1, 0); +} + +static int video_sw_generator_async_register(const struct device *dev) +{ + struct video_sw_generator_data *data = dev->data; + + video_async_init(&data->notifier, dev, &data->vdev, NULL, NULL, 0); + + return video_async_register(&data->notifier); +} + static int video_sw_generator_init(const struct device *dev) { + int ret; struct video_sw_generator_data *data = dev->data; data->dev = dev; @@ -372,7 +402,17 @@ 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; + ret = video_sw_generator_init_controls(dev); + if (ret) { + return ret; + } + + ret = video_register_vdev(&data->vdev, dev); + if (ret) { + return ret; + } + + return video_sw_generator_async_register(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..298179f17efa 100644 --- a/include/zephyr/drivers/video-controls.h +++ b/include/zephyr/drivers/video-controls.h @@ -161,6 +161,11 @@ enum video_power_line_frequency { * @} */ +struct video_control { + uint32_t id; + int32_t val; +}; + #ifdef __cplusplus } #endif diff --git a/include/zephyr/drivers/video.h b/include/zephyr/drivers/video.h index fb647cac70fb..aff71a421d7d 100644 --- a/include/zephyr/drivers/video.h +++ b/include/zephyr/drivers/video.h @@ -37,6 +37,8 @@ extern "C" { */ #define LINE_COUNT_HEIGHT (-1) +struct video_control; + /** * @struct video_format * @brief Video format structure @@ -202,6 +204,33 @@ struct video_frmival_enum { }; }; +/** + * @brief Port / Endpoint DT Helpers + * + */ + +/* Drivers should not use this except they know exactly that port has a pid */ +#define _DT_INST_PORT_BY_ID(n, pid) \ + COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(n, ports)), (DT_CHILD(DT_INST_CHILD(n, ports), port_##pid)), (DT_INST_CHILD(n, port_##pid))) + +/* Drivers rarely use this as they usually deals with only endpoint */ +#define DT_INST_PORT_BY_ID(n, pid) \ + COND_CODE_1(DT_NODE_EXISTS(_DT_INST_PORT_BY_ID(n, pid)), (_DT_INST_PORT_BY_ID(n, pid)), (DT_INST_CHILD(n, port))) + +/* Drivers should not use this except they know exactly that the endpoint has a pid */ +#define _DT_INST_ENDPOINT_BY_ID(n, pid, id) DT_CHILD(DT_INST_PORT_BY_ID(n, pid), endpoint_##id) + +#define DT_INST_ENDPOINT_BY_ID(n, pid, id) \ + COND_CODE_1(DT_NODE_EXISTS(_DT_INST_ENDPOINT_BY_ID(n, pid, id)), (_DT_INST_ENDPOINT_BY_ID(n, pid, id)), (DT_CHILD(DT_INST_PORT_BY_ID(n, pid), endpoint))) + +/* Drivers should not use this except they know exactly that the remote device has no ports node */ +#define DT_REMOTE_DEVICE_NO_PORTS(ep) \ + DT_GPARENT(DT_NODELABEL(DT_STRING_TOKEN(ep, remote_endpoint_label))) + +#define DEVICE_DT_GET_REMOTE_DEVICE(ep) \ + COND_CODE_1(DT_NODE_EXISTS(DT_CHILD(DT_PARENT(DT_REMOTE_DEVICE_NO_PORTS(ep)), ports)), \ +(DEVICE_DT_GET(DT_PARENT(DT_REMOTE_DEVICE_NO_PORTS(ep)))), (DEVICE_DT_GET(DT_REMOTE_DEVICE_NO_PORTS(ep)))) + /** * @brief video_endpoint_id enum * @@ -323,15 +352,7 @@ typedef int (*video_api_stream_stop_t)(const struct device *dev); * * 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 @@ -363,7 +384,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; @@ -657,24 +677,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 video_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. @@ -683,24 +693,14 @@ static inline int video_set_ctrl(const struct device *dev, unsigned int cid, voi * 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 video_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.