Skip to content
Open
6 changes: 6 additions & 0 deletions doc/releases/migration-guide-4.3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ Stepper

* :dtcompatible:`zephyr,gpio-stepper` has been replaced by :dtcompatible:`zephyr,h-bridge-stepper`.

USB
===

* The USB Video Class was configuring the framerate and format of the source video device.
This is now to be done by the application after the host selected the format (:github:`93192`).

.. zephyr-keep-sorted-stop

Bluetooth
Expand Down
6 changes: 6 additions & 0 deletions doc/releases/release-notes-4.3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,12 @@ New APIs and options

* :c:macro:`__deprecated_version`

* USB

* Video

* :c:func:`uvc_add_format`

* Video

* :c:member:`video_format.size` field
Expand Down
21 changes: 21 additions & 0 deletions drivers/video/video_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,3 +468,24 @@ int video_estimate_fmt_size(struct video_format *fmt)

return 0;
}

int video_set_compose_format(const struct device *dev, struct video_format *fmt)
{
struct video_selection sel = {
.type = fmt->type,
.target = VIDEO_SEL_TGT_COMPOSE,
.rect.left = 0,
.rect.top = 0,
.rect.width = fmt->width,
.rect.height = fmt->height,
};
int ret;

ret = video_set_selection(dev, &sel);
if (ret < 0 && ret != -ENOSYS) {
LOG_ERR("Unable to set selection compose");
return ret;
}

return video_set_format(dev, fmt);
}
89 changes: 80 additions & 9 deletions drivers/video/video_stm32_dcmipp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1359,22 +1359,93 @@ static int stm32_dcmipp_dequeue(const struct device *dev, struct video_buffer **
}

/*
* TODO: caps aren't yet handled hence give back straight the caps given by the
* source. Normally this should be the intersection of what the source produces
* vs what the DCMIPP can input (for pipe0) and, for pipe 1 and 2, for a given
* input format, generate caps based on capabilities, color conversion, decimation
* etc
* For MAIN / AUX pipe, it is necessary that the pitch is a multiple of 16 bytes.
* Give here the multiple in number of pixels, which depends on the format chosen
*/
#define DCMIPP_CEIL_DIV_ROUND_UP_MUL(val, div, mul) \
((((val) + (div) - 1) / (div) + (mul) - 1) / (mul) * (mul))
#define DCMIPP_CEIL_DIV(val, div) \
(((val) + (div) - 1) / (div))
#define DCMIPP_VIDEO_FORMAT_CAP(format, pixmul) { \
.pixelformat = VIDEO_PIX_FMT_##format, \
.width_min = DCMIPP_CEIL_DIV_ROUND_UP_MUL(CONFIG_VIDEO_STM32_DCMIPP_SENSOR_WIDTH, \
STM32_DCMIPP_MAX_PIPE_SCALE_FACTOR, \
pixmul), \
.width_max = CONFIG_VIDEO_STM32_DCMIPP_SENSOR_WIDTH / (pixmul) * (pixmul), \
.height_min = DCMIPP_CEIL_DIV(CONFIG_VIDEO_STM32_DCMIPP_SENSOR_HEIGHT, \
STM32_DCMIPP_MAX_PIPE_SCALE_FACTOR), \
.height_max = CONFIG_VIDEO_STM32_DCMIPP_SENSOR_HEIGHT, \
.width_step = pixmul, .height_step = 1, \
}

static const struct video_format_cap stm32_dcmipp_dump_fmt[] = {
{
.pixelformat =
VIDEO_FOURCC_FROM_STR(CONFIG_VIDEO_STM32_DCMIPP_SENSOR_PIXEL_FORMAT),
.width_min = CONFIG_VIDEO_STM32_DCMIPP_SENSOR_WIDTH,
.width_max = CONFIG_VIDEO_STM32_DCMIPP_SENSOR_WIDTH,
.height_min = CONFIG_VIDEO_STM32_DCMIPP_SENSOR_HEIGHT,
.height_max = CONFIG_VIDEO_STM32_DCMIPP_SENSOR_HEIGHT,
.width_step = 1, .height_step = 1,
},
{0},
};

static const struct video_format_cap stm32_dcmipp_main_fmts[] = {
DCMIPP_VIDEO_FORMAT_CAP(RGB565, 8),
DCMIPP_VIDEO_FORMAT_CAP(YUYV, 8),
DCMIPP_VIDEO_FORMAT_CAP(YVYU, 8),
DCMIPP_VIDEO_FORMAT_CAP(GREY, 16),
DCMIPP_VIDEO_FORMAT_CAP(RGB24, 16),
DCMIPP_VIDEO_FORMAT_CAP(BGR24, 16),
DCMIPP_VIDEO_FORMAT_CAP(ARGB32, 4),
DCMIPP_VIDEO_FORMAT_CAP(ABGR32, 4),
DCMIPP_VIDEO_FORMAT_CAP(RGBA32, 4),
DCMIPP_VIDEO_FORMAT_CAP(BGRA32, 4),
DCMIPP_VIDEO_FORMAT_CAP(NV12, 16),
DCMIPP_VIDEO_FORMAT_CAP(NV21, 16),
DCMIPP_VIDEO_FORMAT_CAP(NV16, 16),
DCMIPP_VIDEO_FORMAT_CAP(NV61, 16),
DCMIPP_VIDEO_FORMAT_CAP(YUV420, 16),
DCMIPP_VIDEO_FORMAT_CAP(YVU420, 16),
{0},
};

