[PATCHv4 5/5] media: uvcvideo: add UVC 1.5 ROI control

From: Sergey Senozhatsky
Date: Fri Apr 30 2021 - 07:26:45 EST


This patch implements UVC 1.5 Region of Interest (ROI) control.

Note that, UVC 1.5 defines CT_DIGITAL_WINDOW_CONTROL controls
and mentions that ROI rectangle coordinates "must be within
the current Digital Window as specified by the CT_WINDOW control."
(4.2.2.1.20 Digital Region of Interest (ROI) Control).

It's is not entirely clear if we need to implement WINDOW_CONTROL.
ROI is naturally limited by GET_MIN and GET_MAX rectangles.

Another thing to note is that ROI support is implemented as
V4L2 selection target: selection rectangle represents ROI
rectangle.

Signed-off-by: Sergey Senozhatsky <senozhatsky@xxxxxxxxxxxx>
---
drivers/media/usb/uvc/uvc_v4l2.c | 185 ++++++++++++++++++++++++++++++-
1 file changed, 182 insertions(+), 3 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 252136cc885c..eb4e306d7a38 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1139,14 +1139,73 @@ static int uvc_ioctl_querymenu(struct file *file, void *fh,
return uvc_query_v4l2_menu(chain, qm);
}

-static int uvc_ioctl_g_selection(struct file *file, void *fh,
- struct v4l2_selection *sel)
+/* UVC 1.5 ROI rectangle is half the size of v4l2_rect */
+struct uvc_roi_rect {
+ __u16 top;
+ __u16 left;
+ __u16 bottom;
+ __u16 right;
+ __u16 auto_controls;
+} __packed;
+
+static int uvc_ioctl_g_roi_target(struct file *file, void *fh,
+ struct v4l2_selection *sel)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
+ struct uvc_video_chain *chain = handle->chain;
+ struct uvc_roi_rect *roi;
+ u8 query;
+ int ret;

