Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -103,6 +103,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 @@ -230,6 +230,12 @@ New APIs and options

* :kconfig:option:`CONFIG_TASK_WDT_DUMMY`

* USB

* Video

* :c:func:`uvc_add_format`

.. zephyr-keep-sorted-stop

New Boards
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 @@ -443,3 +443,24 @@ int64_t video_get_csi_link_freq(const struct device *dev, uint8_t bpp, uint8_t l
/* CSI D-PHY is using a DDR data bus so bitrate is twice the frequency */
return ctrl.val64 * bpp / (2 * lane_nb);
}

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);
}
86 changes: 75 additions & 11 deletions drivers/video/video_stm32_dcmipp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1355,25 +1355,89 @@ static int stm32_dcmipp_dequeue(const struct device *dev, struct video_buffer **
return 0;
}

/*
* 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
*/
#define DCMIPP_CEIL_DIV(a, b) (((a) + (b) - 1) / (b))
#define DCMIPP_VIDEO_FORMAT_CAP(format) \
{ \
.pixelformat = VIDEO_PIX_FMT_##format, \
.width_min = DCMIPP_CEIL_DIV(CONFIG_VIDEO_STM32_DCMIPP_SENSOR_WIDTH, \
STM32_DCMIPP_MAX_PIPE_SCALE_FACTOR), \
.width_max = CONFIG_VIDEO_STM32_DCMIPP_SENSOR_WIDTH, \
.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 = 1, .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),
DCMIPP_VIDEO_FORMAT_CAP(YUYV),
DCMIPP_VIDEO_FORMAT_CAP(YVYU),
DCMIPP_VIDEO_FORMAT_CAP(GREY),
DCMIPP_VIDEO_FORMAT_CAP(RGB24),
DCMIPP_VIDEO_FORMAT_CAP(BGR24),
DCMIPP_VIDEO_FORMAT_CAP(ARGB32),
DCMIPP_VIDEO_FORMAT_CAP(ABGR32),
DCMIPP_VIDEO_FORMAT_CAP(RGBA32),
DCMIPP_VIDEO_FORMAT_CAP(BGRA32),
DCMIPP_VIDEO_FORMAT_CAP(NV12),
DCMIPP_VIDEO_FORMAT_CAP(NV21),
DCMIPP_VIDEO_FORMAT_CAP(NV16),
DCMIPP_VIDEO_FORMAT_CAP(NV61),
DCMIPP_VIDEO_FORMAT_CAP(YUV420),
DCMIPP_VIDEO_FORMAT_CAP(YVU420),
{0},
};

static const struct video_format_cap stm32_dcmipp_aux_fmts[] = {
DCMIPP_VIDEO_FORMAT_CAP(RGB565),
DCMIPP_VIDEO_FORMAT_CAP(YUYV),
DCMIPP_VIDEO_FORMAT_CAP(YVYU),
DCMIPP_VIDEO_FORMAT_CAP(GREY),
DCMIPP_VIDEO_FORMAT_CAP(RGB24),
DCMIPP_VIDEO_FORMAT_CAP(BGR24),
DCMIPP_VIDEO_FORMAT_CAP(ARGB32),
DCMIPP_VIDEO_FORMAT_CAP(ABGR32),
DCMIPP_VIDEO_FORMAT_CAP(RGBA32),
DCMIPP_VIDEO_FORMAT_CAP(BGRA32),
{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;
caps->min_line_count = LINE_COUNT_HEIGHT;
caps->max_line_count = LINE_COUNT_HEIGHT;

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 @@ -977,6 +977,22 @@ void video_closest_frmival(const struct device *dev, struct video_frmival_enum *
*/
int64_t video_get_csi_link_freq(const struct device *dev, uint8_t bpp, uint8_t lane_nb);

/**
* @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 @@ -150,20 +154,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 @@ -172,39 +170,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"
Loading