static const struct video_format_cap stm32_dcmipp_aux_fmts[] = {
DCMIPP_VIDEO_FORMAT_CAP(RGB565, 8),
DCMIPP_VIDEO_FORMAT_CAP(YUYV, 8),
DCMIPP_VIDEO_FORMAT_CAP(YVYU, 8),
DCMIPP_VIDEO_FORMAT_CAP(GREY, 16),
DCMIPP_VIDEO_FORMAT_CAP(RGB24, 16),
DCMIPP_VIDEO_FORMAT_CAP(BGR24, 16),
DCMIPP_VIDEO_FORMAT_CAP(ARGB32, 4),
DCMIPP_VIDEO_FORMAT_CAP(ABGR32, 4),
DCMIPP_VIDEO_FORMAT_CAP(RGBA32, 4),
DCMIPP_VIDEO_FORMAT_CAP(BGRA32, 4),
{0},
};

static int stm32_dcmipp_get_caps(const struct device *dev, struct video_caps *caps)
{
const struct stm32_dcmipp_config *config = dev->config;
int ret;
struct stm32_dcmipp_pipe_data *pipe = dev->data;

ret = video_get_caps(config->source_dev, caps);
switch (pipe->id) {
case DCMIPP_PIPE0:
caps->format_caps = stm32_dcmipp_dump_fmt;
break;
case DCMIPP_PIPE1:
caps->format_caps = stm32_dcmipp_main_fmts;
break;
case DCMIPP_PIPE2:
caps->format_caps = stm32_dcmipp_aux_fmts;
break;
default:
CODE_UNREACHABLE;
}

caps->min_vbuf_count = 1;

return ret;
return 0;
}