- if (sel->type != stream->type)
+ switch (sel->target) {
+ case V4L2_SEL_TGT_ROI:
+ query = UVC_GET_CUR;
+ break;
+ case V4L2_SEL_TGT_ROI_DEFAULT:
+ query = UVC_GET_DEF;
+ break;
+ case V4L2_SEL_TGT_ROI_BOUNDS_MIN:
+ query = UVC_GET_MAX;
+ break;
+ case V4L2_SEL_TGT_ROI_BOUNDS_MAX:
+ query = UVC_GET_MAX;
+ break;
+ default:
return -EINVAL;
+ }
+
+ roi = kzalloc(sizeof(struct uvc_roi_rect), GFP_KERNEL);
+ if (!roi)
+ return -ENOMEM;
+
+ /*
+ * Synchronize with uvc_ioctl_query_ext_ctrl() that can set
+ * ROI auto_controls concurrently.
+ */
+ mutex_lock(&chain->ctrl_mutex);
+
+ ret = uvc_query_ctrl(stream->dev, query, 1, stream->dev->intfnum,
+ UVC_CT_REGION_OF_INTEREST_CONTROL, roi,
+ sizeof(struct uvc_roi_rect));
+ if (!ret) {
+ /* ROI left, top, right, bottom are global coordinates. */
+ sel->r.left = roi->left;
+ sel->r.top = roi->top;
+ sel->r.width = roi->right - roi->left + 1;
+ sel->r.height = roi->bottom - roi->top + 1;
+ }
+
+ mutex_unlock(&chain->ctrl_mutex);
+ kfree(roi);
+ return ret;
+}
+
+static int uvc_ioctl_g_sel_target(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;

switch (sel->target) {
case V4L2_SEL_TGT_CROP_DEFAULT:
@@ -1173,6 +1232,125 @@ static int uvc_ioctl_g_selection(struct file *file, void *fh,
return 0;
}

+static int uvc_ioctl_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ if (sel->type != stream->type)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ return uvc_ioctl_g_sel_target(file, fh, sel);
+ case V4L2_SEL_TGT_ROI:
+ case V4L2_SEL_TGT_ROI_DEFAULT:
+ case V4L2_SEL_TGT_ROI_BOUNDS_MIN:
+ case V4L2_SEL_TGT_ROI_BOUNDS_MAX:
+ return uvc_ioctl_g_roi_target(file, fh, sel);
+ }
+
+ return -EINVAL;
+}
+
+static void validate_roi_bounds(struct uvc_streaming *stream,
+ struct v4l2_selection *sel)
+{
+ if (sel->r.left > USHRT_MAX)
+ sel->r.left = 0;
+
+ if (sel->r.top > USHRT_MAX)
+ sel->r.top = 0;
+
+ if (sel->r.width + sel->r.left > USHRT_MAX || !sel->r.width) {
+ sel->r.left = 0;
+ sel->r.width = stream->cur_frame->wWidth;
+ }
+
+ if (sel->r.height + sel->r.top > USHRT_MAX || !sel->r.height) {
+ sel->r.top = 0;
+ sel->r.height = stream->cur_frame->wHeight;
+ }
+}
+
+static int uvc_ioctl_s_roi(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ struct uvc_video_chain *chain = handle->chain;
+ struct uvc_roi_rect *roi;
+ int ret;
+
+ roi = kzalloc(sizeof(struct uvc_roi_rect), GFP_KERNEL);
+ if (!roi)
+ return -ENOMEM;
+
+ /*
+ * Synchronize with uvc_ioctl_query_ext_ctrl() that can set
+ * ROI auto_controls concurrently.
+ */
+ mutex_lock(&chain->ctrl_mutex);
+
+ /*
+ * Get current ROI configuration. We are especially interested in
+ * ->auto_controls, because we will use GET_CUR ->auto_controls
+ * value for SET_CUR. Some firmwares require sizeof(uvc_roi_rect)
+ * to be 5 * sizeof(__u16) so we need to set correct rectangle
+ * dimensions and correct auto_controls value.
+ */
+ ret = uvc_query_ctrl(stream->dev, UVC_GET_CUR, 1, stream->dev->intfnum,
+ UVC_CT_REGION_OF_INTEREST_CONTROL, roi,
+ sizeof(struct uvc_roi_rect));
+ if (ret)
+ goto out;
+
+ mutex_lock(&stream->mutex);
+
+ validate_roi_bounds(stream, sel);
+
+ /*
+ * ROI left, top, right, bottom are global coordinates.
+ * Note that we use ->auto_controls value which we read earlier.
+ */
+ roi->left = sel->r.left;
+ roi->top = sel->r.top;
+ roi->right = sel->r.width + sel->r.left - 1;
+ roi->bottom = sel->r.height + sel->r.top - 1;
+
+ ret = uvc_query_ctrl(stream->dev, UVC_SET_CUR, 1, stream->dev->intfnum,
+ UVC_CT_REGION_OF_INTEREST_CONTROL, roi,
+ sizeof(struct uvc_roi_rect));
+
+ mutex_unlock(&stream->mutex);
+
+out:
+ mutex_unlock(&chain->ctrl_mutex);
+ kfree(roi);
+ return ret;
+}
+
+static int uvc_ioctl_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ if (sel->type != stream->type)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_ROI:
+ return uvc_ioctl_s_roi(file, fh, sel);
+ }
+
+ return -EINVAL;
+}
+
static int uvc_ioctl_g_parm(struct file *file, void *fh,
struct v4l2_streamparm *parm)
{
@@ -1533,6 +1711,7 @@ const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
.vidioc_querymenu = uvc_ioctl_querymenu,
.vidioc_g_selection = uvc_ioctl_g_selection,
+ .vidioc_s_selection = uvc_ioctl_s_selection,
.vidioc_g_parm = uvc_ioctl_g_parm,
.vidioc_s_parm = uvc_ioctl_s_parm,
.vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
--
2.31.1.527.g47e6f16901-goog