| From 41ad631ee528c86d9a20557839929ccf648ea2a6 Mon Sep 17 00:00:00 2001 |
| From: Yunke Cao <yunkec@chromium.org> |
| Date: Mon, 3 Jul 2023 14:49:27 +0900 |
| Subject: [PATCH] BACKPORT:FROMLIST:media: uvcvideo: implement UVC v1.5 ROI |
| |
| Implement support for ROI as described in UVC 1.5: |
| 4.2.2.1.20 Digital Region of Interest (ROI) Control |
| |
| ROI control is implemented using V4L2 control API as |
| two UVC-specific controls: |
| V4L2_CID_UVC_REGION_OF_INTEREST_RECT and |
| V4L2_CID_UVC_REGION_OF_INTEREST_AUTO. |
| |
| Reviewed-by: Ricardo Ribalda <ribalda@chromium.org> |
| Signed-off-by: Yunke Cao <yunkec@google.com> |
| (am from https://patchwork.kernel.org/project/linux-media/patch/20230426082923.132909-10-yunkec@google.com/) |
| |
| conflicts: uvc_ctrl.c |
| |
| UPSTREAM-TASK=b:191930245 |
| BUG=b:191930245 |
| TEST=v4l2-compliance |
| |
| Change-Id: I925473d8d2808c292c506fdaf7f6858970adec63 |
| Signed-off-by: Yunke Cao <yunkec@chromium.org> |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/4663718 |
| Reviewed-by: Ricardo Ribalda <ribalda@chromium.org> |
| (cherry picked from commit 35549cf6d41b026c25f864921a63473f05629cb7) |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/5534431 |
| --- |
| drivers/media/usb/uvc/uvc_ctrl.c | 134 +++++++++++++++++++++++++++-- |
| drivers/media/usb/uvc/uvc_v4l2.c | 5 +- |
| drivers/media/usb/uvc/uvcvideo.h | 12 ++- |
| include/uapi/linux/usb/video.h | 1 + |
| include/uapi/linux/uvcvideo.h | 13 +++ |
| include/uapi/linux/v4l2-controls.h | 9 ++ |
| 6 files changed, 166 insertions(+), 8 deletions(-) |
| |
| diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c |
| index 5511a4d99f3753403756ba9e4b8f92aa6e12496e..f5a04b9ab9866f8bfc48bd59345302093ce20441 100644 |
| --- a/drivers/media/usb/uvc/uvc_ctrl.c |
| +++ b/drivers/media/usb/uvc/uvc_ctrl.c |
| @@ -358,6 +358,24 @@ static const struct uvc_control_info uvc_ctrls[] = { |
| .flags = UVC_CTRL_FLAG_GET_CUR |
| | UVC_CTRL_FLAG_AUTO_UPDATE, |
| }, |
| + /* |
| + * UVC_CTRL_FLAG_AUTO_UPDATE is needed because the RoI may get updated |
| + * by sensors. |
| + * "This RoI should be the same as specified in most recent SET_CUR |
| + * except in the case where the 'Auto Detect and Track' and/or |
| + * 'Image Stabilization' bit have been set." |
| + * 4.2.2.1.20 Digital Region of Interest (ROI) Control |
| + */ |
| + { |
| + .entity = UVC_GUID_UVC_CAMERA, |
| + .selector = UVC_CT_REGION_OF_INTEREST_CONTROL, |
| + .index = 21, |
| + .size = 10, |
| + .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR |
| + | UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
| + | UVC_CTRL_FLAG_GET_DEF |
| + | UVC_CTRL_FLAG_AUTO_UPDATE, |
| + }, |
| }; |
| |
| static const u32 uvc_control_classes[] = { |
| @@ -547,6 +565,70 @@ static const struct uvc_control_mapping *uvc_ctrl_filter_plf_mapping( |
| return out_mapping; |
| } |
| |
| +static int uvc_to_v4l2_rect(struct v4l2_rect *v4l2_rect, |
| + const struct uvc_rect *uvc_rect) |
| +{ |
| + if (uvc_rect->bottom < uvc_rect->top || |
| + uvc_rect->right < uvc_rect->left) |
| + return -EINVAL; |
| + |
| + v4l2_rect->top = uvc_rect->top; |
| + v4l2_rect->left = uvc_rect->left; |
| + v4l2_rect->height = uvc_rect->bottom - uvc_rect->top + 1; |
| + v4l2_rect->width = uvc_rect->right - uvc_rect->left + 1; |
| + return 0; |
| +} |
| + |
| +static int v4l2_to_uvc_rect(struct uvc_rect *uvc_rect, |
| + const struct v4l2_rect *min_rect, |
| + const struct v4l2_rect *max_rect, |
| + struct v4l2_rect *v4l2_rect) |
| +{ |
| + v4l2_rect->left = clamp_t(s32, v4l2_rect->left, 0, max_rect->width); |
| + v4l2_rect->top = clamp_t(s32, v4l2_rect->top, 0, max_rect->height); |
| + v4l2_rect->height = clamp_t(s32, v4l2_rect->height, |
| + min_rect->height, max_rect->height); |
| + v4l2_rect->width = clamp_t(s32, v4l2_rect->width, |
| + min_rect->width, max_rect->width); |
| + |
| + uvc_rect->top = v4l2_rect->top; |
| + uvc_rect->left = v4l2_rect->left; |
| + uvc_rect->bottom = v4l2_rect->height + v4l2_rect->top - 1; |
| + uvc_rect->right = v4l2_rect->width + v4l2_rect->left - 1; |
| + return 0; |
| +} |
| + |
| +static int uvc_get_compound_rect(struct uvc_control_mapping *mapping, |
| + const u8 *data, u8 *data_out) |
| +{ |
| + struct uvc_rect *uvc_rect; |
| + |
| + uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8); |
| + return uvc_to_v4l2_rect((struct v4l2_rect *)data_out, uvc_rect); |
| +} |
| + |
| +static int uvc_set_compound_rect(struct uvc_control_mapping *mapping, |
| + const u8 *data_in, const u8 *data_min, |
| + const u8 *data_max, u8 *data) |
| +{ |
| + struct uvc_rect *uvc_rect; |
| + struct v4l2_rect min_rect, max_rect; |
| + int ret; |
| + |
| + uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8); |
| + |
| + ret = uvc_get_compound_rect(mapping, data_min, (u8 *)&min_rect); |
| + if (ret) |
| + return ret; |
| + |
| + ret = uvc_get_compound_rect(mapping, data_max, (u8 *)&max_rect); |
| + if (ret) |
| + return ret; |
| + |
| + return v4l2_to_uvc_rect(uvc_rect, &min_rect, &max_rect, |
| + (struct v4l2_rect *)data_in); |
| +} |
| + |
| static const struct uvc_control_mapping uvc_ctrl_mappings[] = { |
| { |
| .id = V4L2_CID_BRIGHTNESS, |
| @@ -841,6 +923,29 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { |
| .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, |
| .filter_mapping = uvc_ctrl_filter_plf_mapping, |
| }, |
| + { |
| + .id = V4L2_CID_UVC_REGION_OF_INTEREST_RECT, |
| + .entity = UVC_GUID_UVC_CAMERA, |
| + .selector = UVC_CT_REGION_OF_INTEREST_CONTROL, |
| + .v4l2_size = sizeof(struct v4l2_rect) * 8, |
| + .data_size = sizeof(struct uvc_rect) * 8, |
| + .offset = 0, |
| + .v4l2_type = V4L2_CTRL_TYPE_RECT, |
| + .data_type = UVC_CTRL_DATA_TYPE_RECT, |
| + .get_compound = uvc_get_compound_rect, |
| + .set_compound = uvc_set_compound_rect, |
| + .name = "Region Of Interest Rectangle", |
| + }, |
| + { |
| + .id = V4L2_CID_UVC_REGION_OF_INTEREST_AUTO, |
| + .entity = UVC_GUID_UVC_CAMERA, |
| + .selector = UVC_CT_REGION_OF_INTEREST_CONTROL, |
| + .data_size = 16, |
| + .offset = 64, |
| + .v4l2_type = V4L2_CTRL_TYPE_BITMASK, |
| + .data_type = UVC_CTRL_DATA_TYPE_BITMASK, |
| + .name = "Region Of Interest Auto Controls", |
| + }, |
| }; |
| |
| /* ------------------------------------------------------------------------ |
| @@ -947,7 +1052,8 @@ static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data, |
| * and mapping->data_size stored at 'data'. |
| */ |
| static int uvc_set_compound(struct uvc_control_mapping *mapping, |
| - const u8 *data_in, u8 *data) |
| + const u8 *data_in, const u8 *data_min, |
| + const u8 *data_max, u8 *data) |
| { |
| memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8); |
| return 0; |
| @@ -2139,6 +2245,8 @@ static int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping, |
| goto out; |
| |
| ret = mapping->set_compound(mapping, data, |
| + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN), |
| + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX), |
| uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); |
| |
| __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT, xctrl); |
| @@ -2267,6 +2375,13 @@ int uvc_ctrl_set(struct uvc_fh *handle, |
| mapping->set(mapping, value, |
| uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); |
| } else { |
| + /* Populates min/max value cache for clamping. */ |
| + if (!ctrl->cached) { |
| + ret = uvc_ctrl_populate_cache(chain, ctrl); |
| + if (ret < 0) |
| + return ret; |
| + } |
| + |
| ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl); |
| if (ret < 0) |
| return ret; |
| @@ -2689,12 +2804,21 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain, |
| } |
| |
| if (uvc_ctrl_mapping_is_compound(map)) { |
| - if (map->data_size != map->v4l2_size) |
| + switch (map->v4l2_type) { |
| + case V4L2_CTRL_TYPE_RECT: |
| + /* Only supports 4 bytes-aligned data. */ |
| + if (WARN_ON(map->offset % 32)) |
| + return -EINVAL; |
| + break; |
| + default: |
| + if (WARN_ON(map->data_size != map->v4l2_size)) |
| return -EINVAL; |
| |
| - /* Only supports byte-aligned data. */ |
| - if (WARN_ON(map->offset % 8 || map->data_size % 8)) |
| - return -EINVAL; |
| + /* Only supports byte-aligned data. */ |
| + if (WARN_ON(map->offset % 8 || map->data_size % 8)) |
| + return -EINVAL; |
| + } |
| + |
| } |
| |
| if (!map->get && !uvc_ctrl_mapping_is_compound(map)) |
| diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c |
| index 2aec182a780fe1e3532694c5dce8af9b899e5d03..687652301d87783165f835741fdf2d053bccd76d 100644 |
| --- a/drivers/media/usb/uvc/uvc_v4l2.c |
| +++ b/drivers/media/usb/uvc/uvc_v4l2.c |
| @@ -1053,7 +1053,10 @@ static int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh, |
| qec->step = qc.step; |
| qec->default_value = qc.default_value; |
| qec->flags = qc.flags; |
| - qec->elem_size = 4; |
| + if (qc.type == V4L2_CTRL_TYPE_RECT) |
| + qec->elem_size = sizeof(struct v4l2_rect); |
| + else |
| + qec->elem_size = 4; |
| qec->elems = 1; |
| qec->nr_of_dims = 0; |
| memset(qec->dims, 0, sizeof(qec->dims)); |
| diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h |
| index b554a12cbaf5d676606a80abfb6247e912a64847..edf5b4f9fb25c5f3d7dff83f05f478ff14ffdb69 100644 |
| --- a/drivers/media/usb/uvc/uvcvideo.h |
| +++ b/drivers/media/usb/uvc/uvcvideo.h |
| @@ -142,8 +142,9 @@ struct uvc_control_mapping { |
| u8 *data_out); |
| void (*set)(struct uvc_control_mapping *mapping, s32 value, |
| u8 *data); |
| - int (*set_compound)(struct uvc_control_mapping *mapping, const u8 *data_in, |
| - u8 *data); |
| + int (*set_compound)(struct uvc_control_mapping *mapping, |
| + const u8 *data_in, const u8 *data_min, |
| + const u8 *data_max, u8 *data); |
| }; |
| |
| struct uvc_control { |
| @@ -309,6 +310,13 @@ struct uvc_streaming_header { |
| u8 bTriggerUsage; |
| }; |
| |
| +struct uvc_rect { |
| + u16 top; |
| + u16 left; |
| + u16 bottom; |
| + u16 right; |
| +} __packed; |
| + |
| enum uvc_buffer_state { |
| UVC_BUF_STATE_IDLE = 0, |
| UVC_BUF_STATE_QUEUED = 1, |
| diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h |
| index 2ff0e8a3a683dcd0dec4d2269041096831d88ea9..2afb4420e6c4ac101457295928818086d4ac58ee 100644 |
| --- a/include/uapi/linux/usb/video.h |
| +++ b/include/uapi/linux/usb/video.h |
| @@ -104,6 +104,7 @@ |
| #define UVC_CT_ROLL_ABSOLUTE_CONTROL 0x0f |
| #define UVC_CT_ROLL_RELATIVE_CONTROL 0x10 |
| #define UVC_CT_PRIVACY_CONTROL 0x11 |
| +#define UVC_CT_REGION_OF_INTEREST_CONTROL 0x14 |
| |
| /* A.9.5. Processing Unit Control Selectors */ |
| #define UVC_PU_CONTROL_UNDEFINED 0x00 |
| diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h |
| index f86185456dc5b76c4a4460f7ef06c9481b7617f2..9d1e6085feba9dae2815b2cda885d06ff52d37ea 100644 |
| --- a/include/uapi/linux/uvcvideo.h |
| +++ b/include/uapi/linux/uvcvideo.h |
| @@ -16,6 +16,7 @@ |
| #define UVC_CTRL_DATA_TYPE_BOOLEAN 3 |
| #define UVC_CTRL_DATA_TYPE_ENUM 4 |
| #define UVC_CTRL_DATA_TYPE_BITMASK 5 |
| +#define UVC_CTRL_DATA_TYPE_RECT 6 |
| |
| /* Control flags */ |
| #define UVC_CTRL_FLAG_SET_CUR (1 << 0) |
| @@ -38,6 +39,18 @@ |
| |
| #define UVC_MENU_NAME_LEN 32 |
| |
| +/* V4L2 driver-specific controls */ |
| +#define V4L2_CID_UVC_REGION_OF_INTEREST_RECT (V4L2_CID_CAMERA_UVC_BASE + 1) |
| +#define V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (V4L2_CID_CAMERA_UVC_BASE + 2) |
| +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE (1 << 0) |
| +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS (1 << 1) |
| +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE (1 << 2) |
| +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS (1 << 3) |
| +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT (1 << 4) |
| +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK (1 << 5) |
| +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION (1 << 6) |
| +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY (1 << 7) |
| + |
| struct uvc_menu_info { |
| __u32 value; |
| __u8 name[UVC_MENU_NAME_LEN]; |
| diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h |
| index 974fd254e57309e6def95b4a4f8e4de13a3972a7..c06999e4f8781a345aac336c1e9d57b165dd3f65 100644 |
| --- a/include/uapi/linux/v4l2-controls.h |
| +++ b/include/uapi/linux/v4l2-controls.h |
| @@ -1089,6 +1089,15 @@ enum v4l2_auto_focus_range { |
| |
| #define V4L2_CID_HDR_SENSOR_MODE (V4L2_CID_CAMERA_CLASS_BASE+36) |
| |
| +/* CAMERA-class private control IDs */ |
| + |
| +/* |
| + * The base for the uvc driver controls. |
| + * See linux/uvcvideo.h for the list of controls. |
| + * We reserve 64 controls for this driver. |
| + */ |
| +#define V4L2_CID_CAMERA_UVC_BASE (V4L2_CID_CAMERA_CLASS_BASE + 0x1000) |
| + |
| /* FM Modulator class control IDs */ |
| |
| #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) |
| -- |
| 2.47.0.rc1.288.g06298d1525-goog |
| |