static int stm32_dcmipp_get_frmival(const struct device *dev, struct video_frmival *frmival)
Expand Down
16 changes: 16 additions & 0 deletions include/zephyr/drivers/video.h
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,22 @@ int64_t video_get_csi_link_freq(const struct device *dev, uint8_t bpp, uint8_t l
*/
int video_estimate_fmt_size(struct video_format *fmt);

/**
* @brief Set compose rectangle (if applicable) prior to setting format
*
* Some devices expose compose capabilities, allowing them to apply a transformation
* (downscale / upscale) to the frame. For those devices, it is necessary to set the
* compose rectangle before being able to apply the frame format (which must have the
* same width / height and the compose rectangle width / height.
* In order to allow non-compose aware application to be able to control such devices,
* introduce a helper which, if available, will apply the compose rectangle prior to
* setting the format.
*
* @param dev Video device to query.
* @param fmt Video format structure pointer
*/
int video_set_compose_format(const struct device *dev, struct video_format *fmt);

/**
* @defgroup video_pixel_formats Video pixel formats
* The '|' characters separate the pixels or logical blocks, and spaces separate the bytes.
Expand Down
25 changes: 19 additions & 6 deletions include/zephyr/usb/class/usbd_uvc.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,33 @@
*/

/**
* @brief Set the video device that a UVC instance will use.
* @brief Set the video device that a UVC instance will use for control requests.
*
* It will query its supported controls, formats and frame rates, and use this information to
* generate USB descriptors sent to the host.
*
* At runtime, it will forward all USB controls from the host to this device.
* It will query its supported video controls and frame intervals and use this information to
* generate the USB descriptors presented to the host. At runtime, it will forward all USB controls
* from the host to this device.
*
* @note This function must be called before @ref usbd_enable.
*
* @param uvc_dev The UVC device
* @param video_dev The video device that this UVC instance controls
* @param video_dev The video device that this UVC instance send controls requests to
*/
void uvc_set_video_dev(const struct device *uvc_dev, const struct device *video_dev);

/**
* @brief Set the video format capabilities that a UVC instance will present to the host.
*
* This information will be used to generate USB descriptors.
* The particular format selected by the host can be queried with @ref video_get_format.
*
* @note This function must be called before @ref usbd_enable and before @ref uvc_set_video_dev.
*
* @param uvc_dev The UVC device to configure
* @param fmt The video format to add to this UVC instance
* @return 0 on success, negative value on error
*/
int uvc_add_format(const struct device *const uvc_dev, const struct video_format *const fmt);

/**
* @}
*/
Expand Down
45 changes: 9 additions & 36 deletions samples/drivers/video/capture/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,14 @@ int main(void)
struct video_frmival frmival;
struct video_frmival_enum fie;
enum video_buf_type type = VIDEO_BUF_TYPE_OUTPUT;
#if (CONFIG_VIDEO_SOURCE_CROP_WIDTH && CONFIG_VIDEO_SOURCE_CROP_HEIGHT) || \
CONFIG_VIDEO_FRAME_HEIGHT || CONFIG_VIDEO_FRAME_WIDTH
struct video_selection sel = {
#if (CONFIG_VIDEO_SOURCE_CROP_WIDTH && CONFIG_VIDEO_SOURCE_CROP_HEIGHT)
struct video_selection crop_sel = {
.type = VIDEO_BUF_TYPE_OUTPUT,
.target = VIDEO_SEL_TGT_CROP;
.rect.left = CONFIG_VIDEO_SOURCE_CROP_LEFT;
.rect.top = CONFIG_VIDEO_SOURCE_CROP_TOP;
.rect.width = CONFIG_VIDEO_SOURCE_CROP_WIDTH;
.rect.height = CONFIG_VIDEO_SOURCE_CROP_HEIGHT;
};
#endif
unsigned int frame = 0;
Expand Down Expand Up @@ -149,20 +153,14 @@ int main(void)

/* Set the crop setting if necessary */
#if CONFIG_VIDEO_SOURCE_CROP_WIDTH && CONFIG_VIDEO_SOURCE_CROP_HEIGHT
sel.target = VIDEO_SEL_TGT_CROP;
sel.rect.left = CONFIG_VIDEO_SOURCE_CROP_LEFT;
sel.rect.top = CONFIG_VIDEO_SOURCE_CROP_TOP;
sel.rect.width = CONFIG_VIDEO_SOURCE_CROP_WIDTH;
sel.rect.height = CONFIG_VIDEO_SOURCE_CROP_HEIGHT;
if (video_set_selection(video_dev, &sel)) {
if (video_set_selection(video_dev, &crop_sel)) {
LOG_ERR("Unable to set selection crop");
return 0;
}
LOG_INF("Selection crop set to (%u,%u)/%ux%u",
sel.rect.left, sel.rect.top, sel.rect.width, sel.rect.height);
#endif

#if CONFIG_VIDEO_FRAME_HEIGHT || CONFIG_VIDEO_FRAME_WIDTH
#if CONFIG_VIDEO_FRAME_HEIGHT
fmt.height = CONFIG_VIDEO_FRAME_HEIGHT;
#endif
Expand All @@ -171,39 +169,14 @@ int main(void)
fmt.width = CONFIG_VIDEO_FRAME_WIDTH;
#endif

/*
* Check (if possible) if targeted size is same as crop
* and if compose is necessary
*/
sel.target = VIDEO_SEL_TGT_CROP;
err = video_get_selection(video_dev, &sel);
if (err < 0 && err != -ENOSYS) {
LOG_ERR("Unable to get selection crop");
return 0;
}

if (err == 0 && (sel.rect.width != fmt.width || sel.rect.height != fmt.height)) {
sel.target = VIDEO_SEL_TGT_COMPOSE;
sel.rect.left = 0;
sel.rect.top = 0;
sel.rect.width = fmt.width;
sel.rect.height = fmt.height;
err = video_set_selection(video_dev, &sel);
if (err < 0 && err != -ENOSYS) {
LOG_ERR("Unable to set selection compose");
return 0;
}
}
#endif

if (strcmp(CONFIG_VIDEO_PIXEL_FORMAT, "")) {
fmt.pixelformat = VIDEO_FOURCC_FROM_STR(CONFIG_VIDEO_PIXEL_FORMAT);
}

LOG_INF("- Video format: %s %ux%u",
VIDEO_FOURCC_TO_STR(fmt.pixelformat), fmt.width, fmt.height);

if (video_set_format(video_dev, &fmt)) {
if (video_set_compose_format(video_dev, &fmt)) {
LOG_ERR("Unable to set format");
return 0;
}
Expand Down
12 changes: 12 additions & 0 deletions samples/subsys/usb/uvc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,16 @@
# tree, you cannot use them in your own application.
source "samples/subsys/usb/common/Kconfig.sample_usbd"

menu "UVC specific configuration"

config VIDEO_MAX_RANGE_RESOLUTIONS
int "Maximum number of intermediate resolutions"
default 5
help
Control the maximum number of resolution that will be advertised
to the USB client in case of the video capture supports a range
of resolutions.

endmenu

source "Kconfig.zephyr"
3 changes: 3 additions & 0 deletions samples/subsys/usb/uvc/boards/stm32n6570_dk.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enough for 2592x1944 YUYV frames
CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=10100000
CONFIG_MAIN_STACK_SIZE=2048
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enough for 2592x1944 YUYV frames
CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=10100000
CONFIG_MAIN_STACK_SIZE=2048
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enough for 2592x1944 YUYV frames
CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=10100000
CONFIG_MAIN_STACK_SIZE=2048
Loading
Loading