linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v14 00/11] Implement UVC v1.5 ROI
@ 2023-12-01  7:18 Yunke Cao
  2023-12-01  7:18 ` [PATCH v14 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
                   ` (10 more replies)
  0 siblings, 11 replies; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:18 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Hi,

This patch set implements UVC v1.5 region of interest using V4L2
control API.

ROI control is consisted two uvc specific controls.
1. A rectangle control with a newly added type V4L2_CTRL_TYPE_RECT.
2. An auto control with type bitmask.

V4L2_CTRL_WHICH_MIN/MAX_VAL is added to support the rectangle control.

The corresponding v4l-utils series can be found at
https://patchwork.linuxtv.org/project/linux-media/list/?series=11069 .

Tested with v4l2-compliance, v4l2-ctl, calling ioctls on usb cameras and
VIVID with a newly added V4L2_CTRL_TYPE_RECT control.

Hans Verkuil (1):
  v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL

Yunke Cao (10):
  media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  media: uvcvideo: add uvc_ctrl_get_boundary for getting default value
  media: uvcvideo: introduce __uvc_ctrl_get_std()
  media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size
  media: uvcvideo: Add support for compound controls
  media: vivid: Add an rectangle control
  media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
  media: uvcvideo: implement UVC v1.5 ROI
  media: uvcvideo: initilaize ROI control to default value
  media: uvcvideo: document UVC v1.5 ROI

 .../userspace-api/media/drivers/uvcvideo.rst  |  64 +-
 .../media/v4l/vidioc-g-ext-ctrls.rst          |  26 +-
 .../media/v4l/vidioc-queryctrl.rst            |  14 +
 .../media/videodev2.h.rst.exceptions          |   4 +
 drivers/media/i2c/imx214.c                    |   5 +-
 .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
 .../media/test-drivers/vivid/vivid-ctrls.c    |  34 +
 drivers/media/usb/uvc/uvc_ctrl.c              | 721 ++++++++++++++----
 drivers/media/usb/uvc/uvc_v4l2.c              |  18 +-
 drivers/media/usb/uvc/uvcvideo.h              |  24 +-
 drivers/media/v4l2-core/v4l2-ctrls-api.c      |  54 +-
 drivers/media/v4l2-core/v4l2-ctrls-core.c     | 167 +++-
 drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
 include/media/v4l2-ctrls.h                    |  38 +-
 include/uapi/linux/usb/video.h                |   1 +
 include/uapi/linux/uvcvideo.h                 |  13 +
 include/uapi/linux/v4l2-controls.h            |   9 +
 include/uapi/linux/videodev2.h                |   5 +
 18 files changed, 997 insertions(+), 213 deletions(-)

-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH v14 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
@ 2023-12-01  7:18 ` Yunke Cao
  2023-12-01  8:35   ` Hans Verkuil
  2023-12-01  7:18 ` [PATCH v14 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value Yunke Cao
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:18 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Add p_rect to struct v4l2_ext_control with basic support in
v4l2-ctrls.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Signed-off-by: Yunke Cao <yunkec@google.com>
Reviewed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
Changelog since v12:
- No Change.
Changelog since v11:
- Added reviewed-by from Hans
Changelog since v10:
- Added reviewed-by from Sergey and Daniel.
Changelog since v9:
- No Change.
Changelog since v8:
- No change.
Changelog since v7:
- Document V4L2_CTRL_TYPE_RECT in vidioc-queryctrl.rst.
- Rebased to media-stage master.
- Do not assign each field in std_equal

 .../media/v4l/vidioc-g-ext-ctrls.rst             |  4 ++++
 .../userspace-api/media/v4l/vidioc-queryctrl.rst |  7 +++++++
 .../media/videodev2.h.rst.exceptions             |  1 +
 drivers/media/v4l2-core/v4l2-ctrls-core.c        | 16 +++++++++++++++-
 include/media/v4l2-ctrls.h                       |  2 ++
 include/uapi/linux/videodev2.h                   |  2 ++
 6 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
index f9f73530a6be..7b1001d11f9c 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
@@ -199,6 +199,10 @@ still cause this situation.
       - ``p_area``
       - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is
         of type ``V4L2_CTRL_TYPE_AREA``.
+    * - struct :c:type:`v4l2_rect` *
+      - ``p_rect``
+      - A pointer to a struct :c:type:`v4l2_rect`. Valid if this control is
+        of type ``V4L2_CTRL_TYPE_RECT``.
     * - struct :c:type:`v4l2_ctrl_h264_sps` *
       - ``p_h264_sps``
       - A pointer to a struct :c:type:`v4l2_ctrl_h264_sps`. Valid if this control is
diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
index 4d38acafe8e1..56d5c8b0b88b 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
@@ -441,6 +441,13 @@ See also the examples in :ref:`control`.
       - n/a
       - A struct :c:type:`v4l2_area`, containing the width and the height
         of a rectangular area. Units depend on the use case.
+    * - ``V4L2_CTRL_TYPE_RECT``
+      - n/a
+      - n/a
+      - n/a
+      - A struct :c:type:`v4l2_rect`, containing a rectangle described by
+	the position of its top-left corner, the width and the height. Units
+	depend on the use case.
     * - ``V4L2_CTRL_TYPE_H264_SPS``
       - n/a
       - n/a
diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
index 3e58aac4ef0b..c46082ef0e4d 100644
--- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
@@ -150,6 +150,7 @@ replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type`
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index a662fb60f73f..f1486ab032cf 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -367,7 +367,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
 	case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
 		pr_cont("AV1_FILM_GRAIN");
 		break;
-
+	case V4L2_CTRL_TYPE_RECT:
+		pr_cont("%ux%u@%dx%d",
+			ptr.p_rect->width, ptr.p_rect->height,
+			ptr.p_rect->left, ptr.p_rect->top);
+		break;
 	default:
 		pr_cont("unknown type %d", ctrl->type);
 		break;
@@ -812,6 +816,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
 	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
 	struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
 	struct v4l2_area *area;
+	struct v4l2_rect *rect;
 	void *p = ptr.p + idx * ctrl->elem_size;
 	unsigned int i;
 
@@ -1169,6 +1174,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
 			return -EINVAL;
 		break;
 
+	case V4L2_CTRL_TYPE_RECT:
+		rect = p;
+		if (!rect->width || !rect->height)
+			return -EINVAL;
+		break;
+
 	default:
 		return -EINVAL;
 	}
@@ -1868,6 +1879,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 	case V4L2_CTRL_TYPE_AREA:
 		elem_size = sizeof(struct v4l2_area);
 		break;
+	case V4L2_CTRL_TYPE_RECT:
+		elem_size = sizeof(struct v4l2_rect);
+		break;
 	default:
 		if (type < V4L2_CTRL_COMPOUND_TYPES)
 			elem_size = sizeof(s32);
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 59679a42b3e7..b0db167a3ac4 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -56,6 +56,7 @@ struct video_device;
  * @p_av1_tile_group_entry:	Pointer to an AV1 tile group entry structure.
  * @p_av1_frame:		Pointer to an AV1 frame structure.
  * @p_av1_film_grain:		Pointer to an AV1 film grain structure.
+ * @p_rect:			Pointer to a rectangle.
  * @p:				Pointer to a compound value.
  * @p_const:			Pointer to a constant compound value.
  */
@@ -89,6 +90,7 @@ union v4l2_ctrl_ptr {
 	struct v4l2_ctrl_av1_tile_group_entry *p_av1_tile_group_entry;
 	struct v4l2_ctrl_av1_frame *p_av1_frame;
 	struct v4l2_ctrl_av1_film_grain *p_av1_film_grain;
+	struct v4l2_rect *p_rect;
 	void *p;
 	const void *p_const;
 };
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index c3d4e490ce7c..82d86abcf89c 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1815,6 +1815,7 @@ struct v4l2_ext_control {
 		__s32 __user *p_s32;
 		__s64 __user *p_s64;
 		struct v4l2_area __user *p_area;
+		struct v4l2_rect __user *p_rect;
 		struct v4l2_ctrl_h264_sps __user *p_h264_sps;
 		struct v4l2_ctrl_h264_pps *p_h264_pps;
 		struct v4l2_ctrl_h264_scaling_matrix __user *p_h264_scaling_matrix;
@@ -1883,6 +1884,7 @@ enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_U16	     = 0x0101,
 	V4L2_CTRL_TYPE_U32	     = 0x0102,
 	V4L2_CTRL_TYPE_AREA          = 0x0106,
+	V4L2_CTRL_TYPE_RECT	     = 0x0107,
 
 	V4L2_CTRL_TYPE_HDR10_CLL_INFO		= 0x0110,
 	V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY	= 0x0111,
-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v14 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
  2023-12-01  7:18 ` [PATCH v14 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
@ 2023-12-01  7:18 ` Yunke Cao
  2023-12-08 15:13   ` Laurent Pinchart
  2023-12-01  7:18 ` [PATCH v14 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() Yunke Cao
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:18 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Introduce uvc_ctrl_get_boundary(), making it easier to extend to
support compound controls and V4L2_CTRL_WHICH_MIN/MAX_VAL in the
following patches.

For now, it simply returns EINVAL for compound controls and calls
__query_v4l2_ctrl() for non-compound controls.

Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
An alternative name is uvc_ctrl_get_static(). Let me know if that's better.

Changelog since v11:
- No change.
Changelog since v10:
- Fix __uvc_ctrl_get_boundary_std() typo causing build error.
- Added Reviewed-by from Sergey.
Changelog since v9:
- Make __uvc_ctrl_get_boundary_std() static.
Changelog since v8:
- No Change.
Changelog since v7:
- Rename uvc_ctrl_get_fixed() to uvc_ctrl_get_boundary().
- Move refactor (introduce  __uvc_ctrl_get_boundary_std()) in this patch
  instead of in the patch where we implement compound control.
- Fix locking. uvc_ctrl_get_boundary() now acquires ctrl_mutex.
  __uvc_ctrl_get_boundary_std() calls __uvc_query_v4l2_ctrl() while
  holding the mutex.
- Define a uvc_ctrl_mapping_is_compound() for readability.

 drivers/media/usb/uvc/uvc_ctrl.c | 48 ++++++++++++++++++++++++++++++++
 drivers/media/usb/uvc/uvc_v4l2.c |  6 +---
 drivers/media/usb/uvc/uvcvideo.h |  2 ++
 3 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index e59a463c2761..8d8333786333 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -884,6 +884,12 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
 	}
 }
 
+static bool
+uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
+{
+	return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
+}
+
 /* ------------------------------------------------------------------------
  * Terminal and unit management
  */
@@ -1880,6 +1886,48 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
 	return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
 }
 
+static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
+				       struct uvc_control *ctrl,
+				       struct uvc_control_mapping *mapping,
+				       struct v4l2_ext_control *xctrl)
+{
+	struct v4l2_queryctrl qc = { .id = xctrl->id };
+
+	int ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &qc);
+
+	if (ret < 0)
+		return ret;
+
+	xctrl->value = qc.default_value;
+	return 0;
+}
+
+int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
+			  struct v4l2_ext_control *xctrl)
+{
+	struct uvc_control *ctrl;
+	struct uvc_control_mapping *mapping;
+	int ret;
+
+	if (mutex_lock_interruptible(&chain->ctrl_mutex))
+		return -ERESTARTSYS;
+
+	ctrl = uvc_find_control(chain, xctrl->id, &mapping);
+	if (!ctrl) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (uvc_ctrl_mapping_is_compound(mapping))
+		ret = -EINVAL;
+	else
+		ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
+
+done:
+	mutex_unlock(&chain->ctrl_mutex);
+	return ret;
+}
+
 int uvc_ctrl_set(struct uvc_fh *handle,
 	struct v4l2_ext_control *xctrl)
 {
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index f4988f03640a..5a88847bfbfe 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1096,15 +1096,11 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
 
 	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
 		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
-			struct v4l2_queryctrl qc = { .id = ctrl->id };
-
-			ret = uvc_query_v4l2_ctrl(chain, &qc);
+			ret = uvc_ctrl_get_boundary(chain, ctrl);
 			if (ret < 0) {
 				ctrls->error_idx = i;
 				return ret;
 			}
-
-			ctrl->value = qc.default_value;
 		}
 
 		return 0;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 6fb0a78b1b00..5091085fcfb0 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -781,6 +781,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
 }
 
 int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
+int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
+			  struct v4l2_ext_control *xctrl);
 int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
 int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
 			   const struct v4l2_ext_controls *ctrls,
-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v14 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std()
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
  2023-12-01  7:18 ` [PATCH v14 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
  2023-12-01  7:18 ` [PATCH v14 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value Yunke Cao
@ 2023-12-01  7:18 ` Yunke Cao
  2023-12-08 15:12   ` Laurent Pinchart
  2023-12-01  7:18 ` [PATCH v14 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size Yunke Cao
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:18 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Refactor uvc_ctrl to make adding compound control easier.

Currently __uvc_ctrl_get() only work for non-compound controls.
Move the logic into __uvc_ctrl_std(), return error for compound
controls.

Make __uvc_ctrl_get() call __uvc_ctrl_std() inside. Also modify some of the
callers of __uvc_ctrl_get() to call __uvc_ctrl_get_std() instead.

Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v12:
- No change.
Changelog since v11:
- Minor change to avoid negative if statement.
Changelog since v10:
- Better commit message.
Changelog since v9:
- No change.
Changelog since v8:
- No change.
Changelog since v7:
- Newly added patch. Split the refactoring of uvc_ctrl_get from v7 3/7.

 drivers/media/usb/uvc/uvc_ctrl.c | 42 +++++++++++++++++++++-----------
 1 file changed, 28 insertions(+), 14 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 8d8333786333..4a685532f7eb 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1081,15 +1081,15 @@ static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
 	return ret;
 }
 
-static int __uvc_ctrl_get(struct uvc_video_chain *chain,
-			  struct uvc_control *ctrl,
-			  struct uvc_control_mapping *mapping,
-			  s32 *value)
+static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
+			      struct uvc_control *ctrl,
+			      struct uvc_control_mapping *mapping,
+			      s32 *value)
 {
 	int ret;
 
-	if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
-		return -EACCES;
+	if (uvc_ctrl_mapping_is_compound(mapping))
+		return -EINVAL;
 
 	ret = __uvc_ctrl_load_cur(chain, ctrl);
 	if (ret < 0)
@@ -1199,7 +1199,7 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
 	if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
 		return 0;
 
-	ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
+	ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
 	if (ret >= 0 && val != mapping->master_manual)
 		return -EACCES;
 
@@ -1264,8 +1264,13 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
 		__uvc_find_control(ctrl->entity, mapping->master_id,
 				   &master_map, &master_ctrl, 0);
 	if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
-		s32 val;
-		int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
+		s32 val = 0;
+		int ret;
+
+		if (uvc_ctrl_mapping_is_compound(master_map))
+			return -EINVAL;
+
+		ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
 		if (ret < 0)
 			return ret;
 
@@ -1532,7 +1537,8 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
 	if (ctrl == NULL)
 		return;
 
-	if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0)
+	if (uvc_ctrl_mapping_is_compound(mapping) ||
+	    __uvc_ctrl_get_std(chain, ctrl, mapping, &val) == 0)
 		changes |= V4L2_EVENT_CTRL_CH_VALUE;
 
 	uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
@@ -1703,7 +1709,8 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
 		u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
 		s32 val = 0;
 
-		if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
+		if (uvc_ctrl_mapping_is_compound(mapping) ||
+		    __uvc_ctrl_get_std(handle->chain, ctrl, mapping, &val) == 0)
 			changes |= V4L2_EVENT_CTRL_CH_VALUE;
 
 		uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
@@ -1883,7 +1890,10 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
 	if (ctrl == NULL)
 		return -EINVAL;
 
-	return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
+	if (uvc_ctrl_mapping_is_compound(mapping))
+		return -EINVAL;
+	else
+		return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
 }
 
 static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
@@ -2042,8 +2052,12 @@ int uvc_ctrl_set(struct uvc_fh *handle,
 		       ctrl->info.size);
 	}
 
-	mapping->set(mapping, value,
-		uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+	if (uvc_ctrl_mapping_is_compound(mapping))
+		return -EINVAL;
+	else
+		mapping->set(mapping, value,
+			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+
 
 	if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
 		ctrl->handle = handle;
-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v14 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
                   ` (2 preceding siblings ...)
  2023-12-01  7:18 ` [PATCH v14 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() Yunke Cao
@ 2023-12-01  7:18 ` Yunke Cao
  2023-12-08 14:15   ` Laurent Pinchart
  2023-12-01  7:18 ` [PATCH v14 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:18 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Rename the existing size to data_size to represent uvc control data size,
add a separate field for v4l2 control size. v4l2 control size will be
used the compound controls.

Also modify the uvc driver documents to clarify the size in
uvc_xu_control_mapping corresponds to the uvc control data size.

Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- No change.
Changelog since v10:
- Added Reviewed-by from Daniel Scally.
Changelog since v9:
- No change.
Changelog since v8:
- No change.
Changelog since v7:
- Newly added patch.

 .../userspace-api/media/drivers/uvcvideo.rst  |  2 +-
 drivers/media/usb/uvc/uvc_ctrl.c              | 80 +++++++++----------
 drivers/media/usb/uvc/uvc_v4l2.c              |  2 +-
 drivers/media/usb/uvc/uvcvideo.h              |  6 +-
 4 files changed, 47 insertions(+), 43 deletions(-)

diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
index a290f9fadae9..aab4304e6bb5 100644
--- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
+++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
@@ -157,7 +157,7 @@ Argument: struct uvc_xu_control_mapping
 	__u8	name[32]	V4L2 control name
 	__u8	entity[16]	UVC extension unit GUID
 	__u8	selector	UVC control selector
-	__u8	size		V4L2 control size (in bits)
+	__u8	size		UVC control data size (in bits)
 	__u8	offset		V4L2 control offset (in bits)
 	enum v4l2_ctrl_type
 		v4l2_type	V4L2 control type
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 4a685532f7eb..98254b93eb46 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -464,7 +464,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_BRIGHTNESS,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_BRIGHTNESS_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
@@ -473,7 +473,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_CONTRAST,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_CONTRAST_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -482,7 +482,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_HUE,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_HUE_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
@@ -493,7 +493,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_SATURATION,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_SATURATION_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -502,7 +502,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_SHARPNESS,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_SHARPNESS_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -511,7 +511,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_GAMMA,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_GAMMA_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -520,7 +520,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_BACKLIGHT_COMPENSATION,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -529,7 +529,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_GAIN,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_GAIN_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -538,7 +538,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_HUE_AUTO,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_HUE_AUTO_CONTROL,
-		.size		= 1,
+		.data_size	= 1,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -548,7 +548,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_EXPOSURE_AUTO,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_AE_MODE_CONTROL,
-		.size		= 4,
+		.data_size	= 4,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_MENU,
 		.data_type	= UVC_CTRL_DATA_TYPE_BITMASK,
@@ -561,7 +561,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_EXPOSURE_AUTO_PRIORITY,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_AE_PRIORITY_CONTROL,
-		.size		= 1,
+		.data_size	= 1,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -570,7 +570,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_EXPOSURE_ABSOLUTE,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
-		.size		= 32,
+		.data_size	= 32,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -581,7 +581,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_AUTO_WHITE_BALANCE,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
-		.size		= 1,
+		.data_size	= 1,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -591,7 +591,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_WHITE_BALANCE_TEMPERATURE,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -602,7 +602,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_AUTO_WHITE_BALANCE,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
-		.size		= 1,
+		.data_size	= 1,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -613,7 +613,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_BLUE_BALANCE,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
@@ -624,7 +624,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_RED_BALANCE,
 		.entity		= UVC_GUID_UVC_PROCESSING,
 		.selector	= UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 16,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
@@ -635,7 +635,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_FOCUS_ABSOLUTE,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_FOCUS_ABSOLUTE_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -646,7 +646,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_FOCUS_AUTO,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_FOCUS_AUTO_CONTROL,
-		.size		= 1,
+		.data_size	= 1,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -656,7 +656,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_IRIS_ABSOLUTE,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_IRIS_ABSOLUTE_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -665,7 +665,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_IRIS_RELATIVE,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_IRIS_RELATIVE_CONTROL,
-		.size		= 8,
+		.data_size	= 8,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
@@ -674,7 +674,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_ZOOM_ABSOLUTE,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_ZOOM_ABSOLUTE_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -683,7 +683,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_ZOOM_CONTINUOUS,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_ZOOM_RELATIVE_CONTROL,
-		.size		= 0,
+		.data_size	= 0,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
@@ -694,7 +694,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_PAN_ABSOLUTE,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_PANTILT_ABSOLUTE_CONTROL,
-		.size		= 32,
+		.data_size	= 32,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
@@ -703,7 +703,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_TILT_ABSOLUTE,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_PANTILT_ABSOLUTE_CONTROL,
-		.size		= 32,
+		.data_size	= 32,
 		.offset		= 32,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
@@ -712,7 +712,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_PAN_SPEED,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_PANTILT_RELATIVE_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
@@ -723,7 +723,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_TILT_SPEED,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_PANTILT_RELATIVE_CONTROL,
-		.size		= 16,
+		.data_size	= 16,
 		.offset		= 16,
 		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
 		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
@@ -734,7 +734,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_PRIVACY,
 		.entity		= UVC_GUID_UVC_CAMERA,
 		.selector	= UVC_CT_PRIVACY_CONTROL,
-		.size		= 1,
+		.data_size	= 1,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -743,7 +743,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.id		= V4L2_CID_PRIVACY,
 		.entity		= UVC_GUID_EXT_GPIO_CONTROLLER,
 		.selector	= UVC_CT_PRIVACY_CONTROL,
-		.size		= 1,
+		.data_size	= 1,
 		.offset		= 0,
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -754,7 +754,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
 	.id		= V4L2_CID_POWER_LINE_FREQUENCY,
 	.entity		= UVC_GUID_UVC_PROCESSING,
 	.selector	= UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
-	.size		= 2,
+	.data_size	= 2,
 	.offset		= 0,
 	.v4l2_type	= V4L2_CTRL_TYPE_MENU,
 	.data_type	= UVC_CTRL_DATA_TYPE_ENUM,
@@ -766,7 +766,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
 	.id		= V4L2_CID_POWER_LINE_FREQUENCY,
 	.entity		= UVC_GUID_UVC_PROCESSING,
 	.selector	= UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
-	.size		= 2,
+	.data_size	= 2,
 	.offset		= 0,
 	.v4l2_type	= V4L2_CTRL_TYPE_MENU,
 	.data_type	= UVC_CTRL_DATA_TYPE_ENUM,
@@ -783,7 +783,7 @@ static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
 	.id		= V4L2_CID_POWER_LINE_FREQUENCY,
 	.entity		= UVC_GUID_UVC_PROCESSING,
 	.selector	= UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
-	.size		= 2,
+	.data_size	= 2,
 	.offset		= 0,
 	.v4l2_type	= V4L2_CTRL_TYPE_MENU,
 	.data_type	= UVC_CTRL_DATA_TYPE_ENUM,
@@ -816,7 +816,7 @@ static inline void uvc_clear_bit(u8 *data, int bit)
 }
 
 /*
- * Extract the bit string specified by mapping->offset and mapping->size
+ * Extract the bit string specified by mapping->offset and mapping->data_size
  * from the little-endian data stored at 'data' and return the result as
  * a signed 32bit integer. Sign extension will be performed if the mapping
  * references a signed data type.
@@ -824,7 +824,7 @@ static inline void uvc_clear_bit(u8 *data, int bit)
 static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
 	u8 query, const u8 *data)
 {
-	int bits = mapping->size;
+	int bits = mapping->data_size;
 	int offset = mapping->offset;
 	s32 value = 0;
 	u8 mask;
@@ -847,19 +847,19 @@ static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
 
 	/* Sign-extend the value if needed. */
 	if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
-		value |= -(value & (1 << (mapping->size - 1)));
+		value |= -(value & (1 << (mapping->data_size - 1)));
 
 	return value;
 }
 
 /*
- * Set the bit string specified by mapping->offset and mapping->size
+ * Set the bit string specified by mapping->offset and mapping->data_size
  * in the little-endian data stored at 'data' to the value 'value'.
  */
 static void uvc_set_le_value(struct uvc_control_mapping *mapping,
 	s32 value, u8 *data)
 {
-	int bits = mapping->size;
+	int bits = mapping->data_size;
 	int offset = mapping->offset;
 	u8 mask;
 
@@ -2039,7 +2039,7 @@ int uvc_ctrl_set(struct uvc_fh *handle,
 	 * needs to be loaded from the device to perform the read-modify-write
 	 * operation.
 	 */
-	if ((ctrl->info.size * 8) != mapping->size) {
+	if ((ctrl->info.size * 8) != mapping->data_size) {
 		ret = __uvc_ctrl_load_cur(chain, ctrl);
 		if (ret < 0)
 			return ret;
@@ -2546,8 +2546,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
 	}
 
 	/* Validate the user-provided bit-size and offset */
-	if (mapping->size > 32 ||
-	    mapping->offset + mapping->size > ctrl->info.size * 8) {
+	if (mapping->data_size > 32 ||
+	    mapping->offset + mapping->data_size > ctrl->info.size * 8) {
 		ret = -EINVAL;
 		goto done;
 	}
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 5a88847bfbfe..ff72caf7bc9e 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -122,7 +122,7 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
 	}
 	memcpy(map->entity, xmap->entity, sizeof(map->entity));
 	map->selector = xmap->selector;
-	map->size = xmap->size;
+	map->data_size = xmap->size;
 	map->offset = xmap->offset;
 	map->v4l2_type = xmap->v4l2_type;
 	map->data_type = xmap->data_type;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 5091085fcfb0..7bc41270ed94 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -110,7 +110,11 @@ struct uvc_control_mapping {
 	u8 entity[16];
 	u8 selector;
 
-	u8 size;
+	/* Size of the v4l2 control. Required for compound controls. */
+	u8 v4l2_size;
+	/* UVC data size. Required for all controls. */
+	u8 data_size;
+
 	u8 offset;
 	enum v4l2_ctrl_type v4l2_type;
 	u32 data_type;
-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v14 05/11] media: uvcvideo: Add support for compound controls
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
                   ` (3 preceding siblings ...)
  2023-12-01  7:18 ` [PATCH v14 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size Yunke Cao
@ 2023-12-01  7:18 ` Yunke Cao
  2023-12-06  5:45   ` Dan Carpenter
  2023-12-08 15:07   ` Laurent Pinchart
  2023-12-01  7:18 ` [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
                   ` (5 subsequent siblings)
  10 siblings, 2 replies; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:18 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Supports getting/setting current value.
Supports getting default value.
Handles V4L2_CTRL_FLAG_NEXT_COMPOUND.

Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- No change.
Changelog since v10:
- Rewrite some logic in __uvc_find_control().
- Remove a duplicate check in __uvc_ctrl_get_compound_cur().
- Thanks, Daniel!
Changelog since v9:
- Make __uvc_ctrl_set_compound() static.
Changelog since v8:
- No change.
Changelog since v7:
- Fixed comments styles, indentation and a few other style issues.
- Renamed uvc_g/set_array() to uvc_g/set_compound().
- Moved size check to __uvc_ctrl_add_mapping().
- After setting a new value, copy it back to user.
- In __uvc_ctrl_set_compound(), check size before allocating.

 drivers/media/usb/uvc/uvc_ctrl.c | 179 +++++++++++++++++++++++++++----
 drivers/media/usb/uvc/uvcvideo.h |   4 +
 2 files changed, 164 insertions(+), 19 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 98254b93eb46..aae2480496b7 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -884,6 +884,28 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
 	}
 }
 
+/*
+ * Extract the byte array specified by mapping->offset and mapping->data_size
+ * stored at 'data' to the output array 'data_out'.
+ */
+static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
+			    u8 *data_out)
+{
+	memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
+	return 0;
+}
+
+/*
+ * Copy the byte array 'data_in' to the destination specified by mapping->offset
+ * and mapping->data_size stored at 'data'.
+ */
+static int uvc_set_compound(struct uvc_control_mapping *mapping,
+			    const u8 *data_in, u8 *data)
+{
+	memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
+	return 0;
+}
+
 static bool
 uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
 {
@@ -906,7 +928,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
 
 static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
 	struct uvc_control_mapping **mapping, struct uvc_control **control,
-	int next)
+	int next, int next_compound)
 {
 	struct uvc_control *ctrl;
 	struct uvc_control_mapping *map;
@@ -921,14 +943,16 @@ static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
 			continue;
 
 		list_for_each_entry(map, &ctrl->info.mappings, list) {
-			if ((map->id == v4l2_id) && !next) {
+			if (map->id == v4l2_id && !next && !next_compound) {
 				*control = ctrl;
 				*mapping = map;
 				return;
 			}
 
 			if ((*mapping == NULL || (*mapping)->id > map->id) &&
-			    (map->id > v4l2_id) && next) {
+			    (map->id > v4l2_id) &&
+			    (uvc_ctrl_mapping_is_compound(map) ?
+			     next_compound : next)) {
 				*control = ctrl;
 				*mapping = map;
 			}
@@ -942,6 +966,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
 	struct uvc_control *ctrl = NULL;
 	struct uvc_entity *entity;
 	int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
+	int next_compound = v4l2_id & V4L2_CTRL_FLAG_NEXT_COMPOUND;
 
 	*mapping = NULL;
 
@@ -950,12 +975,13 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
 
 	/* Find the control. */
 	list_for_each_entry(entity, &chain->entities, chain) {
-		__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
-		if (ctrl && !next)
+		__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next,
+				   next_compound);
+		if (ctrl && !next && !next_compound)
 			return ctrl;
 	}
 
-	if (ctrl == NULL && !next)
+	if (!ctrl && !next && !next_compound)
 		uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
 			v4l2_id);
 
@@ -1101,12 +1127,59 @@ static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
 	return 0;
 }
 
+static int __uvc_ctrl_get_compound(struct uvc_control_mapping *mapping,
+				   struct uvc_control *ctrl,
+				   int id,
+				   struct v4l2_ext_control *xctrl)
+{
+	u8 size;
+	u8 *data;
+	int ret;
+
+	size = mapping->v4l2_size / 8;
+	if (xctrl->size < size) {
+		xctrl->size = size;
+		return -ENOSPC;
+	}
+
+	data = kmalloc(size, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	ret = mapping->get_compound(mapping, uvc_ctrl_data(ctrl, id), data);
+	if (ret < 0)
+		goto out;
+
+	ret = copy_to_user(xctrl->ptr, data, size) ? -EFAULT : 0;
+
+out:
+	kfree(data);
+	return ret;
+}
+
+static int __uvc_ctrl_get_compound_cur(struct uvc_video_chain *chain,
+				       struct uvc_control *ctrl,
+				       struct uvc_control_mapping *mapping,
+				       struct v4l2_ext_control *xctrl)
+{
+	int ret;
+
+	ret = __uvc_ctrl_load_cur(chain, ctrl);
+	if (ret < 0)
+		return ret;
+
+	return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT,
+				       xctrl);
+}
+
 static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
 				  u32 found_id)
 {
-	bool find_next = req_id & V4L2_CTRL_FLAG_NEXT_CTRL;
 	unsigned int i;
 
+	bool find_next = req_id &
+		(V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND);
+
 	req_id &= V4L2_CTRL_ID_MASK;
 
 	for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
@@ -1194,7 +1267,7 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
 	}
 
 	__uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
-			   &master_ctrl, 0);
+			   &master_ctrl, 0, 0);
 
 	if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
 		return 0;
@@ -1262,7 +1335,7 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
 
 	if (mapping->master_id)
 		__uvc_find_control(ctrl->entity, mapping->master_id,
-				   &master_map, &master_ctrl, 0);
+				   &master_map, &master_ctrl, 0, 0);
 	if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
 		s32 val = 0;
 		int ret;
@@ -1278,6 +1351,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
 				v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
 	}
 
+	if (v4l2_ctrl->type >= V4L2_CTRL_COMPOUND_TYPES) {
+		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
+		v4l2_ctrl->default_value = 0;
+		v4l2_ctrl->minimum = 0;
+		v4l2_ctrl->maximum = 0;
+		v4l2_ctrl->step = 0;
+		return 0;
+	}
+
 	if (!ctrl->cached) {
 		int ret = uvc_ctrl_populate_cache(chain, ctrl);
 		if (ret < 0)
@@ -1533,7 +1615,7 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
 	u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
 	s32 val = 0;
 
-	__uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
+	__uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0, 0);
 	if (ctrl == NULL)
 		return;
 
@@ -1843,7 +1925,7 @@ static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
 
 	for (i = 0; i < ctrls->count; i++) {
 		__uvc_find_control(entity, ctrls->controls[i].id, &mapping,
-				   &ctrl_found, 0);
+				   &ctrl_found, 0, 0);
 		if (uvc_control == ctrl_found)
 			return i;
 	}
@@ -1891,7 +1973,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
 		return -EINVAL;
 
 	if (uvc_ctrl_mapping_is_compound(mapping))
-		return -EINVAL;
+		return __uvc_ctrl_get_compound_cur(chain, ctrl, mapping, xctrl);
 	else
 		return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
 }
@@ -1912,6 +1994,22 @@ static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
 	return 0;
 }
 
+static int __uvc_ctrl_get_boundary_compound(struct uvc_video_chain *chain,
+					    struct uvc_control *ctrl,
+					    struct uvc_control_mapping *mapping,
+					    struct v4l2_ext_control *xctrl)
+{
+	int ret;
+
+	if (!ctrl->cached) {
+		ret = uvc_ctrl_populate_cache(chain, ctrl);
+		if (ret < 0)
+			return ret;
+	}
+
+	return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_DEF, xctrl);
+}
+
 int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
 			  struct v4l2_ext_control *xctrl)
 {
@@ -1929,7 +2027,8 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
 	}
 
 	if (uvc_ctrl_mapping_is_compound(mapping))
-		ret = -EINVAL;
+		ret = __uvc_ctrl_get_boundary_compound(chain, ctrl, mapping,
+						       xctrl);
 	else
 		ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
 
@@ -1938,6 +2037,34 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
 	return ret;
 }
 
+static int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
+				   struct v4l2_ext_control *xctrl,
+				   struct uvc_control *ctrl)
+{
+	u8 *data;
+	int ret;
+
+	if (xctrl->size != mapping->v4l2_size / 8)
+		return -EINVAL;
+
+	data = kmalloc(xctrl->size, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	ret = copy_from_user(data, xctrl->ptr, xctrl->size);
+	if (ret < 0)
+		goto out;
+
+	ret = mapping->set_compound(mapping, data,
+			uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+
+	__uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT, xctrl);
+
+out:
+	kfree(data);
+	return ret;
+}
+
 int uvc_ctrl_set(struct uvc_fh *handle,
 	struct v4l2_ext_control *xctrl)
 {
@@ -2052,13 +2179,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
 		       ctrl->info.size);
 	}
 
-	if (uvc_ctrl_mapping_is_compound(mapping))
-		return -EINVAL;
-	else
+	if (uvc_ctrl_mapping_is_compound(mapping)) {
+		ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
+		if (ret < 0)
+			return ret;
+	} else
 		mapping->set(mapping, value,
 			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
 
-
 	if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
 		ctrl->handle = handle;
 
@@ -2468,10 +2596,23 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
 			goto err_nomem;
 	}
 
-	if (map->get == NULL)
+	if (uvc_ctrl_mapping_is_compound(map)) {
+		if (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;
+	}
+
+	if (!map->get && !uvc_ctrl_mapping_is_compound(map))
 		map->get = uvc_get_le_value;
-	if (map->set == NULL)
+	if (!map->set && !uvc_ctrl_mapping_is_compound(map))
 		map->set = uvc_set_le_value;
+	if (!map->get_compound && uvc_ctrl_mapping_is_compound(map))
+		map->get_compound = uvc_get_compound;
+	if (!map->set_compound && uvc_ctrl_mapping_is_compound(map))
+		map->set_compound = uvc_set_compound;
 
 	for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
 		if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 7bc41270ed94..11805b729c22 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -129,8 +129,12 @@ struct uvc_control_mapping {
 
 	s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
 		   const u8 *data);
+	int (*get_compound)(struct uvc_control_mapping *mapping, const u8 *data,
+			    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);
 };
 
 struct uvc_control {
-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
                   ` (4 preceding siblings ...)
  2023-12-01  7:18 ` [PATCH v14 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
@ 2023-12-01  7:18 ` Yunke Cao
  2023-12-01  8:32   ` Hans Verkuil
                     ` (5 more replies)
  2023-12-01  7:18 ` [PATCH v14 07/11] media: vivid: Add an rectangle control Yunke Cao
                   ` (4 subsequent siblings)
  10 siblings, 6 replies; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:18 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

From: Hans Verkuil <hverkuil-cisco@xs4all.nl>

Add the capability of retrieving the min and max values of a
compound control.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v13:
- Updated comments of v4l2_ctrl_new_std_compound()
Changelog since v12:
- Addressed comments from Hans.
Changelog since v11:
- Added a flag V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
- Modified std_min/max_compound() to be void function. Moved the check of
  whether WHICH_MIN/MAX_VAL into prepare_ext_ctrls(), and return EINVAL.
- Modified documentations to reflect this change.
Changelog since v10:
- No change.
Changelog since v9:
- No change.
Changelog since v8:
- Return ENODATA when min/max is not implemented. Document this behavior.
- Created a shared helper function __v4l2_ctrl_type_op_init that takes "which"
  as a parameter. Call it in def, min and max operations.
Changelog since v7:
- Document that the definition of the min/max are provided by compound controls
  are defined in control documentation.
- Return error, instead of zeroed memory for v4l2_ctrl_ptr_create(NULL).

 .../media/v4l/vidioc-g-ext-ctrls.rst          |  22 ++-
 .../media/v4l/vidioc-queryctrl.rst            |   9 +-
 .../media/videodev2.h.rst.exceptions          |   3 +
 drivers/media/i2c/imx214.c                    |   5 +-
 .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
 drivers/media/v4l2-core/v4l2-ctrls-api.c      |  54 +++++--
 drivers/media/v4l2-core/v4l2-ctrls-core.c     | 151 +++++++++++++++---
 drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
 include/media/v4l2-ctrls.h                    |  36 ++++-
 include/uapi/linux/videodev2.h                |   3 +
 10 files changed, 248 insertions(+), 48 deletions(-)

diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
index 7b1001d11f9c..0b87c23e66ff 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
@@ -330,14 +330,26 @@ still cause this situation.
       - Which value of the control to get/set/try.
     * - :cspan:`2` ``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of
 	the control, ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
-	value of the control and ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
-	these controls have to be retrieved from a request or tried/set for
-	a request. In the latter case the ``request_fd`` field contains the
+	value of the control, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
+	value of the control, and ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
+	value of the control. ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
+	the control value has to be retrieved from a request or tried/set for
+	a request. In that case the ``request_fd`` field contains the
 	file descriptor of the request that should be used. If the device
 	does not support requests, then ``EACCES`` will be returned.
 
-	When using ``V4L2_CTRL_WHICH_DEF_VAL`` be aware that you can only
-	get the default value of the control, you cannot set or try it.
+	When using ``V4L2_CTRL_WHICH_DEF_VAL``, ``V4L2_CTRL_WHICH_MIN_VAL``
+	or ``V4L2_CTRL_WHICH_MAX_VAL`` be aware that you can only get the
+	default/minimum/maximum value of the control, you cannot set or try it.
+
+	Whether a control supports querying the minimum and maximum values using
+	``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` is indicated
+	by the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. Most non-compound
+	control types support this. For controls with compound types, the
+	definition of minimum/maximum values are provided by
+	the control documentation. If a compound control does not document the
+	meaning of minimum/maximum value, then querying the minimum or maximum
+	value will result in the error code -EINVAL.
 
 	For backwards compatibility you can also use a control class here
 	(see :ref:`ctrl-class`). In that case all controls have to
diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
index 56d5c8b0b88b..b39f7e27bbbe 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
@@ -447,7 +447,10 @@ See also the examples in :ref:`control`.
       - n/a
       - A struct :c:type:`v4l2_rect`, containing a rectangle described by
 	the position of its top-left corner, the width and the height. Units
-	depend on the use case.
+	depend on the use case. Support for ``V4L2_CTRL_WHICH_MIN_VAL`` and
+	``V4L2_CTRL_WHICH_MAX_VAL`` is optional and depends on the
+	``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. See the documentation of
+	the specific control on how to interpret the minimum and maximum values.
     * - ``V4L2_CTRL_TYPE_H264_SPS``
       - n/a
       - n/a
@@ -664,6 +667,10 @@ See also the examples in :ref:`control`.
 	``dims[0]``. So setting the control with a differently sized
 	array will change the ``elems`` field when the control is
 	queried afterwards.
+    * - ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
+      - 0x1000
+      - This control supports getting minimum and maximum values using
+      vidioc_g_ext_ctrls with V4L2_CTRL_WHICH_MIN/MAX_VAL.
 
 Return Value
 ============
diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
index c46082ef0e4d..a417af25e9a4 100644
--- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
@@ -393,6 +393,7 @@ replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags
 replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags
 replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags
 replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags
+replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags
 
 replace define V4L2_CTRL_FLAG_NEXT_CTRL control
 replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control
@@ -567,6 +568,8 @@ ignore define V4L2_CTRL_DRIVER_PRIV
 ignore define V4L2_CTRL_MAX_DIMS
 ignore define V4L2_CTRL_WHICH_CUR_VAL
 ignore define V4L2_CTRL_WHICH_DEF_VAL
+ignore define V4L2_CTRL_WHICH_MIN_VAL
+ignore define V4L2_CTRL_WHICH_MAX_VAL
 ignore define V4L2_CTRL_WHICH_REQUEST_VAL
 ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS
 ignore define V4L2_CID_MAX_CTRLS
diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
index 4f77ea02cc27..926b5cae12d8 100644
--- a/drivers/media/i2c/imx214.c
+++ b/drivers/media/i2c/imx214.c
@@ -998,7 +998,10 @@ static int imx214_probe(struct i2c_client *client)
 	imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls,
 				NULL,
 				V4L2_CID_UNIT_CELL_SIZE,
-				v4l2_ctrl_ptr_create((void *)&unit_size));
+				v4l2_ctrl_ptr_create((void *)&unit_size),
+				v4l2_ctrl_ptr_create(NULL),
+				v4l2_ctrl_ptr_create(NULL));
+
 	ret = imx214->ctrls.error;
 	if (ret) {
 		dev_err(&client->dev, "%s control init failed (%d)\n",
diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
index d9d2a293f3ef..7f370438d655 100644
--- a/drivers/media/platform/qcom/venus/venc_ctrls.c
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -607,11 +607,16 @@ int venc_ctrl_init(struct venus_inst *inst)
 
 	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
 				   V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
-				   v4l2_ctrl_ptr_create(&p_hdr10_cll));
+				   v4l2_ctrl_ptr_create(&p_hdr10_cll),
+				   v4l2_ctrl_ptr_create(NULL),
+				   v4l2_ctrl_ptr_create(NULL));
 
 	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
 				   V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
-				   v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering));
+				   v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
+				   v4l2_ctrl_ptr_create(NULL),
+				   v4l2_ctrl_ptr_create(NULL));
+
 
 	v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
 			       V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
index 002ea6588edf..d022e1ed4835 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
@@ -94,6 +94,22 @@ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
 	return ptr_to_user(c, ctrl, ctrl->p_new);
 }
 
+/* Helper function: copy the minimum control value back to the caller */
+static int min_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
+{
+	ctrl->type_ops->minimum(ctrl, 0, ctrl->p_new);
+
+	return ptr_to_user(c, ctrl, ctrl->p_new);
+}
+
+/* Helper function: copy the maximum control value back to the caller */
+static int max_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
+{
+	ctrl->type_ops->maximum(ctrl, 0, ctrl->p_new);
+
+	return ptr_to_user(c, ctrl, ctrl->p_new);
+}
+
 /* Helper function: copy the caller-provider value as the new control value */
 static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
 {
@@ -229,8 +245,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
 		cs->error_idx = i;
 
 		if (cs->which &&
-		    cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
-		    cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
+		    (cs->which < V4L2_CTRL_WHICH_DEF_VAL ||
+		     cs->which > V4L2_CTRL_WHICH_MAX_VAL) &&
 		    V4L2_CTRL_ID2WHICH(id) != cs->which) {
 			dprintk(vdev,
 				"invalid which 0x%x or control id 0x%x\n",
@@ -259,6 +275,15 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
 			return -EINVAL;
 		}
 
+		if (!(ctrl->flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) &&
+		    (cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
+		     cs->which == V4L2_CTRL_WHICH_MAX_VAL)) {
+			dprintk(vdev,
+				"invalid which 0x%x or control id 0x%x\n",
+				cs->which, id);
+			return -EINVAL;
+		}
+
 		if (ctrl->cluster[0]->ncontrols > 1)
 			have_clusters = true;
 		if (ctrl->cluster[0] != ctrl)
@@ -368,8 +393,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
  */
 static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
 {
-	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
-	    which == V4L2_CTRL_WHICH_REQUEST_VAL)
+	if (which == 0 || (which >= V4L2_CTRL_WHICH_DEF_VAL &&
+			   which <= V4L2_CTRL_WHICH_MAX_VAL))
 		return 0;
 	return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
 }
@@ -389,10 +414,12 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
 	struct v4l2_ctrl_helper *helpers = helper;
 	int ret;
 	int i, j;
-	bool is_default, is_request;
+	bool is_default, is_request, is_min, is_max;
 
 	is_default = (cs->which == V4L2_CTRL_WHICH_DEF_VAL);
 	is_request = (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL);
+	is_min = (cs->which == V4L2_CTRL_WHICH_MIN_VAL);
+	is_max = (cs->which == V4L2_CTRL_WHICH_MAX_VAL);
 
 	cs->error_idx = cs->count;
 	cs->which = V4L2_CTRL_ID2WHICH(cs->which);
@@ -432,13 +459,14 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
 
 		/*
 		 * g_volatile_ctrl will update the new control values.
-		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL and
+		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL,
+		 * V4L2_CTRL_WHICH_MIN_VAL, V4L2_CTRL_WHICH_MAX_VAL and
 		 * V4L2_CTRL_WHICH_REQUEST_VAL. In the case of requests
 		 * it is v4l2_ctrl_request_complete() that copies the
 		 * volatile controls at the time of request completion
 		 * to the request, so you don't want to do that again.
 		 */
-		if (!is_default && !is_request &&
+		if (!is_default && !is_request && !is_min && !is_max &&
 		    ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
 		    (master->has_volatiles && !is_cur_manual(master)))) {
 			for (j = 0; j < master->ncontrols; j++)
@@ -467,6 +495,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
 				ret = -ENOMEM;
 			else if (is_request && ref->p_req_valid)
 				ret = req_to_user(cs->controls + idx, ref);
+			else if (is_min)
+				ret = min_to_user(cs->controls + idx, ref->ctrl);
+			else if (is_max)
+				ret = max_to_user(cs->controls + idx, ref->ctrl);
 			else if (is_volatile)
 				ret = new_to_user(cs->controls + idx, ref->ctrl);
 			else
@@ -564,9 +596,11 @@ int try_set_ext_ctrls_common(struct v4l2_fh *fh,
 
 	cs->error_idx = cs->count;
 
-	/* Default value cannot be changed */
-	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL) {
-		dprintk(vdev, "%s: cannot change default value\n",
+	/* Default/minimum/maximum values cannot be changed */
+	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL ||
+	    cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
+	    cs->which == V4L2_CTRL_WHICH_MAX_VAL) {
+		dprintk(vdev, "%s: cannot change default/min/max value\n",
 			video_device_node_name(vdev));
 		return -EINVAL;
 	}
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index f1486ab032cf..ef418165e88d 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -182,29 +182,66 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
 	}
 }
 
-void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
+static void std_min_compound(const struct v4l2_ctrl *ctrl, u32 idx,
+			    union v4l2_ctrl_ptr ptr)
+{
+	void *p = ptr.p + idx * ctrl->elem_size;
+
+	if (ctrl->p_min.p_const)
+		memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
+	else
+		memset(p, 0, ctrl->elem_size);
+}
+
+static void std_max_compound(const struct v4l2_ctrl *ctrl, u32 idx,
 			    union v4l2_ctrl_ptr ptr)
+{
+	void *p = ptr.p + idx * ctrl->elem_size;
+
+	if (ctrl->p_max.p_const)
+		memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
+	else
+		memset(p, 0, ctrl->elem_size);
+}
+
+static void __v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
+				     u32 which, union v4l2_ctrl_ptr ptr)
 {
 	unsigned int i;
 	u32 tot_elems = ctrl->elems;
 	u32 elems = tot_elems - from_idx;
+	s64 value;
 
-	if (from_idx >= tot_elems)
+	switch (which) {
+	case V4L2_CTRL_WHICH_DEF_VAL:
+		value = ctrl->default_value;
+		break;
+	case V4L2_CTRL_WHICH_MAX_VAL:
+		value = ctrl->maximum;
+		break;
+	case V4L2_CTRL_WHICH_MIN_VAL:
+		value = ctrl->minimum;
+		break;
+	default:
 		return;
+	}
 
 	switch (ctrl->type) {
 	case V4L2_CTRL_TYPE_STRING:
+		if (which == V4L2_CTRL_WHICH_DEF_VAL)
+			value = ctrl->minimum;
+
 		for (i = from_idx; i < tot_elems; i++) {
 			unsigned int offset = i * ctrl->elem_size;
 
-			memset(ptr.p_char + offset, ' ', ctrl->minimum);
-			ptr.p_char[offset + ctrl->minimum] = '\0';
+			memset(ptr.p_char + offset, ' ', value);
+			ptr.p_char[offset + value] = '\0';
 		}
 		break;
 	case V4L2_CTRL_TYPE_INTEGER64:
-		if (ctrl->default_value) {
+		if (value) {
 			for (i = from_idx; i < tot_elems; i++)
-				ptr.p_s64[i] = ctrl->default_value;
+				ptr.p_s64[i] = value;
 		} else {
 			memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
 		}
@@ -214,9 +251,9 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
 	case V4L2_CTRL_TYPE_MENU:
 	case V4L2_CTRL_TYPE_BITMASK:
 	case V4L2_CTRL_TYPE_BOOLEAN:
-		if (ctrl->default_value) {
+		if (value) {
 			for (i = from_idx; i < tot_elems; i++)
-				ptr.p_s32[i] = ctrl->default_value;
+				ptr.p_s32[i] = value;
 		} else {
 			memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
 		}
@@ -226,32 +263,61 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
 		memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
 		break;
 	case V4L2_CTRL_TYPE_U8:
-		memset(ptr.p_u8 + from_idx, ctrl->default_value, elems);
+		memset(ptr.p_u8 + from_idx, value, elems);
 		break;
 	case V4L2_CTRL_TYPE_U16:
-		if (ctrl->default_value) {
+		if (value) {
 			for (i = from_idx; i < tot_elems; i++)
-				ptr.p_u16[i] = ctrl->default_value;
+				ptr.p_u16[i] = value;
 		} else {
 			memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
 		}
 		break;
 	case V4L2_CTRL_TYPE_U32:
-		if (ctrl->default_value) {
+		if (value) {
 			for (i = from_idx; i < tot_elems; i++)
-				ptr.p_u32[i] = ctrl->default_value;
+				ptr.p_u32[i] = value;
 		} else {
 			memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
 		}
 		break;
 	default:
-		for (i = from_idx; i < tot_elems; i++)
-			std_init_compound(ctrl, i, ptr);
+		for (i = from_idx; i < tot_elems; i++) {
+			switch (which) {
+			case V4L2_CTRL_WHICH_DEF_VAL:
+				std_init_compound(ctrl, i, ptr);
+				break;
+			case V4L2_CTRL_WHICH_MAX_VAL:
+				std_max_compound(ctrl, i, ptr);
+				break;
+			case V4L2_CTRL_WHICH_MIN_VAL:
+				std_min_compound(ctrl, i, ptr);
+				break;
+			}
+		}
 		break;
 	}
 }
+
+void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
+			    union v4l2_ctrl_ptr ptr)
+{
+	__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_DEF_VAL, ptr);
+}
 EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
 
+void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
+			       union v4l2_ctrl_ptr ptr)
+{
+	__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
+}
+
+void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
+			       union v4l2_ctrl_ptr ptr)
+{
+	__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
+}
+
 void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
 {
 	union v4l2_ctrl_ptr ptr = ctrl->p_cur;
@@ -1293,6 +1359,8 @@ EXPORT_SYMBOL(v4l2_ctrl_type_op_validate);
 static const struct v4l2_ctrl_type_ops std_type_ops = {
 	.equal = v4l2_ctrl_type_op_equal,
 	.init = v4l2_ctrl_type_op_init,
+	.minimum = v4l2_ctrl_type_op_minimum,
+	.maximum = v4l2_ctrl_type_op_maximum,
 	.log = v4l2_ctrl_type_op_log,
 	.validate = v4l2_ctrl_type_op_validate,
 };
@@ -1764,7 +1832,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 			s64 min, s64 max, u64 step, s64 def,
 			const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
 			u32 flags, const char * const *qmenu,
-			const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def,
+			const s64 *qmenu_int,
+			const union v4l2_ctrl_ptr p_def,
+			const union v4l2_ctrl_ptr p_min,
+			const union v4l2_ctrl_ptr p_max,
 			void *priv)
 {
 	struct v4l2_ctrl *ctrl;
@@ -1888,6 +1959,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		break;
 	}
 
+	if (type < V4L2_CTRL_COMPOUND_TYPES &&
+	    type != V4L2_CTRL_TYPE_BUTTON &&
+	    type != V4L2_CTRL_TYPE_CTRL_CLASS &&
+	    type != V4L2_CTRL_TYPE_STRING)
+		flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
+
 	/* Sanity checks */
 	if (id == 0 || name == NULL || !elem_size ||
 	    id >= V4L2_CID_PRIVATE_BASE ||
@@ -1896,6 +1973,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		handler_set_err(hdl, -ERANGE);
 		return NULL;
 	}
+
 	err = check_range(type, min, max, step, def);
 	if (err) {
 		handler_set_err(hdl, err);
@@ -1937,6 +2015,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 
 	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
 		sz_extra += elem_size;
+	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
+		sz_extra += elem_size;
+	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_max.p_const)
+		sz_extra += elem_size;
 
 	ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
 	if (ctrl == NULL) {
@@ -2002,6 +2084,22 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
 	}
 
+	if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) {
+		void *ptr = ctrl->p_def.p;
+
+		if (p_min.p_const) {
+			ptr += elem_size;
+			ctrl->p_min.p = ptr;
+			memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
+		}
+
+		if (p_max.p_const) {
+			ptr += elem_size;
+			ctrl->p_max.p = ptr;
+			memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
+		}
+	}
+
 	ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
 	cur_to_new(ctrl);
 
@@ -2052,7 +2150,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
 			type, min, max,
 			is_menu ? cfg->menu_skip_mask : step, def,
 			cfg->dims, cfg->elem_size,
-			flags, qmenu, qmenu_int, cfg->p_def, priv);
+			flags, qmenu, qmenu_int, cfg->p_def, cfg->p_min,
+			cfg->p_max, priv);
 	if (ctrl)
 		ctrl->is_private = cfg->is_private;
 	return ctrl;
@@ -2077,7 +2176,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
 	}
 	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
 			     min, max, step, def, NULL, 0,
-			     flags, NULL, NULL, ptr_null, NULL);
+			     flags, NULL, NULL, ptr_null, ptr_null,
+			     ptr_null, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_std);
 
@@ -2110,7 +2210,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
 	}
 	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
 			     0, max, mask, def, NULL, 0,
-			     flags, qmenu, qmenu_int, ptr_null, NULL);
+			     flags, qmenu, qmenu_int, ptr_null, ptr_null,
+			     ptr_null, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
 
@@ -2142,7 +2243,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
 	}
 	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
 			     0, max, mask, def, NULL, 0,
-			     flags, qmenu, NULL, ptr_null, NULL);
+			     flags, qmenu, NULL, ptr_null, ptr_null,
+			     ptr_null, NULL);
 
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
@@ -2150,7 +2252,9 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
 /* Helper function for standard compound controls */
 struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
 				const struct v4l2_ctrl_ops *ops, u32 id,
-				const union v4l2_ctrl_ptr p_def)
+				const union v4l2_ctrl_ptr p_def,
+				const union v4l2_ctrl_ptr p_min,
+				const union v4l2_ctrl_ptr p_max)
 {
 	const char *name;
 	enum v4l2_ctrl_type type;
@@ -2164,7 +2268,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
 	}
 	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
 			     min, max, step, def, NULL, 0,
-			     flags, NULL, NULL, p_def, NULL);
+			     flags, NULL, NULL, p_def, p_min, p_max, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_std_compound);
 
@@ -2188,7 +2292,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
 	}
 	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
 			     0, max, 0, def, NULL, 0,
-			     flags, NULL, qmenu_int, ptr_null, NULL);
+			     flags, NULL, qmenu_int, ptr_null, ptr_null,
+			     ptr_null, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
 
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 9b1de54ce379..db5bd765db3c 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -884,7 +884,9 @@ static bool check_ext_ctrls(struct v4l2_ext_controls *c, unsigned long ioctl)
 			return false;
 		break;
 	case V4L2_CTRL_WHICH_DEF_VAL:
-		/* Default value cannot be changed */
+	case V4L2_CTRL_WHICH_MIN_VAL:
+	case V4L2_CTRL_WHICH_MAX_VAL:
+		/* Default, minimum or maximum value cannot be changed */
 		if (ioctl == VIDIOC_S_EXT_CTRLS ||
 		    ioctl == VIDIOC_TRY_EXT_CTRLS) {
 			c->error_idx = c->count;
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index b0db167a3ac4..9ed7be1e696f 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -133,6 +133,8 @@ struct v4l2_ctrl_ops {
  *
  * @equal: return true if all ctrl->elems array elements are equal.
  * @init: initialize the value for array elements from from_idx to ctrl->elems.
+ * @minimum: set the value to the minimum value of the control.
+ * @maximum: set the value to the maximum value of the control.
  * @log: log the value.
  * @validate: validate the value for ctrl->new_elems array elements.
  *	Return 0 on success and a negative value otherwise.
@@ -142,6 +144,10 @@ struct v4l2_ctrl_type_ops {
 		      union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2);
 	void (*init)(const struct v4l2_ctrl *ctrl, u32 from_idx,
 		     union v4l2_ctrl_ptr ptr);
+	void (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
+			union v4l2_ctrl_ptr ptr);
+	void (*maximum)(const struct v4l2_ctrl *ctrl, u32 idx,
+			union v4l2_ctrl_ptr ptr);
 	void (*log)(const struct v4l2_ctrl *ctrl);
 	int (*validate)(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr ptr);
 };
@@ -247,6 +253,12 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
  * @p_def:	The control's default value represented via a union which
  *		provides a standard way of accessing control types
  *		through a pointer (for compound controls only).
+ * @p_min:	The control's minimum value represented via a union which
+ *		provides a standard way of accessing control types
+ *		through a pointer (for compound controls only).
+ * @p_max:	The control's maximum value represented via a union which
+ *		provides a standard way of accessing control types
+ *		through a pointer (for compound controls only).
  * @p_cur:	The control's current value represented via a union which
  *		provides a standard way of accessing control types
  *		through a pointer.
@@ -306,6 +318,8 @@ struct v4l2_ctrl {
 	} cur;
 
 	union v4l2_ctrl_ptr p_def;
+	union v4l2_ctrl_ptr p_min;
+	union v4l2_ctrl_ptr p_max;
 	union v4l2_ctrl_ptr p_new;
 	union v4l2_ctrl_ptr p_cur;
 };
@@ -425,6 +439,8 @@ struct v4l2_ctrl_handler {
  * @step:	The control's step value for non-menu controls.
  * @def:	The control's default value.
  * @p_def:	The control's default value for compound controls.
+ * @p_min:	The control's minimum value for compound controls.
+ * @p_max:	The control's maximum value for compound controls.
  * @dims:	The size of each dimension.
  * @elem_size:	The size in bytes of the control.
  * @flags:	The control's flags.
@@ -454,6 +470,8 @@ struct v4l2_ctrl_config {
 	u64 step;
 	s64 def;
 	union v4l2_ctrl_ptr p_def;
+	union v4l2_ctrl_ptr p_min;
+	union v4l2_ctrl_ptr p_max;
 	u32 dims[V4L2_CTRL_MAX_DIMS];
 	u32 elem_size;
 	u32 flags;
@@ -723,17 +741,25 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
  * @ops:       The control ops.
  * @id:        The control ID.
  * @p_def:     The control's default value.
+ * @p_min:     The control's minimum value.
+ * @p_max:     The control's maximum value.
  *
- * Sames as v4l2_ctrl_new_std(), but with support to compound controls, thanks
- * to the @p_def field. Use v4l2_ctrl_ptr_create() to create @p_def from a
- * pointer. Use v4l2_ctrl_ptr_create(NULL) if the default value of the
- * compound control should be all zeroes.
+ * Same as v4l2_ctrl_new_std(), but with support for compound controls.
+ * To fill in the @p_def, @p_min and @p_max fields, use v4l2_ctrl_ptr_create()
+ * to convert a pointer to a const union v4l2_ctrl_ptr.
+ * Use v4l2_ctrl_ptr_create(NULL) if you want the default, minimum or maximum
+ * value of the compound control to be all zeroes.
+ * If the compound control does not set the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
+ * flag, then it does not has minimum and maximum values. In that case just use
+ * v4l2_ctrl_ptr_create(NULL) for the @p_min and @p_max arguments.
  *
  */
 struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
 					     const struct v4l2_ctrl_ops *ops,
 					     u32 id,
-					     const union v4l2_ctrl_ptr p_def);
+					     const union v4l2_ctrl_ptr p_def,
+					     const union v4l2_ctrl_ptr p_min,
+					     const union v4l2_ctrl_ptr p_max);
 
 /**
  * v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control.
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 82d86abcf89c..8fdeb5188af5 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1866,6 +1866,8 @@ struct v4l2_ext_controls {
 #define V4L2_CTRL_WHICH_CUR_VAL   0
 #define V4L2_CTRL_WHICH_DEF_VAL   0x0f000000
 #define V4L2_CTRL_WHICH_REQUEST_VAL 0x0f010000
+#define V4L2_CTRL_WHICH_MIN_VAL   0x0f020000
+#define V4L2_CTRL_WHICH_MAX_VAL   0x0f030000
 
 enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_INTEGER	     = 1,
@@ -1973,6 +1975,7 @@ struct v4l2_querymenu {
 #define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE	0x0200
 #define V4L2_CTRL_FLAG_MODIFY_LAYOUT	0x0400
 #define V4L2_CTRL_FLAG_DYNAMIC_ARRAY	0x0800
+#define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
 
 /*  Query flags, to be ORed with the control ID */
 #define V4L2_CTRL_FLAG_NEXT_CTRL	0x80000000
-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v14 07/11] media: vivid: Add an rectangle control
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
                   ` (5 preceding siblings ...)
  2023-12-01  7:18 ` [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2023-12-01  7:18 ` Yunke Cao
  2023-12-01  7:18 ` [PATCH v14 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:18 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

This control represents a generic read/write rectangle.
It supports V4L2_CTRL_WHICH_MIN/MAX_VAL.

Signed-off-by: Yunke Cao <yunkec@google.com>
Reviewed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
Changelog since v13:
- Added Reviewed-by from Hans

 .../media/test-drivers/vivid/vivid-ctrls.c    | 34 +++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c
index f2b20e25a7a4..27a1173c7734 100644
--- a/drivers/media/test-drivers/vivid/vivid-ctrls.c
+++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c
@@ -38,6 +38,7 @@
 #define VIVID_CID_U8_PIXEL_ARRAY	(VIVID_CID_CUSTOM_BASE + 14)
 #define VIVID_CID_S32_ARRAY		(VIVID_CID_CUSTOM_BASE + 15)
 #define VIVID_CID_S64_ARRAY		(VIVID_CID_CUSTOM_BASE + 16)
+#define VIVID_CID_RECT			(VIVID_CID_CUSTOM_BASE + 17)
 
 #define VIVID_CID_VIVID_BASE		(0x00f00000 | 0xf000)
 #define VIVID_CID_VIVID_CLASS		(0x00f00000 | 1)
@@ -357,6 +358,38 @@ static const struct v4l2_ctrl_config vivid_ctrl_ro_int32 = {
 	.step = 1,
 };
 
+static const struct v4l2_rect rect_def = {
+	.top = 100,
+	.left = 200,
+	.width = 300,
+	.height = 400,
+};
+
+static const struct v4l2_rect rect_min = {
+	.top = 0,
+	.left = 0,
+	.width = 1,
+	.height = 1,
+};
+
+static const struct v4l2_rect rect_max = {
+	.top = 0,
+	.left = 0,
+	.width = 1000,
+	.height = 2000,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_rect = {
+	.ops = &vivid_user_gen_ctrl_ops,
+	.id = VIVID_CID_RECT,
+	.name = "Rect",
+	.type = V4L2_CTRL_TYPE_RECT,
+	.flags = V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX,
+	.p_def.p_const = &rect_def,
+	.p_min.p_const = &rect_min,
+	.p_max.p_const = &rect_max,
+};
+
 /* Framebuffer Controls */
 
 static int vivid_fb_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -1677,6 +1710,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
 	dev->int_menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int_menu, NULL);
 	dev->ro_int32 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_ro_int32, NULL);
 	v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_area, NULL);
+	v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_rect, NULL);
 	v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_array, NULL);
 	v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_dyn_array, NULL);
 	v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u16_matrix, NULL);
-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v14 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
                   ` (6 preceding siblings ...)
  2023-12-01  7:18 ` [PATCH v14 07/11] media: vivid: Add an rectangle control Yunke Cao
@ 2023-12-01  7:18 ` Yunke Cao
  2023-12-08 15:26   ` Laurent Pinchart
  2023-12-01  7:19 ` [PATCH v14 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:18 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Add support for V4L2_CTRL_WHICH_MIN/MAX_VAL in uvc driver.
It is useful for the V4L2_CID_UVC_REGION_OF_INTEREST_RECT control.

Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- No change.
Changelog since v10:
- Added Reviewed-by from Sergey.
Changelog since v9:
- Revert a change in v7 that causes v4l2-compliance failure:
- In uvc_ioctl_g_ext_ctrls(), when v4l2_which is not V4L2_CTRL_WHICH_*_VAL,
- treat it the same as cur instead of returning EINVAL. This is the existing
- behavior.
- The change in v7 of returning EINVAL fails the check in
- v4l2-compliance/v4l2-test-controls.cpp#L834.
Changelog since v8:
- No change.
Changelog since v7:
- Address some comments.

 drivers/media/usb/uvc/uvc_ctrl.c | 64 +++++++++++++++++++++++++++-----
 drivers/media/usb/uvc/uvc_v4l2.c |  7 +++-
 drivers/media/usb/uvc/uvcvideo.h |  3 +-
 3 files changed, 61 insertions(+), 13 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index aae2480496b7..c073c2a02102 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1247,11 +1247,18 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
 	if (!ctrl)
 		return -EINVAL;
 
-	if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) && read)
-		return -EACCES;
-
-	if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
-		return -EACCES;
+	if (read) {
+		if (ctrls->which == V4L2_CTRL_WHICH_MIN_VAL ||
+		    ctrls->which == V4L2_CTRL_WHICH_MAX_VAL) {
+			if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) ||
+			    !(ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX))
+				return -EINVAL;
+		} else if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
+			return -EACCES;
+	} else {
+		if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
+			return -EACCES;
+	}
 
 	if (ioctl != VIDIOC_S_EXT_CTRLS || !mapping->master_id)
 		return 0;
@@ -1332,6 +1339,9 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
 		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
 	if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
 		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) &&
+	    (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN))
+		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
 
 	if (mapping->master_id)
 		__uvc_find_control(ctrl->entity, mapping->master_id,
@@ -1981,6 +1991,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
 static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
 				       struct uvc_control *ctrl,
 				       struct uvc_control_mapping *mapping,
+				       u32 v4l2_which,
 				       struct v4l2_ext_control *xctrl)
 {
 	struct v4l2_queryctrl qc = { .id = xctrl->id };
@@ -1990,28 +2001,59 @@ static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
 	if (ret < 0)
 		return ret;
 
-	xctrl->value = qc.default_value;
+	switch (v4l2_which) {
+	case V4L2_CTRL_WHICH_DEF_VAL:
+		xctrl->value = qc.default_value;
+		break;
+	case V4L2_CTRL_WHICH_MIN_VAL:
+		xctrl->value = qc.minimum;
+		break;
+	case V4L2_CTRL_WHICH_MAX_VAL:
+		xctrl->value = qc.maximum;
+		break;
+	}
+
 	return 0;
 }
 
 static int __uvc_ctrl_get_boundary_compound(struct uvc_video_chain *chain,
 					    struct uvc_control *ctrl,
 					    struct uvc_control_mapping *mapping,
+					    u32 v4l2_which,
 					    struct v4l2_ext_control *xctrl)
 {
+	u32 flag, id;
 	int ret;
 
+	switch (v4l2_which) {
+	case V4L2_CTRL_WHICH_DEF_VAL:
+		flag = UVC_CTRL_FLAG_GET_DEF;
+		id = UVC_CTRL_DATA_DEF;
+		break;
+	case V4L2_CTRL_WHICH_MIN_VAL:
+		flag = UVC_CTRL_FLAG_GET_MIN;
+		id = UVC_CTRL_DATA_MIN;
+		break;
+	case V4L2_CTRL_WHICH_MAX_VAL:
+		flag = UVC_CTRL_FLAG_GET_MAX;
+		id = UVC_CTRL_DATA_MAX;
+		break;
+	}
+
+	if (!(ctrl->info.flags & flag) && flag != UVC_CTRL_FLAG_GET_DEF)
+		return -EINVAL;
+
 	if (!ctrl->cached) {
 		ret = uvc_ctrl_populate_cache(chain, ctrl);
 		if (ret < 0)
 			return ret;
 	}
 
-	return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_DEF, xctrl);
+	return __uvc_ctrl_get_compound(mapping, ctrl, id, xctrl);
 }
 
 int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
-			  struct v4l2_ext_control *xctrl)
+			  struct v4l2_ext_control *xctrl, u32 v4l2_which)
 {
 	struct uvc_control *ctrl;
 	struct uvc_control_mapping *mapping;
@@ -2028,9 +2070,11 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
 
 	if (uvc_ctrl_mapping_is_compound(mapping))
 		ret = __uvc_ctrl_get_boundary_compound(chain, ctrl, mapping,
-						       xctrl);
+						       v4l2_which, xctrl);
 	else
-		ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
+		ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping,
+						  v4l2_which, xctrl);
+
 
 done:
 	mutex_unlock(&chain->ctrl_mutex);
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index ff72caf7bc9e..352f62ef02f2 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1094,9 +1094,12 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
 	if (ret < 0)
 		return ret;
 
-	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
+	switch (ctrls->which) {
+	case V4L2_CTRL_WHICH_DEF_VAL:
+	case V4L2_CTRL_WHICH_MIN_VAL:
+	case V4L2_CTRL_WHICH_MAX_VAL:
 		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
-			ret = uvc_ctrl_get_boundary(chain, ctrl);
+			ret = uvc_ctrl_get_boundary(chain, ctrl, ctrls->which);
 			if (ret < 0) {
 				ctrls->error_idx = i;
 				return ret;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 11805b729c22..6fda40919e7f 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -790,7 +790,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
 
 int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
 int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
-			  struct v4l2_ext_control *xctrl);
+			  struct v4l2_ext_control *xctrl,
+			  u32 v4l2_which);
 int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
 int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
 			   const struct v4l2_ext_controls *ctrls,
-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v14 09/11] media: uvcvideo: implement UVC v1.5 ROI
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
                   ` (7 preceding siblings ...)
  2023-12-01  7:18 ` [PATCH v14 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2023-12-01  7:19 ` Yunke Cao
  2023-12-08 15:43   ` Laurent Pinchart
  2024-03-14 13:24   ` Gergo Koteles
  2023-12-01  7:19 ` [PATCH v14 10/11] media: uvcvideo: initilaize ROI control to default value Yunke Cao
  2023-12-01  7:19 ` [PATCH v14 11/11] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
  10 siblings, 2 replies; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:19 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

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>
---
Changelog since v11:
- No change.
Changelog since v10:
- Moved after the patches adding support for MIN/MAX.
- Clamp the set current value based on MIN/MAX.
- Thanks, Daniel!
Changelog since v9:
- No change.
Changelog since v8:
- No change.
Changelog since v7:
- Fix a few style issues.
- Only allow 4-byte aligned data.
- Add control names.
- Move initialization to 7/10.

 drivers/media/usb/uvc/uvc_ctrl.c   | 137 +++++++++++++++++++++++++++--
 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, 167 insertions(+), 10 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index c073c2a02102..d405b2a9d477 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[] = {
@@ -459,6 +477,70 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
 	data[first+1] = min_t(int, abs(value), 0xff);
 }
 
+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,
@@ -748,6 +830,29 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
 	},
+	{
+		.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",
+	},
 };
 
 const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
@@ -900,12 +1005,12 @@ 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;
 }
-
 static bool
 uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
 {
@@ -2100,6 +2205,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);
@@ -2224,6 +2331,13 @@ int uvc_ctrl_set(struct uvc_fh *handle,
 	}
 
 	if (uvc_ctrl_mapping_is_compound(mapping)) {
+		/* 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;
@@ -2641,12 +2755,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)
-			return -EINVAL;
+		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 352f62ef02f2..c0a7c0091099 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 6fda40919e7f..18da5e0b8cb2 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -133,8 +133,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 {
@@ -289,6 +290,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 2ff0e8a3a683..2afb4420e6c4 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 f86185456dc5..9d1e6085feba 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 68db66d4aae8..6c7e03d18d79 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1081,6 +1081,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.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v14 10/11] media: uvcvideo: initilaize ROI control to default value
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
                   ` (8 preceding siblings ...)
  2023-12-01  7:19 ` [PATCH v14 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
@ 2023-12-01  7:19 ` Yunke Cao
  2023-12-08 15:50   ` Laurent Pinchart
  2023-12-01  7:19 ` [PATCH v14 11/11] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
  10 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:19 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Add an init function to uvc_control_info. Use the function to
initialize ROI control to default value.

Also moves utility functions to the top of uvc_ctrl.c, above
the uvc_ctrls definition. uvc_ctrl_init_roi() calls uvc_ctrl_data()
and need to be declared before uvc_ctrls.

Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v8:
- No change.
Changelog since v7:
- Newly added patch. Split initializing from the previous patch.
- Add an init operation to uvc_control_info and use it for ROI
  initialization.
  
 drivers/media/usb/uvc/uvc_ctrl.c | 273 ++++++++++++++++++-------------
 drivers/media/usb/uvc/uvcvideo.h |   3 +
 2 files changed, 160 insertions(+), 116 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index d405b2a9d477..bda625c392c2 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -32,6 +32,158 @@
 #define UVC_CTRL_DATA_DEF	5
 #define UVC_CTRL_DATA_LAST	6
 
+/* ------------------------------------------------------------------------
+ * Utility functions
+ */
+
+static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
+{
+	return ctrl->uvc_data + id * ctrl->info.size;
+}
+
+static inline int uvc_test_bit(const u8 *data, int bit)
+{
+	return (data[bit >> 3] >> (bit & 7)) & 1;
+}
+
+static inline void uvc_clear_bit(u8 *data, int bit)
+{
+	data[bit >> 3] &= ~(1 << (bit & 7));
+}
+
+/*
+ * Extract the bit string specified by mapping->offset and mapping->data_size
+ * from the little-endian data stored at 'data' and return the result as
+ * a signed 32bit integer. Sign extension will be performed if the mapping
+ * references a signed data type.
+ */
+static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
+			    u8 query, const u8 *data)
+{
+	int bits = mapping->data_size;
+	int offset = mapping->offset;
+	s32 value = 0;
+	u8 mask;
+
+	data += offset / 8;
+	offset &= 7;
+	mask = ((1LL << bits) - 1) << offset;
+
+	while (1) {
+		u8 byte = *data & mask;
+
+		value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
+		bits -= 8 - (offset > 0 ? offset : 0);
+		if (bits <= 0)
+			break;
+
+		offset -= 8;
+		mask = (1 << bits) - 1;
+		data++;
+	}
+
+	/* Sign-extend the value if needed. */
+	if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
+		value |= -(value & (1 << (mapping->data_size - 1)));
+
+	return value;
+}
+
+/*
+ * Set the bit string specified by mapping->offset and mapping->data_size
+ * in the little-endian data stored at 'data' to the value 'value'.
+ */
+static void uvc_set_le_value(struct uvc_control_mapping *mapping,
+			     s32 value, u8 *data)
+{
+	int bits = mapping->data_size;
+	int offset = mapping->offset;
+	u8 mask;
+
+	/*
+	 * According to the v4l2 spec, writing any value to a button control
+	 * should result in the action belonging to the button control being
+	 * triggered. UVC devices however want to see a 1 written -> override
+	 * value.
+	 */
+	if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
+		value = -1;
+
+	data += offset / 8;
+	offset &= 7;
+
+	for (; bits > 0; data++) {
+		mask = ((1LL << bits) - 1) << offset;
+		*data = (*data & ~mask) | ((value << offset) & mask);
+		value >>= offset ? offset : 8;
+		bits -= 8 - offset;
+		offset = 0;
+	}
+}
+
+/*
+ * Extract the byte array specified by mapping->offset and mapping->data_size
+ * stored at 'data' to the output array 'data_out'.
+ */
+static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
+			    u8 *data_out)
+{
+	memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
+	return 0;
+}
+
+/*
+ * Copy the byte array 'data_in' to the destination specified by mapping->offset
+ * and mapping->data_size stored at 'data'.
+ */
+static int uvc_set_compound(struct uvc_control_mapping *mapping,
+			    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;
+}
+
+static bool
+uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
+{
+	return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
+}
+
+static int uvc_ctrl_init_roi(struct uvc_device *dev, struct uvc_control *ctrl)
+{
+	int ret;
+
+	ret = uvc_query_ctrl(dev, UVC_GET_DEF, ctrl->entity->id, dev->intfnum,
+			     UVC_CT_REGION_OF_INTEREST_CONTROL,
+			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
+			     ctrl->info.size);
+	if (ret)
+		goto out;
+
+	/*
+	 * Most firmwares have wrong GET_CUR configuration. E.g. it's
+	 * below GET_MIN, or have rectangle coordinates mixed up. This
+	 * causes problems sometimes, because we are unable to set
+	 * auto-controls value without first setting ROI rectangle to
+	 * valid configuration.
+	 *
+	 * We expect that default configuration is always correct and
+	 * is within the GET_MIN / GET_MAX boundaries.
+	 *
+	 * Set current ROI configuration to GET_DEF, so that we will
+	 * always have properly configured ROI.
+	 */
+	ret = uvc_query_ctrl(dev, UVC_SET_CUR, 1, dev->intfnum,
+			     UVC_CT_REGION_OF_INTEREST_CONTROL,
+			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
+			     ctrl->info.size);
+out:
+	if (ret)
+		dev_err(&dev->udev->dev, "Failed to fixup ROI (%d).\n", ret);
+	return ret;
+}
+
 /* ------------------------------------------------------------------------
  * Controls
  */
@@ -375,6 +527,7 @@ static const struct uvc_control_info uvc_ctrls[] = {
 				| UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
 				| UVC_CTRL_FLAG_GET_DEF
 				| UVC_CTRL_FLAG_AUTO_UPDATE,
+		.init		= uvc_ctrl_init_roi,
 	},
 };
 
@@ -901,122 +1054,6 @@ static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = {
 	NULL, /* Sentinel */
 };
 
-/* ------------------------------------------------------------------------
- * Utility functions
- */
-
-static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
-{
-	return ctrl->uvc_data + id * ctrl->info.size;
-}
-
-static inline int uvc_test_bit(const u8 *data, int bit)
-{
-	return (data[bit >> 3] >> (bit & 7)) & 1;
-}
-
-static inline void uvc_clear_bit(u8 *data, int bit)
-{
-	data[bit >> 3] &= ~(1 << (bit & 7));
-}
-
-/*
- * Extract the bit string specified by mapping->offset and mapping->data_size
- * from the little-endian data stored at 'data' and return the result as
- * a signed 32bit integer. Sign extension will be performed if the mapping
- * references a signed data type.
- */
-static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
-	u8 query, const u8 *data)
-{
-	int bits = mapping->data_size;
-	int offset = mapping->offset;
-	s32 value = 0;
-	u8 mask;
-
-	data += offset / 8;
-	offset &= 7;
-	mask = ((1LL << bits) - 1) << offset;
-
-	while (1) {
-		u8 byte = *data & mask;
-		value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
-		bits -= 8 - (offset > 0 ? offset : 0);
-		if (bits <= 0)
-			break;
-
-		offset -= 8;
-		mask = (1 << bits) - 1;
-		data++;
-	}
-
-	/* Sign-extend the value if needed. */
-	if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
-		value |= -(value & (1 << (mapping->data_size - 1)));
-
-	return value;
-}
-
-/*
- * Set the bit string specified by mapping->offset and mapping->data_size
- * in the little-endian data stored at 'data' to the value 'value'.
- */
-static void uvc_set_le_value(struct uvc_control_mapping *mapping,
-	s32 value, u8 *data)
-{
-	int bits = mapping->data_size;
-	int offset = mapping->offset;
-	u8 mask;
-
-	/*
-	 * According to the v4l2 spec, writing any value to a button control
-	 * should result in the action belonging to the button control being
-	 * triggered. UVC devices however want to see a 1 written -> override
-	 * value.
-	 */
-	if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
-		value = -1;
-
-	data += offset / 8;
-	offset &= 7;
-
-	for (; bits > 0; data++) {
-		mask = ((1LL << bits) - 1) << offset;
-		*data = (*data & ~mask) | ((value << offset) & mask);
-		value >>= offset ? offset : 8;
-		bits -= 8 - offset;
-		offset = 0;
-	}
-}
-
-/*
- * Extract the byte array specified by mapping->offset and mapping->data_size
- * stored at 'data' to the output array 'data_out'.
- */
-static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
-			    u8 *data_out)
-{
-	memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
-	return 0;
-}
-
-/*
- * Copy the byte array 'data_in' to the destination specified by mapping->offset
- * and mapping->data_size stored at 'data'.
- */
-static int uvc_set_compound(struct uvc_control_mapping *mapping,
-			    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;
-}
-static bool
-uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
-{
-	return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
-}
-
 /* ------------------------------------------------------------------------
  * Terminal and unit management
  */
@@ -2984,6 +3021,10 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
 			 * GET_INFO on standard controls.
 			 */
 			uvc_ctrl_get_flags(chain->dev, ctrl, &ctrl->info);
+
+			if (info->init)
+				info->init(chain->dev, ctrl);
+
 			break;
 		 }
 	}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 18da5e0b8cb2..335b565da6de 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -85,6 +85,7 @@
 struct gpio_desc;
 struct sg_table;
 struct uvc_device;
+struct uvc_control;
 
 /*
  * TODO: Put the most frequently accessed fields at the beginning of
@@ -99,6 +100,8 @@ struct uvc_control_info {
 
 	u16 size;
 	u32 flags;
+
+	int (*init)(struct uvc_device *dev, struct uvc_control *ctrl);
 };
 
 struct uvc_control_mapping {
-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v14 11/11] media: uvcvideo: document UVC v1.5 ROI
  2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
                   ` (9 preceding siblings ...)
  2023-12-01  7:19 ` [PATCH v14 10/11] media: uvcvideo: initilaize ROI control to default value Yunke Cao
@ 2023-12-01  7:19 ` Yunke Cao
  2023-12-08 16:00   ` Laurent Pinchart
  10 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-01  7:19 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Added documentation of V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.

Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- No change.
Changelog since v10:
- Added Reviewed-by from Sergey.
Changelog since v9:
- No change.
Changelog since v8:
- No change.
Changelog since v7:
- Fix documentation for automatic exposure based on comment in v7.

 .../userspace-api/media/drivers/uvcvideo.rst  | 62 +++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
index aab4304e6bb5..3dc062221f8b 100644
--- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
+++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
@@ -181,6 +181,7 @@ Argument: struct uvc_xu_control_mapping
 	UVC_CTRL_DATA_TYPE_BOOLEAN	Boolean
 	UVC_CTRL_DATA_TYPE_ENUM		Enumeration
 	UVC_CTRL_DATA_TYPE_BITMASK	Bitmask
+	UVC_CTRL_DATA_TYPE_RECT		Rectangular area
 
 
 UVCIOC_CTRL_QUERY - Query a UVC XU control
@@ -255,3 +256,64 @@ Argument: struct uvc_xu_control_query
 	__u8	query		Request code to send to the device
 	__u16	size		Control data size (in bytes)
 	__u8	*data		Control value
+
+
+Driver-specific V4L2 controls
+-----------------------------
+
+The uvcvideo driver implements the following UVC-specific controls:
+
+``V4L2_CID_UVC_REGION_OF_INTEREST_RECT (struct)``
+	This control determines the region of interest (ROI). ROI is a
+	rectangular area represented by a struct :c:type:`v4l2_rect`. The
+	rectangle is in global sensor coordinates and pixel units. It is
+	independent of the field of view, not impacted by any cropping or
+	scaling.
+
+	Use ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` to query
+	the range of rectangle sizes. The left/top coordinates of a minimum or
+	maximum rectangle are always 0. For example, a device can have a minimum
+	ROI rectangle of 1x1@0x0 and a maximum of 640x480@0x0.
+
+	Setting a ROI allows the camera to optimize the capture for the region.
+	The value of ``V4L2_CID_REGION_OF_INTEREST_AUTO`` control determines
+	the detailed behavior.
+
+
+``V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (bitmask)``
+	This determines which, if any, on board features should track to the
+	Region of Interest specified by the current value of
+	``V4L2_CID_UVD__REGION_OF_INTEREST_RECT``.
+
+	Max value is a mask indicating all supported Auto Controls.
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE``
+      - Setting this to true causes automatic exposure to track the region of
+	interest instead of the whole image.
+    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS``
+      - Setting this to true causes automatic iris to track the region of
+	interest instead of the whole image.
+    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE``
+      - Setting this to true causes automatic white balance to track the region
+	of interest instead of the whole image.
+    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS``
+      - Setting this to true causes automatic focus adjustment to track the
+	region of interest instead of the whole image.
+    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT``
+      - Setting this to true causes automatic face detection to track the
+	region of interest instead of the whole image.
+    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK``
+      - Setting this to true enables automatic face detection and tracking. The
+	current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
+	the driver.
+    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION``
+      - Setting this to true enables automatic image stabilization. The
+	current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
+	the driver.
+    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY``
+      - Setting this to true enables automatically capture the specified region
+	with higher quality if possible.
-- 
2.43.0.rc2.451.g8631bc7472-goog


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-12-01  7:18 ` [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2023-12-01  8:32   ` Hans Verkuil
  2023-12-05 22:30   ` kernel test robot
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 40+ messages in thread
From: Hans Verkuil @ 2023-12-01  8:32 UTC (permalink / raw)
  To: Yunke Cao, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

On 01/12/2023 08:18, Yunke Cao wrote:
> From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> 
> Add the capability of retrieving the min and max values of a
> compound control.
> 
> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> Changelog since v13:
> - Updated comments of v4l2_ctrl_new_std_compound()
> Changelog since v12:
> - Addressed comments from Hans.
> Changelog since v11:
> - Added a flag V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
> - Modified std_min/max_compound() to be void function. Moved the check of
>   whether WHICH_MIN/MAX_VAL into prepare_ext_ctrls(), and return EINVAL.
> - Modified documentations to reflect this change.
> Changelog since v10:
> - No change.
> Changelog since v9:
> - No change.
> Changelog since v8:
> - Return ENODATA when min/max is not implemented. Document this behavior.
> - Created a shared helper function __v4l2_ctrl_type_op_init that takes "which"
>   as a parameter. Call it in def, min and max operations.
> Changelog since v7:
> - Document that the definition of the min/max are provided by compound controls
>   are defined in control documentation.
> - Return error, instead of zeroed memory for v4l2_ctrl_ptr_create(NULL).
> 
>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  22 ++-
>  .../media/v4l/vidioc-queryctrl.rst            |   9 +-
>  .../media/videodev2.h.rst.exceptions          |   3 +
>  drivers/media/i2c/imx214.c                    |   5 +-
>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  54 +++++--
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 151 +++++++++++++++---
>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>  include/media/v4l2-ctrls.h                    |  36 ++++-
>  include/uapi/linux/videodev2.h                |   3 +
>  10 files changed, 248 insertions(+), 48 deletions(-)
> 
> diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> index 7b1001d11f9c..0b87c23e66ff 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> @@ -330,14 +330,26 @@ still cause this situation.
>        - Which value of the control to get/set/try.
>      * - :cspan:`2` ``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of
>  	the control, ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
> -	value of the control and ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
> -	these controls have to be retrieved from a request or tried/set for
> -	a request. In the latter case the ``request_fd`` field contains the
> +	value of the control, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
> +	value of the control, and ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
> +	value of the control. ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
> +	the control value has to be retrieved from a request or tried/set for
> +	a request. In that case the ``request_fd`` field contains the
>  	file descriptor of the request that should be used. If the device
>  	does not support requests, then ``EACCES`` will be returned.
>  
> -	When using ``V4L2_CTRL_WHICH_DEF_VAL`` be aware that you can only
> -	get the default value of the control, you cannot set or try it.
> +	When using ``V4L2_CTRL_WHICH_DEF_VAL``, ``V4L2_CTRL_WHICH_MIN_VAL``
> +	or ``V4L2_CTRL_WHICH_MAX_VAL`` be aware that you can only get the
> +	default/minimum/maximum value of the control, you cannot set or try it.
> +
> +	Whether a control supports querying the minimum and maximum values using
> +	``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` is indicated
> +	by the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. Most non-compound
> +	control types support this. For controls with compound types, the
> +	definition of minimum/maximum values are provided by
> +	the control documentation. If a compound control does not document the
> +	meaning of minimum/maximum value, then querying the minimum or maximum
> +	value will result in the error code -EINVAL.
>  
>  	For backwards compatibility you can also use a control class here
>  	(see :ref:`ctrl-class`). In that case all controls have to
> diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> index 56d5c8b0b88b..b39f7e27bbbe 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> @@ -447,7 +447,10 @@ See also the examples in :ref:`control`.
>        - n/a
>        - A struct :c:type:`v4l2_rect`, containing a rectangle described by
>  	the position of its top-left corner, the width and the height. Units
> -	depend on the use case.
> +	depend on the use case. Support for ``V4L2_CTRL_WHICH_MIN_VAL`` and
> +	``V4L2_CTRL_WHICH_MAX_VAL`` is optional and depends on the
> +	``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. See the documentation of
> +	the specific control on how to interpret the minimum and maximum values.
>      * - ``V4L2_CTRL_TYPE_H264_SPS``
>        - n/a
>        - n/a
> @@ -664,6 +667,10 @@ See also the examples in :ref:`control`.
>  	``dims[0]``. So setting the control with a differently sized
>  	array will change the ``elems`` field when the control is
>  	queried afterwards.
> +    * - ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
> +      - 0x1000
> +      - This control supports getting minimum and maximum values using
> +      vidioc_g_ext_ctrls with V4L2_CTRL_WHICH_MIN/MAX_VAL.
>  
>  Return Value
>  ============
> diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> index c46082ef0e4d..a417af25e9a4 100644
> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> @@ -393,6 +393,7 @@ replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags
>  replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags
>  replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags
>  replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags
> +replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags
>  
>  replace define V4L2_CTRL_FLAG_NEXT_CTRL control
>  replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control
> @@ -567,6 +568,8 @@ ignore define V4L2_CTRL_DRIVER_PRIV
>  ignore define V4L2_CTRL_MAX_DIMS
>  ignore define V4L2_CTRL_WHICH_CUR_VAL
>  ignore define V4L2_CTRL_WHICH_DEF_VAL
> +ignore define V4L2_CTRL_WHICH_MIN_VAL
> +ignore define V4L2_CTRL_WHICH_MAX_VAL
>  ignore define V4L2_CTRL_WHICH_REQUEST_VAL
>  ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS
>  ignore define V4L2_CID_MAX_CTRLS
> diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
> index 4f77ea02cc27..926b5cae12d8 100644
> --- a/drivers/media/i2c/imx214.c
> +++ b/drivers/media/i2c/imx214.c
> @@ -998,7 +998,10 @@ static int imx214_probe(struct i2c_client *client)
>  	imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls,
>  				NULL,
>  				V4L2_CID_UNIT_CELL_SIZE,
> -				v4l2_ctrl_ptr_create((void *)&unit_size));
> +				v4l2_ctrl_ptr_create((void *)&unit_size),
> +				v4l2_ctrl_ptr_create(NULL),
> +				v4l2_ctrl_ptr_create(NULL));
> +
>  	ret = imx214->ctrls.error;
>  	if (ret) {
>  		dev_err(&client->dev, "%s control init failed (%d)\n",
> diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
> index d9d2a293f3ef..7f370438d655 100644
> --- a/drivers/media/platform/qcom/venus/venc_ctrls.c
> +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
> @@ -607,11 +607,16 @@ int venc_ctrl_init(struct venus_inst *inst)
>  
>  	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
>  				   V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
> -				   v4l2_ctrl_ptr_create(&p_hdr10_cll));
> +				   v4l2_ctrl_ptr_create(&p_hdr10_cll),
> +				   v4l2_ctrl_ptr_create(NULL),
> +				   v4l2_ctrl_ptr_create(NULL));
>  
>  	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
>  				   V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
> -				   v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering));
> +				   v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
> +				   v4l2_ctrl_ptr_create(NULL),
> +				   v4l2_ctrl_ptr_create(NULL));
> +
>  
>  	v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
>  			       V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> index 002ea6588edf..d022e1ed4835 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> @@ -94,6 +94,22 @@ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
>  	return ptr_to_user(c, ctrl, ctrl->p_new);
>  }
>  
> +/* Helper function: copy the minimum control value back to the caller */
> +static int min_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> +{
> +	ctrl->type_ops->minimum(ctrl, 0, ctrl->p_new);
> +
> +	return ptr_to_user(c, ctrl, ctrl->p_new);
> +}
> +
> +/* Helper function: copy the maximum control value back to the caller */
> +static int max_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> +{
> +	ctrl->type_ops->maximum(ctrl, 0, ctrl->p_new);
> +
> +	return ptr_to_user(c, ctrl, ctrl->p_new);
> +}
> +
>  /* Helper function: copy the caller-provider value as the new control value */
>  static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
>  {
> @@ -229,8 +245,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>  		cs->error_idx = i;
>  
>  		if (cs->which &&
> -		    cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
> -		    cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
> +		    (cs->which < V4L2_CTRL_WHICH_DEF_VAL ||
> +		     cs->which > V4L2_CTRL_WHICH_MAX_VAL) &&
>  		    V4L2_CTRL_ID2WHICH(id) != cs->which) {
>  			dprintk(vdev,
>  				"invalid which 0x%x or control id 0x%x\n",
> @@ -259,6 +275,15 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>  			return -EINVAL;
>  		}
>  
> +		if (!(ctrl->flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) &&
> +		    (cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> +		     cs->which == V4L2_CTRL_WHICH_MAX_VAL)) {
> +			dprintk(vdev,
> +				"invalid which 0x%x or control id 0x%x\n",
> +				cs->which, id);
> +			return -EINVAL;
> +		}
> +
>  		if (ctrl->cluster[0]->ncontrols > 1)
>  			have_clusters = true;
>  		if (ctrl->cluster[0] != ctrl)
> @@ -368,8 +393,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>   */
>  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
>  {
> -	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
> -	    which == V4L2_CTRL_WHICH_REQUEST_VAL)
> +	if (which == 0 || (which >= V4L2_CTRL_WHICH_DEF_VAL &&
> +			   which <= V4L2_CTRL_WHICH_MAX_VAL))
>  		return 0;
>  	return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
>  }
> @@ -389,10 +414,12 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>  	struct v4l2_ctrl_helper *helpers = helper;
>  	int ret;
>  	int i, j;
> -	bool is_default, is_request;
> +	bool is_default, is_request, is_min, is_max;
>  
>  	is_default = (cs->which == V4L2_CTRL_WHICH_DEF_VAL);
>  	is_request = (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL);
> +	is_min = (cs->which == V4L2_CTRL_WHICH_MIN_VAL);
> +	is_max = (cs->which == V4L2_CTRL_WHICH_MAX_VAL);
>  
>  	cs->error_idx = cs->count;
>  	cs->which = V4L2_CTRL_ID2WHICH(cs->which);
> @@ -432,13 +459,14 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>  
>  		/*
>  		 * g_volatile_ctrl will update the new control values.
> -		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL and
> +		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL,
> +		 * V4L2_CTRL_WHICH_MIN_VAL, V4L2_CTRL_WHICH_MAX_VAL and
>  		 * V4L2_CTRL_WHICH_REQUEST_VAL. In the case of requests
>  		 * it is v4l2_ctrl_request_complete() that copies the
>  		 * volatile controls at the time of request completion
>  		 * to the request, so you don't want to do that again.
>  		 */
> -		if (!is_default && !is_request &&
> +		if (!is_default && !is_request && !is_min && !is_max &&
>  		    ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
>  		    (master->has_volatiles && !is_cur_manual(master)))) {
>  			for (j = 0; j < master->ncontrols; j++)
> @@ -467,6 +495,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>  				ret = -ENOMEM;
>  			else if (is_request && ref->p_req_valid)
>  				ret = req_to_user(cs->controls + idx, ref);
> +			else if (is_min)
> +				ret = min_to_user(cs->controls + idx, ref->ctrl);
> +			else if (is_max)
> +				ret = max_to_user(cs->controls + idx, ref->ctrl);
>  			else if (is_volatile)
>  				ret = new_to_user(cs->controls + idx, ref->ctrl);
>  			else
> @@ -564,9 +596,11 @@ int try_set_ext_ctrls_common(struct v4l2_fh *fh,
>  
>  	cs->error_idx = cs->count;
>  
> -	/* Default value cannot be changed */
> -	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL) {
> -		dprintk(vdev, "%s: cannot change default value\n",
> +	/* Default/minimum/maximum values cannot be changed */
> +	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL ||
> +	    cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> +	    cs->which == V4L2_CTRL_WHICH_MAX_VAL) {
> +		dprintk(vdev, "%s: cannot change default/min/max value\n",
>  			video_device_node_name(vdev));
>  		return -EINVAL;
>  	}
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> index f1486ab032cf..ef418165e88d 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -182,29 +182,66 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  	}
>  }
>  
> -void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +static void std_min_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> +			    union v4l2_ctrl_ptr ptr)
> +{
> +	void *p = ptr.p + idx * ctrl->elem_size;
> +
> +	if (ctrl->p_min.p_const)
> +		memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
> +	else
> +		memset(p, 0, ctrl->elem_size);
> +}
> +
> +static void std_max_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  			    union v4l2_ctrl_ptr ptr)
> +{
> +	void *p = ptr.p + idx * ctrl->elem_size;
> +
> +	if (ctrl->p_max.p_const)
> +		memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> +	else
> +		memset(p, 0, ctrl->elem_size);
> +}
> +
> +static void __v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +				     u32 which, union v4l2_ctrl_ptr ptr)
>  {
>  	unsigned int i;
>  	u32 tot_elems = ctrl->elems;
>  	u32 elems = tot_elems - from_idx;
> +	s64 value;
>  
> -	if (from_idx >= tot_elems)
> +	switch (which) {
> +	case V4L2_CTRL_WHICH_DEF_VAL:
> +		value = ctrl->default_value;
> +		break;
> +	case V4L2_CTRL_WHICH_MAX_VAL:
> +		value = ctrl->maximum;
> +		break;
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +		value = ctrl->minimum;
> +		break;
> +	default:
>  		return;
> +	}
>  
>  	switch (ctrl->type) {
>  	case V4L2_CTRL_TYPE_STRING:
> +		if (which == V4L2_CTRL_WHICH_DEF_VAL)
> +			value = ctrl->minimum;
> +
>  		for (i = from_idx; i < tot_elems; i++) {
>  			unsigned int offset = i * ctrl->elem_size;
>  
> -			memset(ptr.p_char + offset, ' ', ctrl->minimum);
> -			ptr.p_char[offset + ctrl->minimum] = '\0';
> +			memset(ptr.p_char + offset, ' ', value);
> +			ptr.p_char[offset + value] = '\0';
>  		}
>  		break;
>  	case V4L2_CTRL_TYPE_INTEGER64:
> -		if (ctrl->default_value) {
> +		if (value) {
>  			for (i = from_idx; i < tot_elems; i++)
> -				ptr.p_s64[i] = ctrl->default_value;
> +				ptr.p_s64[i] = value;
>  		} else {
>  			memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
>  		}
> @@ -214,9 +251,9 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
>  	case V4L2_CTRL_TYPE_MENU:
>  	case V4L2_CTRL_TYPE_BITMASK:
>  	case V4L2_CTRL_TYPE_BOOLEAN:
> -		if (ctrl->default_value) {
> +		if (value) {
>  			for (i = from_idx; i < tot_elems; i++)
> -				ptr.p_s32[i] = ctrl->default_value;
> +				ptr.p_s32[i] = value;
>  		} else {
>  			memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
>  		}
> @@ -226,32 +263,61 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
>  		memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
>  		break;
>  	case V4L2_CTRL_TYPE_U8:
> -		memset(ptr.p_u8 + from_idx, ctrl->default_value, elems);
> +		memset(ptr.p_u8 + from_idx, value, elems);
>  		break;
>  	case V4L2_CTRL_TYPE_U16:
> -		if (ctrl->default_value) {
> +		if (value) {
>  			for (i = from_idx; i < tot_elems; i++)
> -				ptr.p_u16[i] = ctrl->default_value;
> +				ptr.p_u16[i] = value;
>  		} else {
>  			memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
>  		}
>  		break;
>  	case V4L2_CTRL_TYPE_U32:
> -		if (ctrl->default_value) {
> +		if (value) {
>  			for (i = from_idx; i < tot_elems; i++)
> -				ptr.p_u32[i] = ctrl->default_value;
> +				ptr.p_u32[i] = value;
>  		} else {
>  			memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
>  		}
>  		break;
>  	default:
> -		for (i = from_idx; i < tot_elems; i++)
> -			std_init_compound(ctrl, i, ptr);
> +		for (i = from_idx; i < tot_elems; i++) {
> +			switch (which) {
> +			case V4L2_CTRL_WHICH_DEF_VAL:
> +				std_init_compound(ctrl, i, ptr);
> +				break;
> +			case V4L2_CTRL_WHICH_MAX_VAL:
> +				std_max_compound(ctrl, i, ptr);
> +				break;
> +			case V4L2_CTRL_WHICH_MIN_VAL:
> +				std_min_compound(ctrl, i, ptr);
> +				break;
> +			}
> +		}
>  		break;
>  	}
>  }
> +
> +void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +			    union v4l2_ctrl_ptr ptr)
> +{
> +	__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_DEF_VAL, ptr);
> +}
>  EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
>  
> +void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +			       union v4l2_ctrl_ptr ptr)
> +{
> +	__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
> +}
> +
> +void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +			       union v4l2_ctrl_ptr ptr)
> +{
> +	__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
> +}
> +
>  void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
>  {
>  	union v4l2_ctrl_ptr ptr = ctrl->p_cur;
> @@ -1293,6 +1359,8 @@ EXPORT_SYMBOL(v4l2_ctrl_type_op_validate);
>  static const struct v4l2_ctrl_type_ops std_type_ops = {
>  	.equal = v4l2_ctrl_type_op_equal,
>  	.init = v4l2_ctrl_type_op_init,
> +	.minimum = v4l2_ctrl_type_op_minimum,
> +	.maximum = v4l2_ctrl_type_op_maximum,
>  	.log = v4l2_ctrl_type_op_log,
>  	.validate = v4l2_ctrl_type_op_validate,
>  };
> @@ -1764,7 +1832,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  			s64 min, s64 max, u64 step, s64 def,
>  			const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
>  			u32 flags, const char * const *qmenu,
> -			const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def,
> +			const s64 *qmenu_int,
> +			const union v4l2_ctrl_ptr p_def,
> +			const union v4l2_ctrl_ptr p_min,
> +			const union v4l2_ctrl_ptr p_max,
>  			void *priv)
>  {
>  	struct v4l2_ctrl *ctrl;
> @@ -1888,6 +1959,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		break;
>  	}
>  
> +	if (type < V4L2_CTRL_COMPOUND_TYPES &&
> +	    type != V4L2_CTRL_TYPE_BUTTON &&
> +	    type != V4L2_CTRL_TYPE_CTRL_CLASS &&
> +	    type != V4L2_CTRL_TYPE_STRING)
> +		flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
> +
>  	/* Sanity checks */
>  	if (id == 0 || name == NULL || !elem_size ||
>  	    id >= V4L2_CID_PRIVATE_BASE ||
> @@ -1896,6 +1973,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		handler_set_err(hdl, -ERANGE);
>  		return NULL;
>  	}
> +
>  	err = check_range(type, min, max, step, def);
>  	if (err) {
>  		handler_set_err(hdl, err);
> @@ -1937,6 +2015,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  
>  	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
>  		sz_extra += elem_size;
> +	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
> +		sz_extra += elem_size;
> +	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_max.p_const)
> +		sz_extra += elem_size;
>  
>  	ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
>  	if (ctrl == NULL) {
> @@ -2002,6 +2084,22 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
>  	}
>  
> +	if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) {
> +		void *ptr = ctrl->p_def.p;
> +
> +		if (p_min.p_const) {
> +			ptr += elem_size;
> +			ctrl->p_min.p = ptr;
> +			memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> +		}
> +
> +		if (p_max.p_const) {
> +			ptr += elem_size;
> +			ctrl->p_max.p = ptr;
> +			memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> +		}
> +	}
> +
>  	ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
>  	cur_to_new(ctrl);
>  
> @@ -2052,7 +2150,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
>  			type, min, max,
>  			is_menu ? cfg->menu_skip_mask : step, def,
>  			cfg->dims, cfg->elem_size,
> -			flags, qmenu, qmenu_int, cfg->p_def, priv);
> +			flags, qmenu, qmenu_int, cfg->p_def, cfg->p_min,
> +			cfg->p_max, priv);
>  	if (ctrl)
>  		ctrl->is_private = cfg->is_private;
>  	return ctrl;
> @@ -2077,7 +2176,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     min, max, step, def, NULL, 0,
> -			     flags, NULL, NULL, ptr_null, NULL);
> +			     flags, NULL, NULL, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std);
>  
> @@ -2110,7 +2210,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     0, max, mask, def, NULL, 0,
> -			     flags, qmenu, qmenu_int, ptr_null, NULL);
> +			     flags, qmenu, qmenu_int, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
>  
> @@ -2142,7 +2243,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     0, max, mask, def, NULL, 0,
> -			     flags, qmenu, NULL, ptr_null, NULL);
> +			     flags, qmenu, NULL, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
> @@ -2150,7 +2252,9 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
>  /* Helper function for standard compound controls */
>  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>  				const struct v4l2_ctrl_ops *ops, u32 id,
> -				const union v4l2_ctrl_ptr p_def)
> +				const union v4l2_ctrl_ptr p_def,
> +				const union v4l2_ctrl_ptr p_min,
> +				const union v4l2_ctrl_ptr p_max)
>  {
>  	const char *name;
>  	enum v4l2_ctrl_type type;
> @@ -2164,7 +2268,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     min, max, step, def, NULL, 0,
> -			     flags, NULL, NULL, p_def, NULL);
> +			     flags, NULL, NULL, p_def, p_min, p_max, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std_compound);
>  
> @@ -2188,7 +2292,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     0, max, 0, def, NULL, 0,
> -			     flags, NULL, qmenu_int, ptr_null, NULL);
> +			     flags, NULL, qmenu_int, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
>  
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 9b1de54ce379..db5bd765db3c 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -884,7 +884,9 @@ static bool check_ext_ctrls(struct v4l2_ext_controls *c, unsigned long ioctl)
>  			return false;
>  		break;
>  	case V4L2_CTRL_WHICH_DEF_VAL:
> -		/* Default value cannot be changed */
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +	case V4L2_CTRL_WHICH_MAX_VAL:
> +		/* Default, minimum or maximum value cannot be changed */
>  		if (ioctl == VIDIOC_S_EXT_CTRLS ||
>  		    ioctl == VIDIOC_TRY_EXT_CTRLS) {
>  			c->error_idx = c->count;
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index b0db167a3ac4..9ed7be1e696f 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -133,6 +133,8 @@ struct v4l2_ctrl_ops {
>   *
>   * @equal: return true if all ctrl->elems array elements are equal.
>   * @init: initialize the value for array elements from from_idx to ctrl->elems.
> + * @minimum: set the value to the minimum value of the control.
> + * @maximum: set the value to the maximum value of the control.
>   * @log: log the value.
>   * @validate: validate the value for ctrl->new_elems array elements.
>   *	Return 0 on success and a negative value otherwise.
> @@ -142,6 +144,10 @@ struct v4l2_ctrl_type_ops {
>  		      union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2);
>  	void (*init)(const struct v4l2_ctrl *ctrl, u32 from_idx,
>  		     union v4l2_ctrl_ptr ptr);
> +	void (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
> +			union v4l2_ctrl_ptr ptr);
> +	void (*maximum)(const struct v4l2_ctrl *ctrl, u32 idx,
> +			union v4l2_ctrl_ptr ptr);
>  	void (*log)(const struct v4l2_ctrl *ctrl);
>  	int (*validate)(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr ptr);
>  };
> @@ -247,6 +253,12 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
>   * @p_def:	The control's default value represented via a union which
>   *		provides a standard way of accessing control types
>   *		through a pointer (for compound controls only).
> + * @p_min:	The control's minimum value represented via a union which
> + *		provides a standard way of accessing control types
> + *		through a pointer (for compound controls only).
> + * @p_max:	The control's maximum value represented via a union which
> + *		provides a standard way of accessing control types
> + *		through a pointer (for compound controls only).
>   * @p_cur:	The control's current value represented via a union which
>   *		provides a standard way of accessing control types
>   *		through a pointer.
> @@ -306,6 +318,8 @@ struct v4l2_ctrl {
>  	} cur;
>  
>  	union v4l2_ctrl_ptr p_def;
> +	union v4l2_ctrl_ptr p_min;
> +	union v4l2_ctrl_ptr p_max;
>  	union v4l2_ctrl_ptr p_new;
>  	union v4l2_ctrl_ptr p_cur;
>  };
> @@ -425,6 +439,8 @@ struct v4l2_ctrl_handler {
>   * @step:	The control's step value for non-menu controls.
>   * @def:	The control's default value.
>   * @p_def:	The control's default value for compound controls.
> + * @p_min:	The control's minimum value for compound controls.
> + * @p_max:	The control's maximum value for compound controls.
>   * @dims:	The size of each dimension.
>   * @elem_size:	The size in bytes of the control.
>   * @flags:	The control's flags.
> @@ -454,6 +470,8 @@ struct v4l2_ctrl_config {
>  	u64 step;
>  	s64 def;
>  	union v4l2_ctrl_ptr p_def;
> +	union v4l2_ctrl_ptr p_min;
> +	union v4l2_ctrl_ptr p_max;
>  	u32 dims[V4L2_CTRL_MAX_DIMS];
>  	u32 elem_size;
>  	u32 flags;
> @@ -723,17 +741,25 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
>   * @ops:       The control ops.
>   * @id:        The control ID.
>   * @p_def:     The control's default value.
> + * @p_min:     The control's minimum value.
> + * @p_max:     The control's maximum value.
>   *
> - * Sames as v4l2_ctrl_new_std(), but with support to compound controls, thanks
> - * to the @p_def field. Use v4l2_ctrl_ptr_create() to create @p_def from a
> - * pointer. Use v4l2_ctrl_ptr_create(NULL) if the default value of the
> - * compound control should be all zeroes.
> + * Same as v4l2_ctrl_new_std(), but with support for compound controls.
> + * To fill in the @p_def, @p_min and @p_max fields, use v4l2_ctrl_ptr_create()
> + * to convert a pointer to a const union v4l2_ctrl_ptr.
> + * Use v4l2_ctrl_ptr_create(NULL) if you want the default, minimum or maximum
> + * value of the compound control to be all zeroes.
> + * If the compound control does not set the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
> + * flag, then it does not has minimum and maximum values. In that case just use

has -> have

Regards,

	Hans

> + * v4l2_ctrl_ptr_create(NULL) for the @p_min and @p_max arguments.
>   *
>   */
>  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>  					     const struct v4l2_ctrl_ops *ops,
>  					     u32 id,
> -					     const union v4l2_ctrl_ptr p_def);
> +					     const union v4l2_ctrl_ptr p_def,
> +					     const union v4l2_ctrl_ptr p_min,
> +					     const union v4l2_ctrl_ptr p_max);
>  
>  /**
>   * v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control.
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 82d86abcf89c..8fdeb5188af5 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1866,6 +1866,8 @@ struct v4l2_ext_controls {
>  #define V4L2_CTRL_WHICH_CUR_VAL   0
>  #define V4L2_CTRL_WHICH_DEF_VAL   0x0f000000
>  #define V4L2_CTRL_WHICH_REQUEST_VAL 0x0f010000
> +#define V4L2_CTRL_WHICH_MIN_VAL   0x0f020000
> +#define V4L2_CTRL_WHICH_MAX_VAL   0x0f030000
>  
>  enum v4l2_ctrl_type {
>  	V4L2_CTRL_TYPE_INTEGER	     = 1,
> @@ -1973,6 +1975,7 @@ struct v4l2_querymenu {
>  #define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE	0x0200
>  #define V4L2_CTRL_FLAG_MODIFY_LAYOUT	0x0400
>  #define V4L2_CTRL_FLAG_DYNAMIC_ARRAY	0x0800
> +#define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
>  
>  /*  Query flags, to be ORed with the control ID */
>  #define V4L2_CTRL_FLAG_NEXT_CTRL	0x80000000


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2023-12-01  7:18 ` [PATCH v14 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
@ 2023-12-01  8:35   ` Hans Verkuil
  2023-12-08 13:41     ` Laurent Pinchart
  0 siblings, 1 reply; 40+ messages in thread
From: Hans Verkuil @ 2023-12-01  8:35 UTC (permalink / raw)
  To: Yunke Cao, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

On 01/12/2023 08:18, Yunke Cao wrote:
> Add p_rect to struct v4l2_ext_control with basic support in
> v4l2-ctrls.
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> Reviewed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> ---
> Changelog since v12:
> - No Change.
> Changelog since v11:
> - Added reviewed-by from Hans
> Changelog since v10:
> - Added reviewed-by from Sergey and Daniel.
> Changelog since v9:
> - No Change.
> Changelog since v8:
> - No change.
> Changelog since v7:
> - Document V4L2_CTRL_TYPE_RECT in vidioc-queryctrl.rst.
> - Rebased to media-stage master.
> - Do not assign each field in std_equal
> 
>  .../media/v4l/vidioc-g-ext-ctrls.rst             |  4 ++++
>  .../userspace-api/media/v4l/vidioc-queryctrl.rst |  7 +++++++
>  .../media/videodev2.h.rst.exceptions             |  1 +
>  drivers/media/v4l2-core/v4l2-ctrls-core.c        | 16 +++++++++++++++-
>  include/media/v4l2-ctrls.h                       |  2 ++
>  include/uapi/linux/videodev2.h                   |  2 ++
>  6 files changed, 31 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> index f9f73530a6be..7b1001d11f9c 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> @@ -199,6 +199,10 @@ still cause this situation.
>        - ``p_area``
>        - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is
>          of type ``V4L2_CTRL_TYPE_AREA``.
> +    * - struct :c:type:`v4l2_rect` *
> +      - ``p_rect``
> +      - A pointer to a struct :c:type:`v4l2_rect`. Valid if this control is
> +        of type ``V4L2_CTRL_TYPE_RECT``.
>      * - struct :c:type:`v4l2_ctrl_h264_sps` *
>        - ``p_h264_sps``
>        - A pointer to a struct :c:type:`v4l2_ctrl_h264_sps`. Valid if this control is
> diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> index 4d38acafe8e1..56d5c8b0b88b 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> @@ -441,6 +441,13 @@ See also the examples in :ref:`control`.
>        - n/a
>        - A struct :c:type:`v4l2_area`, containing the width and the height
>          of a rectangular area. Units depend on the use case.
> +    * - ``V4L2_CTRL_TYPE_RECT``
> +      - n/a
> +      - n/a
> +      - n/a
> +      - A struct :c:type:`v4l2_rect`, containing a rectangle described by
> +	the position of its top-left corner, the width and the height. Units
> +	depend on the use case.
>      * - ``V4L2_CTRL_TYPE_H264_SPS``
>        - n/a
>        - n/a
> diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> index 3e58aac4ef0b..c46082ef0e4d 100644
> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> @@ -150,6 +150,7 @@ replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
> +replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type`
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> index a662fb60f73f..f1486ab032cf 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -367,7 +367,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
>  	case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
>  		pr_cont("AV1_FILM_GRAIN");
>  		break;
> -
> +	case V4L2_CTRL_TYPE_RECT:
> +		pr_cont("%ux%u@%dx%d",
> +			ptr.p_rect->width, ptr.p_rect->height,
> +			ptr.p_rect->left, ptr.p_rect->top);
> +		break;
>  	default:
>  		pr_cont("unknown type %d", ctrl->type);
>  		break;
> @@ -812,6 +816,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
>  	struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
>  	struct v4l2_area *area;
> +	struct v4l2_rect *rect;
>  	void *p = ptr.p + idx * ctrl->elem_size;
>  	unsigned int i;
>  
> @@ -1169,6 +1174,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  			return -EINVAL;
>  		break;
>  
> +	case V4L2_CTRL_TYPE_RECT:
> +		rect = p;
> +		if (!rect->width || !rect->height)
> +			return -EINVAL;
> +		break;
> +
>  	default:
>  		return -EINVAL;
>  	}
> @@ -1868,6 +1879,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  	case V4L2_CTRL_TYPE_AREA:
>  		elem_size = sizeof(struct v4l2_area);
>  		break;
> +	case V4L2_CTRL_TYPE_RECT:
> +		elem_size = sizeof(struct v4l2_rect);
> +		break;
>  	default:
>  		if (type < V4L2_CTRL_COMPOUND_TYPES)
>  			elem_size = sizeof(s32);
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index 59679a42b3e7..b0db167a3ac4 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -56,6 +56,7 @@ struct video_device;
>   * @p_av1_tile_group_entry:	Pointer to an AV1 tile group entry structure.
>   * @p_av1_frame:		Pointer to an AV1 frame structure.
>   * @p_av1_film_grain:		Pointer to an AV1 film grain structure.
> + * @p_rect:			Pointer to a rectangle.
>   * @p:				Pointer to a compound value.
>   * @p_const:			Pointer to a constant compound value.
>   */
> @@ -89,6 +90,7 @@ union v4l2_ctrl_ptr {
>  	struct v4l2_ctrl_av1_tile_group_entry *p_av1_tile_group_entry;
>  	struct v4l2_ctrl_av1_frame *p_av1_frame;
>  	struct v4l2_ctrl_av1_film_grain *p_av1_film_grain;
> +	struct v4l2_rect *p_rect;
>  	void *p;
>  	const void *p_const;
>  };
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index c3d4e490ce7c..82d86abcf89c 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1815,6 +1815,7 @@ struct v4l2_ext_control {
>  		__s32 __user *p_s32;
>  		__s64 __user *p_s64;
>  		struct v4l2_area __user *p_area;
> +		struct v4l2_rect __user *p_rect;
>  		struct v4l2_ctrl_h264_sps __user *p_h264_sps;
>  		struct v4l2_ctrl_h264_pps *p_h264_pps;
>  		struct v4l2_ctrl_h264_scaling_matrix __user *p_h264_scaling_matrix;

This will probably not apply cleanly anymore to the latest staging tree due
to a change to this struct that was merged there.

Laurent, are you planning to make a PR for this? If so, then you can fix this
up yourself, ditto for the very small typo in patch 06/11 that I found.

I'm happy with the v4l2 control changes, so this is ready to go as far as I am
concerned.

Regards,

	Hans

> @@ -1883,6 +1884,7 @@ enum v4l2_ctrl_type {
>  	V4L2_CTRL_TYPE_U16	     = 0x0101,
>  	V4L2_CTRL_TYPE_U32	     = 0x0102,
>  	V4L2_CTRL_TYPE_AREA          = 0x0106,
> +	V4L2_CTRL_TYPE_RECT	     = 0x0107,
>  
>  	V4L2_CTRL_TYPE_HDR10_CLL_INFO		= 0x0110,
>  	V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY	= 0x0111,


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-12-01  7:18 ` [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
  2023-12-01  8:32   ` Hans Verkuil
@ 2023-12-05 22:30   ` kernel test robot
  2023-12-06  4:34   ` kernel test robot
                     ` (3 subsequent siblings)
  5 siblings, 0 replies; 40+ messages in thread
From: kernel test robot @ 2023-12-05 22:30 UTC (permalink / raw)
  To: Yunke Cao, Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: oe-kbuild-all, Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda,
	linux-media, Yunke Cao

Hi Yunke,

kernel test robot noticed the following build warnings:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on usb/usb-next usb/usb-linus linuxtv-media-stage/master linus/master v6.7-rc4 next-20231205]
[cannot apply to media-tree/master sailus-media-tree/streams]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Yunke-Cao/media-v4l2_ctrl-Add-V4L2_CTRL_TYPE_RECT/20231201-152501
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link:    https://lore.kernel.org/r/20231201071907.3080126-7-yunkec%40google.com
patch subject: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
config: alpha-randconfig-r122-20231202 (https://download.01.org/0day-ci/archive/20231206/202312060621.h935AOSJ-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 13.2.0
reproduce: (https://download.01.org/0day-ci/archive/20231206/202312060621.h935AOSJ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202312060621.h935AOSJ-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/media/v4l2-core/v4l2-ctrls-core.c:309:6: sparse: sparse: symbol 'v4l2_ctrl_type_op_minimum' was not declared. Should it be static?
>> drivers/media/v4l2-core/v4l2-ctrls-core.c:315:6: sparse: sparse: symbol 'v4l2_ctrl_type_op_maximum' was not declared. Should it be static?
   drivers/media/v4l2-core/v4l2-ctrls-core.c: note: in included file (through include/linux/preempt.h, include/linux/spinlock.h, include/linux/mmzone.h, ...):
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true

vim +/v4l2_ctrl_type_op_minimum +309 drivers/media/v4l2-core/v4l2-ctrls-core.c

   308	
 > 309	void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
   310				       union v4l2_ctrl_ptr ptr)
   311	{
   312		__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
   313	}
   314	
 > 315	void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
   316				       union v4l2_ctrl_ptr ptr)
   317	{
   318		__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
   319	}
   320	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-12-01  7:18 ` [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
  2023-12-01  8:32   ` Hans Verkuil
  2023-12-05 22:30   ` kernel test robot
@ 2023-12-06  4:34   ` kernel test robot
  2023-12-06  5:59   ` kernel test robot
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 40+ messages in thread
From: kernel test robot @ 2023-12-06  4:34 UTC (permalink / raw)
  To: Yunke Cao, Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: oe-kbuild-all, Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda,
	linux-media, Yunke Cao

Hi Yunke,

kernel test robot noticed the following build warnings:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on usb/usb-next usb/usb-linus linuxtv-media-stage/master linus/master v6.7-rc4 next-20231205]
[cannot apply to media-tree/master sailus-media-tree/streams]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Yunke-Cao/media-v4l2_ctrl-Add-V4L2_CTRL_TYPE_RECT/20231201-152501
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link:    https://lore.kernel.org/r/20231201071907.3080126-7-yunkec%40google.com
patch subject: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
config: i386-buildonly-randconfig-001-20231201 (https://download.01.org/0day-ci/archive/20231206/202312061239.MnFkbC6Q-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231206/202312061239.MnFkbC6Q-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202312061239.MnFkbC6Q-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/media/v4l2-core/v4l2-ctrls-core.c:309:6: warning: no previous prototype for 'v4l2_ctrl_type_op_minimum' [-Wmissing-prototypes]
     309 | void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/media/v4l2-core/v4l2-ctrls-core.c:315:6: warning: no previous prototype for 'v4l2_ctrl_type_op_maximum' [-Wmissing-prototypes]
     315 | void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~


vim +/v4l2_ctrl_type_op_minimum +309 drivers/media/v4l2-core/v4l2-ctrls-core.c

   308	
 > 309	void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
   310				       union v4l2_ctrl_ptr ptr)
   311	{
   312		__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
   313	}
   314	
 > 315	void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
   316				       union v4l2_ctrl_ptr ptr)
   317	{
   318		__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
   319	}
   320	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 05/11] media: uvcvideo: Add support for compound controls
  2023-12-01  7:18 ` [PATCH v14 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
@ 2023-12-06  5:45   ` Dan Carpenter
  2023-12-08 15:07   ` Laurent Pinchart
  1 sibling, 0 replies; 40+ messages in thread
From: Dan Carpenter @ 2023-12-06  5:45 UTC (permalink / raw)
  To: oe-kbuild, Yunke Cao, Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: lkp, oe-kbuild-all, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media, Yunke Cao

Hi Yunke,

kernel test robot noticed the following build warnings:

https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Yunke-Cao/media-v4l2_ctrl-Add-V4L2_CTRL_TYPE_RECT/20231201-152501
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link:    https://lore.kernel.org/r/20231201071907.3080126-6-yunkec%40google.com
patch subject: [PATCH v14 05/11] media: uvcvideo: Add support for compound controls
config: powerpc64-randconfig-r081-20231204 (https://download.01.org/0day-ci/archive/20231206/202312060523.tldbovNM-lkp@intel.com/config)
compiler: powerpc64-linux-gcc (GCC) 13.2.0
reproduce: (https://download.01.org/0day-ci/archive/20231206/202312060523.tldbovNM-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202312060523.tldbovNM-lkp@intel.com/

smatch warnings:
drivers/media/usb/uvc/uvc_ctrl.c:2065 __uvc_ctrl_set_compound() warn: maybe return -EFAULT instead of the bytes remaining?

vim +2065 drivers/media/usb/uvc/uvc_ctrl.c

1a1a2af88920e9 Yunke Cao 2023-12-01  2040  static int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
1a1a2af88920e9 Yunke Cao 2023-12-01  2041  				   struct v4l2_ext_control *xctrl,
1a1a2af88920e9 Yunke Cao 2023-12-01  2042  				   struct uvc_control *ctrl)
1a1a2af88920e9 Yunke Cao 2023-12-01  2043  {
1a1a2af88920e9 Yunke Cao 2023-12-01  2044  	u8 *data;
1a1a2af88920e9 Yunke Cao 2023-12-01  2045  	int ret;
1a1a2af88920e9 Yunke Cao 2023-12-01  2046  
1a1a2af88920e9 Yunke Cao 2023-12-01  2047  	if (xctrl->size != mapping->v4l2_size / 8)
1a1a2af88920e9 Yunke Cao 2023-12-01  2048  		return -EINVAL;
1a1a2af88920e9 Yunke Cao 2023-12-01  2049  
1a1a2af88920e9 Yunke Cao 2023-12-01  2050  	data = kmalloc(xctrl->size, GFP_KERNEL);
1a1a2af88920e9 Yunke Cao 2023-12-01  2051  	if (!data)
1a1a2af88920e9 Yunke Cao 2023-12-01  2052  		return -ENOMEM;
1a1a2af88920e9 Yunke Cao 2023-12-01  2053  
1a1a2af88920e9 Yunke Cao 2023-12-01  2054  	ret = copy_from_user(data, xctrl->ptr, xctrl->size);

copy_from_user() never returns negatives, it returns the number of bytes
remaining to be copied.  This should be:

	if (copy_from_user(data, xctrl->ptr, xctrl->size)) {
		ret = -EFAULT;
		goto out;
	}

1a1a2af88920e9 Yunke Cao 2023-12-01  2055  	if (ret < 0)
1a1a2af88920e9 Yunke Cao 2023-12-01  2056  		goto out;
1a1a2af88920e9 Yunke Cao 2023-12-01  2057  
1a1a2af88920e9 Yunke Cao 2023-12-01  2058  	ret = mapping->set_compound(mapping, data,
1a1a2af88920e9 Yunke Cao 2023-12-01  2059  			uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
1a1a2af88920e9 Yunke Cao 2023-12-01  2060  
1a1a2af88920e9 Yunke Cao 2023-12-01  2061  	__uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT, xctrl);
1a1a2af88920e9 Yunke Cao 2023-12-01  2062  
1a1a2af88920e9 Yunke Cao 2023-12-01  2063  out:
1a1a2af88920e9 Yunke Cao 2023-12-01  2064  	kfree(data);
1a1a2af88920e9 Yunke Cao 2023-12-01 @2065  	return ret;
1a1a2af88920e9 Yunke Cao 2023-12-01  2066  }

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-12-01  7:18 ` [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
                     ` (2 preceding siblings ...)
  2023-12-06  4:34   ` kernel test robot
@ 2023-12-06  5:59   ` kernel test robot
  2023-12-08 15:20   ` Laurent Pinchart
  2023-12-14 17:34   ` kernel test robot
  5 siblings, 0 replies; 40+ messages in thread
From: kernel test robot @ 2023-12-06  5:59 UTC (permalink / raw)
  To: Yunke Cao, Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: oe-kbuild-all, Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda,
	linux-media, Yunke Cao

Hi Yunke,

kernel test robot noticed the following build warnings:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on usb/usb-next usb/usb-linus linuxtv-media-stage/master linus/master v6.7-rc4 next-20231205]
[cannot apply to media-tree/master sailus-media-tree/streams]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Yunke-Cao/media-v4l2_ctrl-Add-V4L2_CTRL_TYPE_RECT/20231201-152501
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link:    https://lore.kernel.org/r/20231201071907.3080126-7-yunkec%40google.com
patch subject: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
config: hexagon-randconfig-r081-20231201 (https://download.01.org/0day-ci/archive/20231206/202312061351.GzQ22IuG-lkp@intel.com/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project.git 4a5ac14ee968ff0ad5d2cc1ffa0299048db4c88a)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231206/202312061351.GzQ22IuG-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202312061351.GzQ22IuG-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from drivers/media/v4l2-core/v4l2-ctrls-core.c:11:
   In file included from include/media/v4l2-ctrls.h:14:
   In file included from include/media/media-request.h:20:
   In file included from include/media/media-device.h:16:
   In file included from include/linux/pci.h:38:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:337:
   include/asm-generic/io.h:547:31: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     547 |         val = __raw_readb(PCI_IOBASE + addr);
         |                           ~~~~~~~~~~ ^
   include/asm-generic/io.h:560:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     560 |         val = __le16_to_cpu((__le16 __force)__raw_readw(PCI_IOBASE + addr));
         |                                                         ~~~~~~~~~~ ^
   include/uapi/linux/byteorder/little_endian.h:37:51: note: expanded from macro '__le16_to_cpu'
      37 | #define __le16_to_cpu(x) ((__force __u16)(__le16)(x))
         |                                                   ^
   In file included from drivers/media/v4l2-core/v4l2-ctrls-core.c:11:
   In file included from include/media/v4l2-ctrls.h:14:
   In file included from include/media/media-request.h:20:
   In file included from include/media/media-device.h:16:
   In file included from include/linux/pci.h:38:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:337:
   include/asm-generic/io.h:573:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     573 |         val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr));
         |                                                         ~~~~~~~~~~ ^
   include/uapi/linux/byteorder/little_endian.h:35:51: note: expanded from macro '__le32_to_cpu'
      35 | #define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
         |                                                   ^
   In file included from drivers/media/v4l2-core/v4l2-ctrls-core.c:11:
   In file included from include/media/v4l2-ctrls.h:14:
   In file included from include/media/media-request.h:20:
   In file included from include/media/media-device.h:16:
   In file included from include/linux/pci.h:38:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:337:
   include/asm-generic/io.h:584:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     584 |         __raw_writeb(value, PCI_IOBASE + addr);
         |                             ~~~~~~~~~~ ^
   include/asm-generic/io.h:594:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     594 |         __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr);
         |                                                       ~~~~~~~~~~ ^
   include/asm-generic/io.h:604:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     604 |         __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr);
         |                                                       ~~~~~~~~~~ ^
>> drivers/media/v4l2-core/v4l2-ctrls-core.c:309:6: warning: no previous prototype for function 'v4l2_ctrl_type_op_minimum' [-Wmissing-prototypes]
     309 | void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
         |      ^
   drivers/media/v4l2-core/v4l2-ctrls-core.c:309:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     309 | void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
         | ^
         | static 
>> drivers/media/v4l2-core/v4l2-ctrls-core.c:315:6: warning: no previous prototype for function 'v4l2_ctrl_type_op_maximum' [-Wmissing-prototypes]
     315 | void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
         |      ^
   drivers/media/v4l2-core/v4l2-ctrls-core.c:315:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     315 | void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
         | ^
         | static 
   8 warnings generated.


vim +/v4l2_ctrl_type_op_minimum +309 drivers/media/v4l2-core/v4l2-ctrls-core.c

   308	
 > 309	void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
   310				       union v4l2_ctrl_ptr ptr)
   311	{
   312		__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
   313	}
   314	
 > 315	void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
   316				       union v4l2_ctrl_ptr ptr)
   317	{
   318		__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
   319	}
   320	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2023-12-01  8:35   ` Hans Verkuil
@ 2023-12-08 13:41     ` Laurent Pinchart
  2023-12-12  1:33       ` Yunke Cao
  0 siblings, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-08 13:41 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Yunke Cao, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Hans,

On Fri, Dec 01, 2023 at 09:35:21AM +0100, Hans Verkuil wrote:
> On 01/12/2023 08:18, Yunke Cao wrote:
> > Add p_rect to struct v4l2_ext_control with basic support in
> > v4l2-ctrls.
> > 
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> > Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> > Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > Reviewed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> > ---
> > Changelog since v12:
> > - No Change.
> > Changelog since v11:
> > - Added reviewed-by from Hans
> > Changelog since v10:
> > - Added reviewed-by from Sergey and Daniel.
> > Changelog since v9:
> > - No Change.
> > Changelog since v8:
> > - No change.
> > Changelog since v7:
> > - Document V4L2_CTRL_TYPE_RECT in vidioc-queryctrl.rst.
> > - Rebased to media-stage master.
> > - Do not assign each field in std_equal
> > 
> >  .../media/v4l/vidioc-g-ext-ctrls.rst             |  4 ++++
> >  .../userspace-api/media/v4l/vidioc-queryctrl.rst |  7 +++++++
> >  .../media/videodev2.h.rst.exceptions             |  1 +
> >  drivers/media/v4l2-core/v4l2-ctrls-core.c        | 16 +++++++++++++++-
> >  include/media/v4l2-ctrls.h                       |  2 ++
> >  include/uapi/linux/videodev2.h                   |  2 ++
> >  6 files changed, 31 insertions(+), 1 deletion(-)
> > 
> > diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > index f9f73530a6be..7b1001d11f9c 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > @@ -199,6 +199,10 @@ still cause this situation.
> >        - ``p_area``
> >        - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is
> >          of type ``V4L2_CTRL_TYPE_AREA``.
> > +    * - struct :c:type:`v4l2_rect` *
> > +      - ``p_rect``
> > +      - A pointer to a struct :c:type:`v4l2_rect`. Valid if this control is
> > +        of type ``V4L2_CTRL_TYPE_RECT``.
> >      * - struct :c:type:`v4l2_ctrl_h264_sps` *
> >        - ``p_h264_sps``
> >        - A pointer to a struct :c:type:`v4l2_ctrl_h264_sps`. Valid if this control is
> > diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > index 4d38acafe8e1..56d5c8b0b88b 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > @@ -441,6 +441,13 @@ See also the examples in :ref:`control`.
> >        - n/a
> >        - A struct :c:type:`v4l2_area`, containing the width and the height
> >          of a rectangular area. Units depend on the use case.
> > +    * - ``V4L2_CTRL_TYPE_RECT``
> > +      - n/a
> > +      - n/a
> > +      - n/a
> > +      - A struct :c:type:`v4l2_rect`, containing a rectangle described by
> > +	the position of its top-left corner, the width and the height. Units
> > +	depend on the use case.
> >      * - ``V4L2_CTRL_TYPE_H264_SPS``
> >        - n/a
> >        - n/a
> > diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > index 3e58aac4ef0b..c46082ef0e4d 100644
> > --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > @@ -150,6 +150,7 @@ replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
> > +replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type`
> > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > index a662fb60f73f..f1486ab032cf 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > @@ -367,7 +367,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> >  	case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
> >  		pr_cont("AV1_FILM_GRAIN");
> >  		break;
> > -
> > +	case V4L2_CTRL_TYPE_RECT:
> > +		pr_cont("%ux%u@%dx%d",
> > +			ptr.p_rect->width, ptr.p_rect->height,
> > +			ptr.p_rect->left, ptr.p_rect->top);
> > +		break;
> >  	default:
> >  		pr_cont("unknown type %d", ctrl->type);
> >  		break;
> > @@ -812,6 +816,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> >  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
> >  	struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
> >  	struct v4l2_area *area;
> > +	struct v4l2_rect *rect;
> >  	void *p = ptr.p + idx * ctrl->elem_size;
> >  	unsigned int i;
> >  
> > @@ -1169,6 +1174,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> >  			return -EINVAL;
> >  		break;
> >  
> > +	case V4L2_CTRL_TYPE_RECT:
> > +		rect = p;
> > +		if (!rect->width || !rect->height)
> > +			return -EINVAL;
> > +		break;
> > +
> >  	default:
> >  		return -EINVAL;
> >  	}
> > @@ -1868,6 +1879,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >  	case V4L2_CTRL_TYPE_AREA:
> >  		elem_size = sizeof(struct v4l2_area);
> >  		break;
> > +	case V4L2_CTRL_TYPE_RECT:
> > +		elem_size = sizeof(struct v4l2_rect);
> > +		break;
> >  	default:
> >  		if (type < V4L2_CTRL_COMPOUND_TYPES)
> >  			elem_size = sizeof(s32);
> > diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> > index 59679a42b3e7..b0db167a3ac4 100644
> > --- a/include/media/v4l2-ctrls.h
> > +++ b/include/media/v4l2-ctrls.h
> > @@ -56,6 +56,7 @@ struct video_device;
> >   * @p_av1_tile_group_entry:	Pointer to an AV1 tile group entry structure.
> >   * @p_av1_frame:		Pointer to an AV1 frame structure.
> >   * @p_av1_film_grain:		Pointer to an AV1 film grain structure.
> > + * @p_rect:			Pointer to a rectangle.
> >   * @p:				Pointer to a compound value.
> >   * @p_const:			Pointer to a constant compound value.
> >   */
> > @@ -89,6 +90,7 @@ union v4l2_ctrl_ptr {
> >  	struct v4l2_ctrl_av1_tile_group_entry *p_av1_tile_group_entry;
> >  	struct v4l2_ctrl_av1_frame *p_av1_frame;
> >  	struct v4l2_ctrl_av1_film_grain *p_av1_film_grain;
> > +	struct v4l2_rect *p_rect;
> >  	void *p;
> >  	const void *p_const;
> >  };
> > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> > index c3d4e490ce7c..82d86abcf89c 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -1815,6 +1815,7 @@ struct v4l2_ext_control {
> >  		__s32 __user *p_s32;
> >  		__s64 __user *p_s64;
> >  		struct v4l2_area __user *p_area;
> > +		struct v4l2_rect __user *p_rect;
> >  		struct v4l2_ctrl_h264_sps __user *p_h264_sps;
> >  		struct v4l2_ctrl_h264_pps *p_h264_pps;
> >  		struct v4l2_ctrl_h264_scaling_matrix __user *p_h264_scaling_matrix;
> 
> This will probably not apply cleanly anymore to the latest staging tree due
> to a change to this struct that was merged there.
> 
> Laurent, are you planning to make a PR for this? If so, then you can fix this
> up yourself, ditto for the very small typo in patch 06/11 that I found.

'git am' complained indeed, but 'patch' was happy with a bit of fuzz.
I've checked the result and it's all fine.

I'm reviewing the rest of the series now.

> I'm happy with the v4l2 control changes, so this is ready to go as far as I am
> concerned.
> 
> > @@ -1883,6 +1884,7 @@ enum v4l2_ctrl_type {
> >  	V4L2_CTRL_TYPE_U16	     = 0x0101,
> >  	V4L2_CTRL_TYPE_U32	     = 0x0102,
> >  	V4L2_CTRL_TYPE_AREA          = 0x0106,
> > +	V4L2_CTRL_TYPE_RECT	     = 0x0107,
> >  
> >  	V4L2_CTRL_TYPE_HDR10_CLL_INFO		= 0x0110,
> >  	V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY	= 0x0111,

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size
  2023-12-01  7:18 ` [PATCH v14 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size Yunke Cao
@ 2023-12-08 14:15   ` Laurent Pinchart
  2023-12-12  7:59     ` Yunke Cao
  0 siblings, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-08 14:15 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Fri, Dec 01, 2023 at 04:18:55PM +0900, Yunke Cao wrote:
> Rename the existing size to data_size to represent uvc control data size,
> add a separate field for v4l2 control size. v4l2 control size will be
> used the compound controls.

s/uvc/UVC/ and s/v4l2/V4L2/ in the whole commit message.

> Also modify the uvc driver documents to clarify the size in
> uvc_xu_control_mapping corresponds to the uvc control data size.
> 
> Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> Changelog since v11:
> - No change.
> Changelog since v10:
> - Added Reviewed-by from Daniel Scally.
> Changelog since v9:
> - No change.
> Changelog since v8:
> - No change.
> Changelog since v7:
> - Newly added patch.
> 
>  .../userspace-api/media/drivers/uvcvideo.rst  |  2 +-
>  drivers/media/usb/uvc/uvc_ctrl.c              | 80 +++++++++----------
>  drivers/media/usb/uvc/uvc_v4l2.c              |  2 +-
>  drivers/media/usb/uvc/uvcvideo.h              |  6 +-
>  4 files changed, 47 insertions(+), 43 deletions(-)
> 
> diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> index a290f9fadae9..aab4304e6bb5 100644
> --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
> +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> @@ -157,7 +157,7 @@ Argument: struct uvc_xu_control_mapping
>  	__u8	name[32]	V4L2 control name
>  	__u8	entity[16]	UVC extension unit GUID
>  	__u8	selector	UVC control selector
> -	__u8	size		V4L2 control size (in bits)
> +	__u8	size		UVC control data size (in bits)

The V4L2 and UVC sizes are identical for all controls mapped through
this mechanism, right ?

>  	__u8	offset		V4L2 control offset (in bits)

If the size if the "UVC control data size", shouldn't this be the "UVC
control data offset" ?

>  	enum v4l2_ctrl_type
>  		v4l2_type	V4L2 control type
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 4a685532f7eb..98254b93eb46 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -464,7 +464,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_BRIGHTNESS,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_BRIGHTNESS_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
> @@ -473,7 +473,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_CONTRAST,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_CONTRAST_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -482,7 +482,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_HUE,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_HUE_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
> @@ -493,7 +493,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_SATURATION,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_SATURATION_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -502,7 +502,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_SHARPNESS,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_SHARPNESS_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -511,7 +511,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_GAMMA,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_GAMMA_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -520,7 +520,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_BACKLIGHT_COMPENSATION,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -529,7 +529,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_GAIN,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_GAIN_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -538,7 +538,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_HUE_AUTO,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_HUE_AUTO_CONTROL,
> -		.size		= 1,
> +		.data_size	= 1,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
> @@ -548,7 +548,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_EXPOSURE_AUTO,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_AE_MODE_CONTROL,
> -		.size		= 4,
> +		.data_size	= 4,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_MENU,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BITMASK,
> @@ -561,7 +561,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_EXPOSURE_AUTO_PRIORITY,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_AE_PRIORITY_CONTROL,
> -		.size		= 1,
> +		.data_size	= 1,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
> @@ -570,7 +570,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_EXPOSURE_ABSOLUTE,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
> -		.size		= 32,
> +		.data_size	= 32,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -581,7 +581,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_AUTO_WHITE_BALANCE,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
> -		.size		= 1,
> +		.data_size	= 1,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
> @@ -591,7 +591,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_WHITE_BALANCE_TEMPERATURE,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -602,7 +602,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_AUTO_WHITE_BALANCE,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
> -		.size		= 1,
> +		.data_size	= 1,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
> @@ -613,7 +613,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_BLUE_BALANCE,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
> @@ -624,7 +624,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_RED_BALANCE,
>  		.entity		= UVC_GUID_UVC_PROCESSING,
>  		.selector	= UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 16,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
> @@ -635,7 +635,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_FOCUS_ABSOLUTE,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_FOCUS_ABSOLUTE_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -646,7 +646,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_FOCUS_AUTO,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_FOCUS_AUTO_CONTROL,
> -		.size		= 1,
> +		.data_size	= 1,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
> @@ -656,7 +656,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_IRIS_ABSOLUTE,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_IRIS_ABSOLUTE_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -665,7 +665,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_IRIS_RELATIVE,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_IRIS_RELATIVE_CONTROL,
> -		.size		= 8,
> +		.data_size	= 8,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
> @@ -674,7 +674,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_ZOOM_ABSOLUTE,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_ZOOM_ABSOLUTE_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_UNSIGNED,
> @@ -683,7 +683,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_ZOOM_CONTINUOUS,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_ZOOM_RELATIVE_CONTROL,
> -		.size		= 0,
> +		.data_size	= 0,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
> @@ -694,7 +694,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_PAN_ABSOLUTE,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_PANTILT_ABSOLUTE_CONTROL,
> -		.size		= 32,
> +		.data_size	= 32,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
> @@ -703,7 +703,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_TILT_ABSOLUTE,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_PANTILT_ABSOLUTE_CONTROL,
> -		.size		= 32,
> +		.data_size	= 32,
>  		.offset		= 32,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
> @@ -712,7 +712,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_PAN_SPEED,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_PANTILT_RELATIVE_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
> @@ -723,7 +723,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_TILT_SPEED,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_PANTILT_RELATIVE_CONTROL,
> -		.size		= 16,
> +		.data_size	= 16,
>  		.offset		= 16,
>  		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
>  		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
> @@ -734,7 +734,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_PRIVACY,
>  		.entity		= UVC_GUID_UVC_CAMERA,
>  		.selector	= UVC_CT_PRIVACY_CONTROL,
> -		.size		= 1,
> +		.data_size	= 1,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
> @@ -743,7 +743,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.id		= V4L2_CID_PRIVACY,
>  		.entity		= UVC_GUID_EXT_GPIO_CONTROLLER,
>  		.selector	= UVC_CT_PRIVACY_CONTROL,
> -		.size		= 1,
> +		.data_size	= 1,
>  		.offset		= 0,
>  		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
> @@ -754,7 +754,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
>  	.id		= V4L2_CID_POWER_LINE_FREQUENCY,
>  	.entity		= UVC_GUID_UVC_PROCESSING,
>  	.selector	= UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
> -	.size		= 2,
> +	.data_size	= 2,
>  	.offset		= 0,
>  	.v4l2_type	= V4L2_CTRL_TYPE_MENU,
>  	.data_type	= UVC_CTRL_DATA_TYPE_ENUM,
> @@ -766,7 +766,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
>  	.id		= V4L2_CID_POWER_LINE_FREQUENCY,
>  	.entity		= UVC_GUID_UVC_PROCESSING,
>  	.selector	= UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
> -	.size		= 2,
> +	.data_size	= 2,
>  	.offset		= 0,
>  	.v4l2_type	= V4L2_CTRL_TYPE_MENU,
>  	.data_type	= UVC_CTRL_DATA_TYPE_ENUM,
> @@ -783,7 +783,7 @@ static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
>  	.id		= V4L2_CID_POWER_LINE_FREQUENCY,
>  	.entity		= UVC_GUID_UVC_PROCESSING,
>  	.selector	= UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
> -	.size		= 2,
> +	.data_size	= 2,
>  	.offset		= 0,
>  	.v4l2_type	= V4L2_CTRL_TYPE_MENU,
>  	.data_type	= UVC_CTRL_DATA_TYPE_ENUM,
> @@ -816,7 +816,7 @@ static inline void uvc_clear_bit(u8 *data, int bit)
>  }
>  
>  /*
> - * Extract the bit string specified by mapping->offset and mapping->size
> + * Extract the bit string specified by mapping->offset and mapping->data_size
>   * from the little-endian data stored at 'data' and return the result as
>   * a signed 32bit integer. Sign extension will be performed if the mapping
>   * references a signed data type.
> @@ -824,7 +824,7 @@ static inline void uvc_clear_bit(u8 *data, int bit)
>  static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
>  	u8 query, const u8 *data)
>  {
> -	int bits = mapping->size;
> +	int bits = mapping->data_size;
>  	int offset = mapping->offset;
>  	s32 value = 0;
>  	u8 mask;
> @@ -847,19 +847,19 @@ static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
>  
>  	/* Sign-extend the value if needed. */
>  	if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
> -		value |= -(value & (1 << (mapping->size - 1)));
> +		value |= -(value & (1 << (mapping->data_size - 1)));
>  
>  	return value;
>  }
>  
>  /*
> - * Set the bit string specified by mapping->offset and mapping->size
> + * Set the bit string specified by mapping->offset and mapping->data_size
>   * in the little-endian data stored at 'data' to the value 'value'.
>   */
>  static void uvc_set_le_value(struct uvc_control_mapping *mapping,
>  	s32 value, u8 *data)
>  {
> -	int bits = mapping->size;
> +	int bits = mapping->data_size;
>  	int offset = mapping->offset;
>  	u8 mask;
>  
> @@ -2039,7 +2039,7 @@ int uvc_ctrl_set(struct uvc_fh *handle,
>  	 * needs to be loaded from the device to perform the read-modify-write
>  	 * operation.
>  	 */
> -	if ((ctrl->info.size * 8) != mapping->size) {
> +	if ((ctrl->info.size * 8) != mapping->data_size) {
>  		ret = __uvc_ctrl_load_cur(chain, ctrl);
>  		if (ret < 0)
>  			return ret;
> @@ -2546,8 +2546,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
>  	}
>  
>  	/* Validate the user-provided bit-size and offset */
> -	if (mapping->size > 32 ||
> -	    mapping->offset + mapping->size > ctrl->info.size * 8) {
> +	if (mapping->data_size > 32 ||
> +	    mapping->offset + mapping->data_size > ctrl->info.size * 8) {
>  		ret = -EINVAL;
>  		goto done;
>  	}
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 5a88847bfbfe..ff72caf7bc9e 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -122,7 +122,7 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
>  	}
>  	memcpy(map->entity, xmap->entity, sizeof(map->entity));
>  	map->selector = xmap->selector;
> -	map->size = xmap->size;
> +	map->data_size = xmap->size;
>  	map->offset = xmap->offset;
>  	map->v4l2_type = xmap->v4l2_type;
>  	map->data_type = xmap->data_type;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 5091085fcfb0..7bc41270ed94 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -110,7 +110,11 @@ struct uvc_control_mapping {
>  	u8 entity[16];
>  	u8 selector;
>  
> -	u8 size;
> +	/* Size of the v4l2 control. Required for compound controls. */
> +	u8 v4l2_size;

Let's introduce this field in the patch that uses it. The commit message
needs to be updated to explain that this patch renames the size field to
data_size to prepare for adding another size field for compound
controls.

> +	/* UVC data size. Required for all controls. */

"UVC data size" is not very clear. Let me attempt to write a more
precise description:

	/*
	 * Size of the control data in the payload of the UVC control GET and
	 * SET requests, expressed in bits.
	 */

Is this correct ?

> +	u8 data_size;
> +
>  	u8 offset;
>  	enum v4l2_ctrl_type v4l2_type;
>  	u32 data_type;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 05/11] media: uvcvideo: Add support for compound controls
  2023-12-01  7:18 ` [PATCH v14 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
  2023-12-06  5:45   ` Dan Carpenter
@ 2023-12-08 15:07   ` Laurent Pinchart
  2023-12-13  7:38     ` Yunke Cao
  1 sibling, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-08 15:07 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Fri, Dec 01, 2023 at 04:18:56PM +0900, Yunke Cao wrote:
> Supports getting/setting current value.
> Supports getting default value.
> Handles V4L2_CTRL_FLAG_NEXT_COMPOUND.

Please write a better commit message.

> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> Changelog since v11:
> - No change.
> Changelog since v10:
> - Rewrite some logic in __uvc_find_control().
> - Remove a duplicate check in __uvc_ctrl_get_compound_cur().
> - Thanks, Daniel!
> Changelog since v9:
> - Make __uvc_ctrl_set_compound() static.
> Changelog since v8:
> - No change.
> Changelog since v7:
> - Fixed comments styles, indentation and a few other style issues.
> - Renamed uvc_g/set_array() to uvc_g/set_compound().
> - Moved size check to __uvc_ctrl_add_mapping().
> - After setting a new value, copy it back to user.
> - In __uvc_ctrl_set_compound(), check size before allocating.
> 
>  drivers/media/usb/uvc/uvc_ctrl.c | 179 +++++++++++++++++++++++++++----
>  drivers/media/usb/uvc/uvcvideo.h |   4 +
>  2 files changed, 164 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 98254b93eb46..aae2480496b7 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -884,6 +884,28 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
>  	}
>  }
>  
> +/*
> + * Extract the byte array specified by mapping->offset and mapping->data_size
> + * stored at 'data' to the output array 'data_out'.
> + */
> +static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
> +			    u8 *data_out)
> +{
> +	memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
> +	return 0;
> +}
> +
> +/*
> + * Copy the byte array 'data_in' to the destination specified by mapping->offset
> + * and mapping->data_size stored at 'data'.
> + */
> +static int uvc_set_compound(struct uvc_control_mapping *mapping,
> +			    const u8 *data_in, u8 *data)
> +{
> +	memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
> +	return 0;
> +}
> +

I may be missing something, but isn't this essentially dead code ? This
series adds support for a single compound control, with custom get/set
handlers, so these two default handlers will never be called.

I don't think it makes sense to have default handlers for compound
controls, I don't foresee any use case where a simple mempcy() will do
the right thing.

>  static bool
>  uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
>  {
> @@ -906,7 +928,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
>  
>  static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
>  	struct uvc_control_mapping **mapping, struct uvc_control **control,
> -	int next)
> +	int next, int next_compound)
>  {
>  	struct uvc_control *ctrl;
>  	struct uvc_control_mapping *map;
> @@ -921,14 +943,16 @@ static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
>  			continue;
>  
>  		list_for_each_entry(map, &ctrl->info.mappings, list) {
> -			if ((map->id == v4l2_id) && !next) {
> +			if (map->id == v4l2_id && !next && !next_compound) {
>  				*control = ctrl;
>  				*mapping = map;
>  				return;
>  			}
>  
>  			if ((*mapping == NULL || (*mapping)->id > map->id) &&
> -			    (map->id > v4l2_id) && next) {
> +			    (map->id > v4l2_id) &&
> +			    (uvc_ctrl_mapping_is_compound(map) ?
> +			     next_compound : next)) {
>  				*control = ctrl;
>  				*mapping = map;
>  			}
> @@ -942,6 +966,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
>  	struct uvc_control *ctrl = NULL;
>  	struct uvc_entity *entity;
>  	int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> +	int next_compound = v4l2_id & V4L2_CTRL_FLAG_NEXT_COMPOUND;
>  
>  	*mapping = NULL;
>  
> @@ -950,12 +975,13 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
>  
>  	/* Find the control. */
>  	list_for_each_entry(entity, &chain->entities, chain) {
> -		__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
> -		if (ctrl && !next)
> +		__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next,
> +				   next_compound);
> +		if (ctrl && !next && !next_compound)
>  			return ctrl;
>  	}
>  
> -	if (ctrl == NULL && !next)
> +	if (!ctrl && !next && !next_compound)
>  		uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
>  			v4l2_id);
>  
> @@ -1101,12 +1127,59 @@ static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
>  	return 0;
>  }
>  
> +static int __uvc_ctrl_get_compound(struct uvc_control_mapping *mapping,
> +				   struct uvc_control *ctrl,
> +				   int id,
> +				   struct v4l2_ext_control *xctrl)
> +{
> +	u8 size;
> +	u8 *data;
> +	int ret;
> +
> +	size = mapping->v4l2_size / 8;
> +	if (xctrl->size < size) {
> +		xctrl->size = size;
> +		return -ENOSPC;
> +	}
> +
> +	data = kmalloc(size, GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	ret = mapping->get_compound(mapping, uvc_ctrl_data(ctrl, id), data);
> +	if (ret < 0)
> +		goto out;
> +
> +	ret = copy_to_user(xctrl->ptr, data, size) ? -EFAULT : 0;
> +
> +out:
> +	kfree(data);
> +	return ret;
> +}
> +
> +static int __uvc_ctrl_get_compound_cur(struct uvc_video_chain *chain,
> +				       struct uvc_control *ctrl,
> +				       struct uvc_control_mapping *mapping,
> +				       struct v4l2_ext_control *xctrl)
> +{
> +	int ret;
> +
> +	ret = __uvc_ctrl_load_cur(chain, ctrl);
> +	if (ret < 0)
> +		return ret;
> +
> +	return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT,
> +				       xctrl);
> +}
> +
>  static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
>  				  u32 found_id)
>  {
> -	bool find_next = req_id & V4L2_CTRL_FLAG_NEXT_CTRL;
>  	unsigned int i;
>  
> +	bool find_next = req_id &
> +		(V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND);
> +
>  	req_id &= V4L2_CTRL_ID_MASK;
>  
>  	for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
> @@ -1194,7 +1267,7 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>  	}
>  
>  	__uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
> -			   &master_ctrl, 0);
> +			   &master_ctrl, 0, 0);
>  
>  	if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
>  		return 0;
> @@ -1262,7 +1335,7 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
>  
>  	if (mapping->master_id)
>  		__uvc_find_control(ctrl->entity, mapping->master_id,
> -				   &master_map, &master_ctrl, 0);
> +				   &master_map, &master_ctrl, 0, 0);
>  	if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
>  		s32 val = 0;
>  		int ret;
> @@ -1278,6 +1351,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
>  				v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
>  	}
>  
> +	if (v4l2_ctrl->type >= V4L2_CTRL_COMPOUND_TYPES) {
> +		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
> +		v4l2_ctrl->default_value = 0;
> +		v4l2_ctrl->minimum = 0;
> +		v4l2_ctrl->maximum = 0;
> +		v4l2_ctrl->step = 0;
> +		return 0;
> +	}
> +
>  	if (!ctrl->cached) {
>  		int ret = uvc_ctrl_populate_cache(chain, ctrl);
>  		if (ret < 0)
> @@ -1533,7 +1615,7 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
>  	u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
>  	s32 val = 0;
>  
> -	__uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
> +	__uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0, 0);
>  	if (ctrl == NULL)
>  		return;
>  
> @@ -1843,7 +1925,7 @@ static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
>  
>  	for (i = 0; i < ctrls->count; i++) {
>  		__uvc_find_control(entity, ctrls->controls[i].id, &mapping,
> -				   &ctrl_found, 0);
> +				   &ctrl_found, 0, 0);
>  		if (uvc_control == ctrl_found)
>  			return i;
>  	}
> @@ -1891,7 +1973,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
>  		return -EINVAL;
>  
>  	if (uvc_ctrl_mapping_is_compound(mapping))
> -		return -EINVAL;
> +		return __uvc_ctrl_get_compound_cur(chain, ctrl, mapping, xctrl);
>  	else
>  		return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
>  }
> @@ -1912,6 +1994,22 @@ static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
>  	return 0;
>  }
>  
> +static int __uvc_ctrl_get_boundary_compound(struct uvc_video_chain *chain,
> +					    struct uvc_control *ctrl,
> +					    struct uvc_control_mapping *mapping,
> +					    struct v4l2_ext_control *xctrl)
> +{
> +	int ret;
> +
> +	if (!ctrl->cached) {
> +		ret = uvc_ctrl_populate_cache(chain, ctrl);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_DEF, xctrl);
> +}
> +
>  int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
>  			  struct v4l2_ext_control *xctrl)
>  {
> @@ -1929,7 +2027,8 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
>  	}
>  
>  	if (uvc_ctrl_mapping_is_compound(mapping))
> -		ret = -EINVAL;
> +		ret = __uvc_ctrl_get_boundary_compound(chain, ctrl, mapping,
> +						       xctrl);
>  	else
>  		ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
>  
> @@ -1938,6 +2037,34 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
>  	return ret;
>  }
>  
> +static int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> +				   struct v4l2_ext_control *xctrl,
> +				   struct uvc_control *ctrl)
> +{
> +	u8 *data;
> +	int ret;
> +
> +	if (xctrl->size != mapping->v4l2_size / 8)
> +		return -EINVAL;
> +
> +	data = kmalloc(xctrl->size, GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	ret = copy_from_user(data, xctrl->ptr, xctrl->size);
> +	if (ret < 0)
> +		goto out;
> +
> +	ret = mapping->set_compound(mapping, data,
> +			uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> +
> +	__uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT, xctrl);

Why do you need to call __uvc_ctrl_get_compound() here ?

> +
> +out:
> +	kfree(data);
> +	return ret;
> +}
> +
>  int uvc_ctrl_set(struct uvc_fh *handle,
>  	struct v4l2_ext_control *xctrl)
>  {
> @@ -2052,13 +2179,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
>  		       ctrl->info.size);
>  	}
>  
> -	if (uvc_ctrl_mapping_is_compound(mapping))
> -		return -EINVAL;
> -	else
> +	if (uvc_ctrl_mapping_is_compound(mapping)) {
> +		ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
> +		if (ret < 0)
> +			return ret;
> +	} else
>  		mapping->set(mapping, value,
>  			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));

I really don't like how handling of compound controls is scattered
everywhere :-( That makes the code difficult to read, and thus more
error-prone.

Could this be refactored to simplify the implementation ? In particular,
I'm thinking about

- Moving the copy_from_user() and copy_to_user() towards the top of the
  call stack, directly in uvc_ctrl_get(), uvc_ctrl_get_boundary() and
  uvc_ctrl_set() 

- Merging the .[gs]et() and .[gs]et_compound() functions (see below)

- Moving the value clamping from uvc_set_compound_rect() to
  uvc_ctrl_set(), with the rest of the clamping code

>  
> -
>  	if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
>  		ctrl->handle = handle;
>  
> @@ -2468,10 +2596,23 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
>  			goto err_nomem;
>  	}
>  
> -	if (map->get == NULL)
> +	if (uvc_ctrl_mapping_is_compound(map)) {
> +		if (map->data_size != map->v4l2_size)
> +			return -EINVAL;

If the two values have to be the same, why do we have two fields ?

> +
> +		/* 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))
>  		map->get = uvc_get_le_value;
> -	if (map->set == NULL)
> +	if (!map->set && !uvc_ctrl_mapping_is_compound(map))
>  		map->set = uvc_set_le_value;
> +	if (!map->get_compound && uvc_ctrl_mapping_is_compound(map))
> +		map->get_compound = uvc_get_compound;
> +	if (!map->set_compound && uvc_ctrl_mapping_is_compound(map))
> +		map->set_compound = uvc_set_compound;
>  
>  	for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
>  		if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 7bc41270ed94..11805b729c22 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -129,8 +129,12 @@ struct uvc_control_mapping {
>  
>  	s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
>  		   const u8 *data);
> +	int (*get_compound)(struct uvc_control_mapping *mapping, const u8 *data,
> +			    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);

Instead of adding new functions, I think we could modify the existing
.get() and .set() handlers to be usable for compound controls.

	int (*get)(struct uvc_control_mapping *mapping, u8 query,
		   const u8 *data_in, void *data_out);
	void (*set)(struct uvc_control_mapping *mapping, const void *data_in,
		    u8 *data_out);

For additional safety, you could pass the size of the void * buffer to
the functions.

>  };
>  
>  struct uvc_control {

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std()
  2023-12-01  7:18 ` [PATCH v14 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() Yunke Cao
@ 2023-12-08 15:12   ` Laurent Pinchart
  0 siblings, 0 replies; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-08 15:12 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Fri, Dec 01, 2023 at 04:18:54PM +0900, Yunke Cao wrote:
> Refactor uvc_ctrl to make adding compound control easier.
> 
> Currently __uvc_ctrl_get() only work for non-compound controls.
> Move the logic into __uvc_ctrl_std(), return error for compound
> controls.
> 
> Make __uvc_ctrl_get() call __uvc_ctrl_std() inside. Also modify some of the
> callers of __uvc_ctrl_get() to call __uvc_ctrl_get_std() instead.
> 
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> Changelog since v12:
> - No change.
> Changelog since v11:
> - Minor change to avoid negative if statement.
> Changelog since v10:
> - Better commit message.
> Changelog since v9:
> - No change.
> Changelog since v8:
> - No change.
> Changelog since v7:
> - Newly added patch. Split the refactoring of uvc_ctrl_get from v7 3/7.
> 
>  drivers/media/usb/uvc/uvc_ctrl.c | 42 +++++++++++++++++++++-----------
>  1 file changed, 28 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 8d8333786333..4a685532f7eb 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1081,15 +1081,15 @@ static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
>  	return ret;
>  }
>  
> -static int __uvc_ctrl_get(struct uvc_video_chain *chain,
> -			  struct uvc_control *ctrl,
> -			  struct uvc_control_mapping *mapping,
> -			  s32 *value)
> +static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
> +			      struct uvc_control *ctrl,
> +			      struct uvc_control_mapping *mapping,
> +			      s32 *value)
>  {
>  	int ret;
>  
> -	if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
> -		return -EACCES;
> +	if (uvc_ctrl_mapping_is_compound(mapping))
> +		return -EINVAL;

This duplicates the check that you have added in multiple callers. With
the whole series applied, this function is called without a compound
check from uvc_ctrl_is_accessible() only. I think it would be better to
explicitly handle compound controls there. Even better possible, you
could check at driver initialization time that V4L2 master controls are
never compound controls.

>  
>  	ret = __uvc_ctrl_load_cur(chain, ctrl);
>  	if (ret < 0)
> @@ -1199,7 +1199,7 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>  	if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
>  		return 0;
>  
> -	ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
> +	ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
>  	if (ret >= 0 && val != mapping->master_manual)
>  		return -EACCES;
>  
> @@ -1264,8 +1264,13 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
>  		__uvc_find_control(ctrl->entity, mapping->master_id,
>  				   &master_map, &master_ctrl, 0);
>  	if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
> -		s32 val;
> -		int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
> +		s32 val = 0;
> +		int ret;
> +
> +		if (uvc_ctrl_mapping_is_compound(master_map))
> +			return -EINVAL;
> +
> +		ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
>  		if (ret < 0)
>  			return ret;
>  
> @@ -1532,7 +1537,8 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
>  	if (ctrl == NULL)
>  		return;
>  
> -	if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0)
> +	if (uvc_ctrl_mapping_is_compound(mapping) ||
> +	    __uvc_ctrl_get_std(chain, ctrl, mapping, &val) == 0)
>  		changes |= V4L2_EVENT_CTRL_CH_VALUE;
>  
>  	uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
> @@ -1703,7 +1709,8 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
>  		u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
>  		s32 val = 0;
>  
> -		if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
> +		if (uvc_ctrl_mapping_is_compound(mapping) ||
> +		    __uvc_ctrl_get_std(handle->chain, ctrl, mapping, &val) == 0)
>  			changes |= V4L2_EVENT_CTRL_CH_VALUE;
>  
>  		uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
> @@ -1883,7 +1890,10 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
>  	if (ctrl == NULL)
>  		return -EINVAL;
>  
> -	return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
> +	if (uvc_ctrl_mapping_is_compound(mapping))
> +		return -EINVAL;
> +	else
> +		return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
>  }
>  
>  static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
> @@ -2042,8 +2052,12 @@ int uvc_ctrl_set(struct uvc_fh *handle,
>  		       ctrl->info.size);
>  	}
>  
> -	mapping->set(mapping, value,
> -		uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> +	if (uvc_ctrl_mapping_is_compound(mapping))
> +		return -EINVAL;
> +	else
> +		mapping->set(mapping, value,
> +			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));

This seems unrelated to the commit message.

> +
>  
>  	if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
>  		ctrl->handle = handle;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value
  2023-12-01  7:18 ` [PATCH v14 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value Yunke Cao
@ 2023-12-08 15:13   ` Laurent Pinchart
  0 siblings, 0 replies; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-08 15:13 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Fri, Dec 01, 2023 at 04:18:53PM +0900, Yunke Cao wrote:
> Introduce uvc_ctrl_get_boundary(), making it easier to extend to
> support compound controls and V4L2_CTRL_WHICH_MIN/MAX_VAL in the
> following patches.
> 
> For now, it simply returns EINVAL for compound controls and calls
> __query_v4l2_ctrl() for non-compound controls.
> 
> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> An alternative name is uvc_ctrl_get_static(). Let me know if that's better.

The name of the new function is weird in this patch, as it returns the
default value for the control. How about calling it
uvc_ctrl_get_default() here, and renaming it later when you extend it
with an additional argument in patch 08/11 ? All users of the function
are modified in that patch, so renaming the function there won't add
much churn.

> Changelog since v11:
> - No change.
> Changelog since v10:
> - Fix __uvc_ctrl_get_boundary_std() typo causing build error.
> - Added Reviewed-by from Sergey.
> Changelog since v9:
> - Make __uvc_ctrl_get_boundary_std() static.
> Changelog since v8:
> - No Change.
> Changelog since v7:
> - Rename uvc_ctrl_get_fixed() to uvc_ctrl_get_boundary().
> - Move refactor (introduce  __uvc_ctrl_get_boundary_std()) in this patch
>   instead of in the patch where we implement compound control.
> - Fix locking. uvc_ctrl_get_boundary() now acquires ctrl_mutex.
>   __uvc_ctrl_get_boundary_std() calls __uvc_query_v4l2_ctrl() while
>   holding the mutex.
> - Define a uvc_ctrl_mapping_is_compound() for readability.
> 
>  drivers/media/usb/uvc/uvc_ctrl.c | 48 ++++++++++++++++++++++++++++++++
>  drivers/media/usb/uvc/uvc_v4l2.c |  6 +---
>  drivers/media/usb/uvc/uvcvideo.h |  2 ++
>  3 files changed, 51 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index e59a463c2761..8d8333786333 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -884,6 +884,12 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
>  	}
>  }
>  
> +static bool
> +uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
> +{
> +	return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
> +}
> +
>  /* ------------------------------------------------------------------------
>   * Terminal and unit management
>   */
> @@ -1880,6 +1886,48 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
>  	return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
>  }
>  
> +static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
> +				       struct uvc_control *ctrl,
> +				       struct uvc_control_mapping *mapping,
> +				       struct v4l2_ext_control *xctrl)
> +{
> +	struct v4l2_queryctrl qc = { .id = xctrl->id };
> +
> +	int ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &qc);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	xctrl->value = qc.default_value;
> +	return 0;
> +}
> +
> +int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> +			  struct v4l2_ext_control *xctrl)
> +{
> +	struct uvc_control *ctrl;
> +	struct uvc_control_mapping *mapping;
> +	int ret;
> +
> +	if (mutex_lock_interruptible(&chain->ctrl_mutex))
> +		return -ERESTARTSYS;
> +
> +	ctrl = uvc_find_control(chain, xctrl->id, &mapping);
> +	if (!ctrl) {
> +		ret = -EINVAL;
> +		goto done;
> +	}
> +
> +	if (uvc_ctrl_mapping_is_compound(mapping))
> +		ret = -EINVAL;
> +	else
> +		ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
> +
> +done:
> +	mutex_unlock(&chain->ctrl_mutex);
> +	return ret;
> +}
> +
>  int uvc_ctrl_set(struct uvc_fh *handle,
>  	struct v4l2_ext_control *xctrl)
>  {
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index f4988f03640a..5a88847bfbfe 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -1096,15 +1096,11 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
>  
>  	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
>  		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> -			struct v4l2_queryctrl qc = { .id = ctrl->id };
> -
> -			ret = uvc_query_v4l2_ctrl(chain, &qc);
> +			ret = uvc_ctrl_get_boundary(chain, ctrl);
>  			if (ret < 0) {
>  				ctrls->error_idx = i;
>  				return ret;
>  			}
> -
> -			ctrl->value = qc.default_value;
>  		}
>  
>  		return 0;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 6fb0a78b1b00..5091085fcfb0 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -781,6 +781,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
>  }
>  
>  int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
> +int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> +			  struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>  			   const struct v4l2_ext_controls *ctrls,

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-12-01  7:18 ` [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
                     ` (3 preceding siblings ...)
  2023-12-06  5:59   ` kernel test robot
@ 2023-12-08 15:20   ` Laurent Pinchart
  2023-12-13  4:06     ` Yunke Cao
  2023-12-14 17:34   ` kernel test robot
  5 siblings, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-08 15:20 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke and Hans,

Thank you for the patch.

On Fri, Dec 01, 2023 at 04:18:57PM +0900, Yunke Cao wrote:
> From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> 
> Add the capability of retrieving the min and max values of a
> compound control.
> 
> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> Changelog since v13:
> - Updated comments of v4l2_ctrl_new_std_compound()
> Changelog since v12:
> - Addressed comments from Hans.
> Changelog since v11:
> - Added a flag V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
> - Modified std_min/max_compound() to be void function. Moved the check of
>   whether WHICH_MIN/MAX_VAL into prepare_ext_ctrls(), and return EINVAL.
> - Modified documentations to reflect this change.
> Changelog since v10:
> - No change.
> Changelog since v9:
> - No change.
> Changelog since v8:
> - Return ENODATA when min/max is not implemented. Document this behavior.
> - Created a shared helper function __v4l2_ctrl_type_op_init that takes "which"
>   as a parameter. Call it in def, min and max operations.
> Changelog since v7:
> - Document that the definition of the min/max are provided by compound controls
>   are defined in control documentation.
> - Return error, instead of zeroed memory for v4l2_ctrl_ptr_create(NULL).
> 
>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  22 ++-
>  .../media/v4l/vidioc-queryctrl.rst            |   9 +-
>  .../media/videodev2.h.rst.exceptions          |   3 +
>  drivers/media/i2c/imx214.c                    |   5 +-
>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  54 +++++--
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 151 +++++++++++++++---
>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>  include/media/v4l2-ctrls.h                    |  36 ++++-
>  include/uapi/linux/videodev2.h                |   3 +
>  10 files changed, 248 insertions(+), 48 deletions(-)

I'm wondering, would it make sense to include in this series, before
this patch, the following (or a similar) change ?

diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index f4988f03640a..52208f2d71f3 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1094,7 +1094,8 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
 	if (ret < 0)
 		return ret;

-	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
+	switch (ctrls->which) {
+	case V4L2_CTRL_WHICH_DEF_VAL:
 		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
 			struct v4l2_queryctrl qc = { .id = ctrl->id };

@@ -1108,24 +1109,28 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
 		}

 		return 0;
-	}

-	ret = uvc_ctrl_begin(chain);
-	if (ret < 0)
-		return ret;
-
-	for (i = 0; i < ctrls->count; ++ctrl, ++i) {
-		ret = uvc_ctrl_get(chain, ctrl);
-		if (ret < 0) {
-			uvc_ctrl_rollback(handle);
-			ctrls->error_idx = i;
+	case V4L2_CTRL_WHICH_CUR_VAL:
+		ret = uvc_ctrl_begin(chain);
+		if (ret < 0)
 			return ret;
+
+		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+			ret = uvc_ctrl_get(chain, ctrl);
+			if (ret < 0) {
+				uvc_ctrl_rollback(handle);
+				ctrls->error_idx = i;
+				return ret;
+			}
 		}
+
+		ctrls->error_idx = 0;
+
+		return uvc_ctrl_rollback(handle);
+
+	default:
+		return -EINVAL;
 	}
-
-	ctrls->error_idx = 0;
-
-	return uvc_ctrl_rollback(handle);
 }

 static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,

> diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> index 7b1001d11f9c..0b87c23e66ff 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> @@ -330,14 +330,26 @@ still cause this situation.
>        - Which value of the control to get/set/try.
>      * - :cspan:`2` ``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of
>  	the control, ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
> -	value of the control and ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
> -	these controls have to be retrieved from a request or tried/set for
> -	a request. In the latter case the ``request_fd`` field contains the
> +	value of the control, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
> +	value of the control, and ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
> +	value of the control. ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
> +	the control value has to be retrieved from a request or tried/set for
> +	a request. In that case the ``request_fd`` field contains the
>  	file descriptor of the request that should be used. If the device
>  	does not support requests, then ``EACCES`` will be returned.
>  
> -	When using ``V4L2_CTRL_WHICH_DEF_VAL`` be aware that you can only
> -	get the default value of the control, you cannot set or try it.
> +	When using ``V4L2_CTRL_WHICH_DEF_VAL``, ``V4L2_CTRL_WHICH_MIN_VAL``
> +	or ``V4L2_CTRL_WHICH_MAX_VAL`` be aware that you can only get the
> +	default/minimum/maximum value of the control, you cannot set or try it.
> +
> +	Whether a control supports querying the minimum and maximum values using
> +	``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` is indicated
> +	by the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. Most non-compound
> +	control types support this. For controls with compound types, the
> +	definition of minimum/maximum values are provided by
> +	the control documentation. If a compound control does not document the
> +	meaning of minimum/maximum value, then querying the minimum or maximum
> +	value will result in the error code -EINVAL.
>  
>  	For backwards compatibility you can also use a control class here
>  	(see :ref:`ctrl-class`). In that case all controls have to
> diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> index 56d5c8b0b88b..b39f7e27bbbe 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> @@ -447,7 +447,10 @@ See also the examples in :ref:`control`.
>        - n/a
>        - A struct :c:type:`v4l2_rect`, containing a rectangle described by
>  	the position of its top-left corner, the width and the height. Units
> -	depend on the use case.
> +	depend on the use case. Support for ``V4L2_CTRL_WHICH_MIN_VAL`` and
> +	``V4L2_CTRL_WHICH_MAX_VAL`` is optional and depends on the
> +	``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. See the documentation of
> +	the specific control on how to interpret the minimum and maximum values.
>      * - ``V4L2_CTRL_TYPE_H264_SPS``
>        - n/a
>        - n/a
> @@ -664,6 +667,10 @@ See also the examples in :ref:`control`.
>  	``dims[0]``. So setting the control with a differently sized
>  	array will change the ``elems`` field when the control is
>  	queried afterwards.
> +    * - ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
> +      - 0x1000
> +      - This control supports getting minimum and maximum values using
> +      vidioc_g_ext_ctrls with V4L2_CTRL_WHICH_MIN/MAX_VAL.
>  
>  Return Value
>  ============
> diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> index c46082ef0e4d..a417af25e9a4 100644
> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> @@ -393,6 +393,7 @@ replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags
>  replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags
>  replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags
>  replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags
> +replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags
>  
>  replace define V4L2_CTRL_FLAG_NEXT_CTRL control
>  replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control
> @@ -567,6 +568,8 @@ ignore define V4L2_CTRL_DRIVER_PRIV
>  ignore define V4L2_CTRL_MAX_DIMS
>  ignore define V4L2_CTRL_WHICH_CUR_VAL
>  ignore define V4L2_CTRL_WHICH_DEF_VAL
> +ignore define V4L2_CTRL_WHICH_MIN_VAL
> +ignore define V4L2_CTRL_WHICH_MAX_VAL
>  ignore define V4L2_CTRL_WHICH_REQUEST_VAL
>  ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS
>  ignore define V4L2_CID_MAX_CTRLS
> diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
> index 4f77ea02cc27..926b5cae12d8 100644
> --- a/drivers/media/i2c/imx214.c
> +++ b/drivers/media/i2c/imx214.c
> @@ -998,7 +998,10 @@ static int imx214_probe(struct i2c_client *client)
>  	imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls,
>  				NULL,
>  				V4L2_CID_UNIT_CELL_SIZE,
> -				v4l2_ctrl_ptr_create((void *)&unit_size));
> +				v4l2_ctrl_ptr_create((void *)&unit_size),
> +				v4l2_ctrl_ptr_create(NULL),
> +				v4l2_ctrl_ptr_create(NULL));
> +
>  	ret = imx214->ctrls.error;
>  	if (ret) {
>  		dev_err(&client->dev, "%s control init failed (%d)\n",
> diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
> index d9d2a293f3ef..7f370438d655 100644
> --- a/drivers/media/platform/qcom/venus/venc_ctrls.c
> +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
> @@ -607,11 +607,16 @@ int venc_ctrl_init(struct venus_inst *inst)
>  
>  	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
>  				   V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
> -				   v4l2_ctrl_ptr_create(&p_hdr10_cll));
> +				   v4l2_ctrl_ptr_create(&p_hdr10_cll),
> +				   v4l2_ctrl_ptr_create(NULL),
> +				   v4l2_ctrl_ptr_create(NULL));
>  
>  	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
>  				   V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
> -				   v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering));
> +				   v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
> +				   v4l2_ctrl_ptr_create(NULL),
> +				   v4l2_ctrl_ptr_create(NULL));
> +
>  
>  	v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
>  			       V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> index 002ea6588edf..d022e1ed4835 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> @@ -94,6 +94,22 @@ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
>  	return ptr_to_user(c, ctrl, ctrl->p_new);
>  }
>  
> +/* Helper function: copy the minimum control value back to the caller */
> +static int min_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> +{
> +	ctrl->type_ops->minimum(ctrl, 0, ctrl->p_new);
> +
> +	return ptr_to_user(c, ctrl, ctrl->p_new);
> +}
> +
> +/* Helper function: copy the maximum control value back to the caller */
> +static int max_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> +{
> +	ctrl->type_ops->maximum(ctrl, 0, ctrl->p_new);
> +
> +	return ptr_to_user(c, ctrl, ctrl->p_new);
> +}
> +
>  /* Helper function: copy the caller-provider value as the new control value */
>  static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
>  {
> @@ -229,8 +245,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>  		cs->error_idx = i;
>  
>  		if (cs->which &&
> -		    cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
> -		    cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
> +		    (cs->which < V4L2_CTRL_WHICH_DEF_VAL ||
> +		     cs->which > V4L2_CTRL_WHICH_MAX_VAL) &&
>  		    V4L2_CTRL_ID2WHICH(id) != cs->which) {
>  			dprintk(vdev,
>  				"invalid which 0x%x or control id 0x%x\n",
> @@ -259,6 +275,15 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>  			return -EINVAL;
>  		}
>  
> +		if (!(ctrl->flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) &&
> +		    (cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> +		     cs->which == V4L2_CTRL_WHICH_MAX_VAL)) {
> +			dprintk(vdev,
> +				"invalid which 0x%x or control id 0x%x\n",
> +				cs->which, id);
> +			return -EINVAL;
> +		}
> +
>  		if (ctrl->cluster[0]->ncontrols > 1)
>  			have_clusters = true;
>  		if (ctrl->cluster[0] != ctrl)
> @@ -368,8 +393,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>   */
>  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
>  {
> -	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
> -	    which == V4L2_CTRL_WHICH_REQUEST_VAL)
> +	if (which == 0 || (which >= V4L2_CTRL_WHICH_DEF_VAL &&
> +			   which <= V4L2_CTRL_WHICH_MAX_VAL))
>  		return 0;
>  	return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
>  }
> @@ -389,10 +414,12 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>  	struct v4l2_ctrl_helper *helpers = helper;
>  	int ret;
>  	int i, j;
> -	bool is_default, is_request;
> +	bool is_default, is_request, is_min, is_max;
>  
>  	is_default = (cs->which == V4L2_CTRL_WHICH_DEF_VAL);
>  	is_request = (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL);
> +	is_min = (cs->which == V4L2_CTRL_WHICH_MIN_VAL);
> +	is_max = (cs->which == V4L2_CTRL_WHICH_MAX_VAL);
>  
>  	cs->error_idx = cs->count;
>  	cs->which = V4L2_CTRL_ID2WHICH(cs->which);
> @@ -432,13 +459,14 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>  
>  		/*
>  		 * g_volatile_ctrl will update the new control values.
> -		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL and
> +		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL,
> +		 * V4L2_CTRL_WHICH_MIN_VAL, V4L2_CTRL_WHICH_MAX_VAL and
>  		 * V4L2_CTRL_WHICH_REQUEST_VAL. In the case of requests
>  		 * it is v4l2_ctrl_request_complete() that copies the
>  		 * volatile controls at the time of request completion
>  		 * to the request, so you don't want to do that again.
>  		 */
> -		if (!is_default && !is_request &&
> +		if (!is_default && !is_request && !is_min && !is_max &&
>  		    ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
>  		    (master->has_volatiles && !is_cur_manual(master)))) {
>  			for (j = 0; j < master->ncontrols; j++)
> @@ -467,6 +495,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>  				ret = -ENOMEM;
>  			else if (is_request && ref->p_req_valid)
>  				ret = req_to_user(cs->controls + idx, ref);
> +			else if (is_min)
> +				ret = min_to_user(cs->controls + idx, ref->ctrl);
> +			else if (is_max)
> +				ret = max_to_user(cs->controls + idx, ref->ctrl);
>  			else if (is_volatile)
>  				ret = new_to_user(cs->controls + idx, ref->ctrl);
>  			else
> @@ -564,9 +596,11 @@ int try_set_ext_ctrls_common(struct v4l2_fh *fh,
>  
>  	cs->error_idx = cs->count;
>  
> -	/* Default value cannot be changed */
> -	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL) {
> -		dprintk(vdev, "%s: cannot change default value\n",
> +	/* Default/minimum/maximum values cannot be changed */
> +	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL ||
> +	    cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> +	    cs->which == V4L2_CTRL_WHICH_MAX_VAL) {
> +		dprintk(vdev, "%s: cannot change default/min/max value\n",
>  			video_device_node_name(vdev));
>  		return -EINVAL;
>  	}
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> index f1486ab032cf..ef418165e88d 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -182,29 +182,66 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  	}
>  }
>  
> -void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +static void std_min_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> +			    union v4l2_ctrl_ptr ptr)
> +{
> +	void *p = ptr.p + idx * ctrl->elem_size;
> +
> +	if (ctrl->p_min.p_const)
> +		memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
> +	else
> +		memset(p, 0, ctrl->elem_size);
> +}
> +
> +static void std_max_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  			    union v4l2_ctrl_ptr ptr)
> +{
> +	void *p = ptr.p + idx * ctrl->elem_size;
> +
> +	if (ctrl->p_max.p_const)
> +		memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> +	else
> +		memset(p, 0, ctrl->elem_size);
> +}
> +
> +static void __v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +				     u32 which, union v4l2_ctrl_ptr ptr)
>  {
>  	unsigned int i;
>  	u32 tot_elems = ctrl->elems;
>  	u32 elems = tot_elems - from_idx;
> +	s64 value;
>  
> -	if (from_idx >= tot_elems)
> +	switch (which) {
> +	case V4L2_CTRL_WHICH_DEF_VAL:
> +		value = ctrl->default_value;
> +		break;
> +	case V4L2_CTRL_WHICH_MAX_VAL:
> +		value = ctrl->maximum;
> +		break;
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +		value = ctrl->minimum;
> +		break;
> +	default:
>  		return;
> +	}
>  
>  	switch (ctrl->type) {
>  	case V4L2_CTRL_TYPE_STRING:
> +		if (which == V4L2_CTRL_WHICH_DEF_VAL)
> +			value = ctrl->minimum;
> +
>  		for (i = from_idx; i < tot_elems; i++) {
>  			unsigned int offset = i * ctrl->elem_size;
>  
> -			memset(ptr.p_char + offset, ' ', ctrl->minimum);
> -			ptr.p_char[offset + ctrl->minimum] = '\0';
> +			memset(ptr.p_char + offset, ' ', value);
> +			ptr.p_char[offset + value] = '\0';
>  		}
>  		break;
>  	case V4L2_CTRL_TYPE_INTEGER64:
> -		if (ctrl->default_value) {
> +		if (value) {
>  			for (i = from_idx; i < tot_elems; i++)
> -				ptr.p_s64[i] = ctrl->default_value;
> +				ptr.p_s64[i] = value;
>  		} else {
>  			memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
>  		}
> @@ -214,9 +251,9 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
>  	case V4L2_CTRL_TYPE_MENU:
>  	case V4L2_CTRL_TYPE_BITMASK:
>  	case V4L2_CTRL_TYPE_BOOLEAN:
> -		if (ctrl->default_value) {
> +		if (value) {
>  			for (i = from_idx; i < tot_elems; i++)
> -				ptr.p_s32[i] = ctrl->default_value;
> +				ptr.p_s32[i] = value;
>  		} else {
>  			memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
>  		}
> @@ -226,32 +263,61 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
>  		memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
>  		break;
>  	case V4L2_CTRL_TYPE_U8:
> -		memset(ptr.p_u8 + from_idx, ctrl->default_value, elems);
> +		memset(ptr.p_u8 + from_idx, value, elems);
>  		break;
>  	case V4L2_CTRL_TYPE_U16:
> -		if (ctrl->default_value) {
> +		if (value) {
>  			for (i = from_idx; i < tot_elems; i++)
> -				ptr.p_u16[i] = ctrl->default_value;
> +				ptr.p_u16[i] = value;
>  		} else {
>  			memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
>  		}
>  		break;
>  	case V4L2_CTRL_TYPE_U32:
> -		if (ctrl->default_value) {
> +		if (value) {
>  			for (i = from_idx; i < tot_elems; i++)
> -				ptr.p_u32[i] = ctrl->default_value;
> +				ptr.p_u32[i] = value;
>  		} else {
>  			memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
>  		}
>  		break;
>  	default:
> -		for (i = from_idx; i < tot_elems; i++)
> -			std_init_compound(ctrl, i, ptr);
> +		for (i = from_idx; i < tot_elems; i++) {
> +			switch (which) {
> +			case V4L2_CTRL_WHICH_DEF_VAL:
> +				std_init_compound(ctrl, i, ptr);
> +				break;
> +			case V4L2_CTRL_WHICH_MAX_VAL:
> +				std_max_compound(ctrl, i, ptr);
> +				break;
> +			case V4L2_CTRL_WHICH_MIN_VAL:
> +				std_min_compound(ctrl, i, ptr);
> +				break;
> +			}
> +		}
>  		break;
>  	}
>  }
> +
> +void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +			    union v4l2_ctrl_ptr ptr)
> +{
> +	__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_DEF_VAL, ptr);
> +}
>  EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
>  
> +void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +			       union v4l2_ctrl_ptr ptr)
> +{
> +	__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
> +}
> +
> +void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +			       union v4l2_ctrl_ptr ptr)
> +{
> +	__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
> +}
> +
>  void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
>  {
>  	union v4l2_ctrl_ptr ptr = ctrl->p_cur;
> @@ -1293,6 +1359,8 @@ EXPORT_SYMBOL(v4l2_ctrl_type_op_validate);
>  static const struct v4l2_ctrl_type_ops std_type_ops = {
>  	.equal = v4l2_ctrl_type_op_equal,
>  	.init = v4l2_ctrl_type_op_init,
> +	.minimum = v4l2_ctrl_type_op_minimum,
> +	.maximum = v4l2_ctrl_type_op_maximum,
>  	.log = v4l2_ctrl_type_op_log,
>  	.validate = v4l2_ctrl_type_op_validate,
>  };
> @@ -1764,7 +1832,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  			s64 min, s64 max, u64 step, s64 def,
>  			const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
>  			u32 flags, const char * const *qmenu,
> -			const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def,
> +			const s64 *qmenu_int,
> +			const union v4l2_ctrl_ptr p_def,
> +			const union v4l2_ctrl_ptr p_min,
> +			const union v4l2_ctrl_ptr p_max,
>  			void *priv)
>  {
>  	struct v4l2_ctrl *ctrl;
> @@ -1888,6 +1959,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		break;
>  	}
>  
> +	if (type < V4L2_CTRL_COMPOUND_TYPES &&
> +	    type != V4L2_CTRL_TYPE_BUTTON &&
> +	    type != V4L2_CTRL_TYPE_CTRL_CLASS &&
> +	    type != V4L2_CTRL_TYPE_STRING)
> +		flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
> +
>  	/* Sanity checks */
>  	if (id == 0 || name == NULL || !elem_size ||
>  	    id >= V4L2_CID_PRIVATE_BASE ||
> @@ -1896,6 +1973,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		handler_set_err(hdl, -ERANGE);
>  		return NULL;
>  	}
> +
>  	err = check_range(type, min, max, step, def);
>  	if (err) {
>  		handler_set_err(hdl, err);
> @@ -1937,6 +2015,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  
>  	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
>  		sz_extra += elem_size;
> +	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
> +		sz_extra += elem_size;
> +	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_max.p_const)
> +		sz_extra += elem_size;
>  
>  	ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
>  	if (ctrl == NULL) {
> @@ -2002,6 +2084,22 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
>  	}
>  
> +	if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) {
> +		void *ptr = ctrl->p_def.p;
> +
> +		if (p_min.p_const) {
> +			ptr += elem_size;
> +			ctrl->p_min.p = ptr;
> +			memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> +		}
> +
> +		if (p_max.p_const) {
> +			ptr += elem_size;
> +			ctrl->p_max.p = ptr;
> +			memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> +		}
> +	}
> +
>  	ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
>  	cur_to_new(ctrl);
>  
> @@ -2052,7 +2150,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
>  			type, min, max,
>  			is_menu ? cfg->menu_skip_mask : step, def,
>  			cfg->dims, cfg->elem_size,
> -			flags, qmenu, qmenu_int, cfg->p_def, priv);
> +			flags, qmenu, qmenu_int, cfg->p_def, cfg->p_min,
> +			cfg->p_max, priv);
>  	if (ctrl)
>  		ctrl->is_private = cfg->is_private;
>  	return ctrl;
> @@ -2077,7 +2176,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     min, max, step, def, NULL, 0,
> -			     flags, NULL, NULL, ptr_null, NULL);
> +			     flags, NULL, NULL, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std);
>  
> @@ -2110,7 +2210,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     0, max, mask, def, NULL, 0,
> -			     flags, qmenu, qmenu_int, ptr_null, NULL);
> +			     flags, qmenu, qmenu_int, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
>  
> @@ -2142,7 +2243,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     0, max, mask, def, NULL, 0,
> -			     flags, qmenu, NULL, ptr_null, NULL);
> +			     flags, qmenu, NULL, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
> @@ -2150,7 +2252,9 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
>  /* Helper function for standard compound controls */
>  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>  				const struct v4l2_ctrl_ops *ops, u32 id,
> -				const union v4l2_ctrl_ptr p_def)
> +				const union v4l2_ctrl_ptr p_def,
> +				const union v4l2_ctrl_ptr p_min,
> +				const union v4l2_ctrl_ptr p_max)
>  {
>  	const char *name;
>  	enum v4l2_ctrl_type type;
> @@ -2164,7 +2268,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     min, max, step, def, NULL, 0,
> -			     flags, NULL, NULL, p_def, NULL);
> +			     flags, NULL, NULL, p_def, p_min, p_max, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std_compound);
>  
> @@ -2188,7 +2292,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     0, max, 0, def, NULL, 0,
> -			     flags, NULL, qmenu_int, ptr_null, NULL);
> +			     flags, NULL, qmenu_int, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
>  
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 9b1de54ce379..db5bd765db3c 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -884,7 +884,9 @@ static bool check_ext_ctrls(struct v4l2_ext_controls *c, unsigned long ioctl)
>  			return false;
>  		break;
>  	case V4L2_CTRL_WHICH_DEF_VAL:
> -		/* Default value cannot be changed */
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +	case V4L2_CTRL_WHICH_MAX_VAL:
> +		/* Default, minimum or maximum value cannot be changed */
>  		if (ioctl == VIDIOC_S_EXT_CTRLS ||
>  		    ioctl == VIDIOC_TRY_EXT_CTRLS) {
>  			c->error_idx = c->count;
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index b0db167a3ac4..9ed7be1e696f 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -133,6 +133,8 @@ struct v4l2_ctrl_ops {
>   *
>   * @equal: return true if all ctrl->elems array elements are equal.
>   * @init: initialize the value for array elements from from_idx to ctrl->elems.
> + * @minimum: set the value to the minimum value of the control.
> + * @maximum: set the value to the maximum value of the control.
>   * @log: log the value.
>   * @validate: validate the value for ctrl->new_elems array elements.
>   *	Return 0 on success and a negative value otherwise.
> @@ -142,6 +144,10 @@ struct v4l2_ctrl_type_ops {
>  		      union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2);
>  	void (*init)(const struct v4l2_ctrl *ctrl, u32 from_idx,
>  		     union v4l2_ctrl_ptr ptr);
> +	void (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
> +			union v4l2_ctrl_ptr ptr);
> +	void (*maximum)(const struct v4l2_ctrl *ctrl, u32 idx,
> +			union v4l2_ctrl_ptr ptr);
>  	void (*log)(const struct v4l2_ctrl *ctrl);
>  	int (*validate)(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr ptr);
>  };
> @@ -247,6 +253,12 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
>   * @p_def:	The control's default value represented via a union which
>   *		provides a standard way of accessing control types
>   *		through a pointer (for compound controls only).
> + * @p_min:	The control's minimum value represented via a union which
> + *		provides a standard way of accessing control types
> + *		through a pointer (for compound controls only).
> + * @p_max:	The control's maximum value represented via a union which
> + *		provides a standard way of accessing control types
> + *		through a pointer (for compound controls only).
>   * @p_cur:	The control's current value represented via a union which
>   *		provides a standard way of accessing control types
>   *		through a pointer.
> @@ -306,6 +318,8 @@ struct v4l2_ctrl {
>  	} cur;
>  
>  	union v4l2_ctrl_ptr p_def;
> +	union v4l2_ctrl_ptr p_min;
> +	union v4l2_ctrl_ptr p_max;
>  	union v4l2_ctrl_ptr p_new;
>  	union v4l2_ctrl_ptr p_cur;
>  };
> @@ -425,6 +439,8 @@ struct v4l2_ctrl_handler {
>   * @step:	The control's step value for non-menu controls.
>   * @def:	The control's default value.
>   * @p_def:	The control's default value for compound controls.
> + * @p_min:	The control's minimum value for compound controls.
> + * @p_max:	The control's maximum value for compound controls.
>   * @dims:	The size of each dimension.
>   * @elem_size:	The size in bytes of the control.
>   * @flags:	The control's flags.
> @@ -454,6 +470,8 @@ struct v4l2_ctrl_config {
>  	u64 step;
>  	s64 def;
>  	union v4l2_ctrl_ptr p_def;
> +	union v4l2_ctrl_ptr p_min;
> +	union v4l2_ctrl_ptr p_max;
>  	u32 dims[V4L2_CTRL_MAX_DIMS];
>  	u32 elem_size;
>  	u32 flags;
> @@ -723,17 +741,25 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
>   * @ops:       The control ops.
>   * @id:        The control ID.
>   * @p_def:     The control's default value.
> + * @p_min:     The control's minimum value.
> + * @p_max:     The control's maximum value.
>   *
> - * Sames as v4l2_ctrl_new_std(), but with support to compound controls, thanks
> - * to the @p_def field. Use v4l2_ctrl_ptr_create() to create @p_def from a
> - * pointer. Use v4l2_ctrl_ptr_create(NULL) if the default value of the
> - * compound control should be all zeroes.
> + * Same as v4l2_ctrl_new_std(), but with support for compound controls.
> + * To fill in the @p_def, @p_min and @p_max fields, use v4l2_ctrl_ptr_create()
> + * to convert a pointer to a const union v4l2_ctrl_ptr.
> + * Use v4l2_ctrl_ptr_create(NULL) if you want the default, minimum or maximum
> + * value of the compound control to be all zeroes.
> + * If the compound control does not set the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
> + * flag, then it does not has minimum and maximum values. In that case just use
> + * v4l2_ctrl_ptr_create(NULL) for the @p_min and @p_max arguments.
>   *
>   */
>  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>  					     const struct v4l2_ctrl_ops *ops,
>  					     u32 id,
> -					     const union v4l2_ctrl_ptr p_def);
> +					     const union v4l2_ctrl_ptr p_def,
> +					     const union v4l2_ctrl_ptr p_min,
> +					     const union v4l2_ctrl_ptr p_max);
>  
>  /**
>   * v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control.
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 82d86abcf89c..8fdeb5188af5 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1866,6 +1866,8 @@ struct v4l2_ext_controls {
>  #define V4L2_CTRL_WHICH_CUR_VAL   0
>  #define V4L2_CTRL_WHICH_DEF_VAL   0x0f000000
>  #define V4L2_CTRL_WHICH_REQUEST_VAL 0x0f010000
> +#define V4L2_CTRL_WHICH_MIN_VAL   0x0f020000
> +#define V4L2_CTRL_WHICH_MAX_VAL   0x0f030000
>  
>  enum v4l2_ctrl_type {
>  	V4L2_CTRL_TYPE_INTEGER	     = 1,
> @@ -1973,6 +1975,7 @@ struct v4l2_querymenu {
>  #define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE	0x0200
>  #define V4L2_CTRL_FLAG_MODIFY_LAYOUT	0x0400
>  #define V4L2_CTRL_FLAG_DYNAMIC_ARRAY	0x0800
> +#define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
>  
>  /*  Query flags, to be ORed with the control ID */
>  #define V4L2_CTRL_FLAG_NEXT_CTRL	0x80000000

-- 
Regards,

Laurent Pinchart

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-12-01  7:18 ` [PATCH v14 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2023-12-08 15:26   ` Laurent Pinchart
  0 siblings, 0 replies; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-08 15:26 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Fri, Dec 01, 2023 at 04:18:59PM +0900, Yunke Cao wrote:
> Add support for V4L2_CTRL_WHICH_MIN/MAX_VAL in uvc driver.
> It is useful for the V4L2_CID_UVC_REGION_OF_INTEREST_RECT control.
> 
> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> Changelog since v11:
> - No change.
> Changelog since v10:
> - Added Reviewed-by from Sergey.
> Changelog since v9:
> - Revert a change in v7 that causes v4l2-compliance failure:
> - In uvc_ioctl_g_ext_ctrls(), when v4l2_which is not V4L2_CTRL_WHICH_*_VAL,
> - treat it the same as cur instead of returning EINVAL. This is the existing
> - behavior.
> - The change in v7 of returning EINVAL fails the check in
> - v4l2-compliance/v4l2-test-controls.cpp#L834.
> Changelog since v8:
> - No change.
> Changelog since v7:
> - Address some comments.
> 
>  drivers/media/usb/uvc/uvc_ctrl.c | 64 +++++++++++++++++++++++++++-----
>  drivers/media/usb/uvc/uvc_v4l2.c |  7 +++-
>  drivers/media/usb/uvc/uvcvideo.h |  3 +-
>  3 files changed, 61 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index aae2480496b7..c073c2a02102 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1247,11 +1247,18 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>  	if (!ctrl)
>  		return -EINVAL;
>  
> -	if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) && read)
> -		return -EACCES;
> -
> -	if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
> -		return -EACCES;
> +	if (read) {
> +		if (ctrls->which == V4L2_CTRL_WHICH_MIN_VAL ||
> +		    ctrls->which == V4L2_CTRL_WHICH_MAX_VAL) {
> +			if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) ||
> +			    !(ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX))
> +				return -EINVAL;

Shouldn't this be

		if (ctrls->which == V4L2_CTRL_WHICH_MIN_VAL &&
		    !(ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN))
			return -EINVAL;

		if (ctrls->which == V4L2_CTRL_WHICH_MAX_VAL &&
		    !(ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX))
			return -EINVAL;

> +		} else if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
> +			return -EACCES;

		if (ctrls->which == V4L2_CTRL_WHICH_CUR_VAL &&
		    !(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
			return -EACCES;

> +	} else {
> +		if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
> +			return -EACCES;
> +	}
>  
>  	if (ioctl != VIDIOC_S_EXT_CTRLS || !mapping->master_id)
>  		return 0;
> @@ -1332,6 +1339,9 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
>  		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
>  	if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
>  		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +	if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) &&
> +	    (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN))
> +		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
>  
>  	if (mapping->master_id)
>  		__uvc_find_control(ctrl->entity, mapping->master_id,
> @@ -1981,6 +1991,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
>  static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
>  				       struct uvc_control *ctrl,
>  				       struct uvc_control_mapping *mapping,
> +				       u32 v4l2_which,
>  				       struct v4l2_ext_control *xctrl)
>  {
>  	struct v4l2_queryctrl qc = { .id = xctrl->id };
> @@ -1990,28 +2001,59 @@ static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
>  	if (ret < 0)
>  		return ret;
>  
> -	xctrl->value = qc.default_value;
> +	switch (v4l2_which) {
> +	case V4L2_CTRL_WHICH_DEF_VAL:
> +		xctrl->value = qc.default_value;
> +		break;
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +		xctrl->value = qc.minimum;
> +		break;
> +	case V4L2_CTRL_WHICH_MAX_VAL:
> +		xctrl->value = qc.maximum;
> +		break;
> +	}
> +
>  	return 0;
>  }
>  
>  static int __uvc_ctrl_get_boundary_compound(struct uvc_video_chain *chain,
>  					    struct uvc_control *ctrl,
>  					    struct uvc_control_mapping *mapping,
> +					    u32 v4l2_which,
>  					    struct v4l2_ext_control *xctrl)
>  {
> +	u32 flag, id;
>  	int ret;
>  
> +	switch (v4l2_which) {
> +	case V4L2_CTRL_WHICH_DEF_VAL:
> +		flag = UVC_CTRL_FLAG_GET_DEF;
> +		id = UVC_CTRL_DATA_DEF;
> +		break;
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +		flag = UVC_CTRL_FLAG_GET_MIN;
> +		id = UVC_CTRL_DATA_MIN;
> +		break;
> +	case V4L2_CTRL_WHICH_MAX_VAL:
> +		flag = UVC_CTRL_FLAG_GET_MAX;
> +		id = UVC_CTRL_DATA_MAX;
> +		break;
> +	}
> +
> +	if (!(ctrl->info.flags & flag) && flag != UVC_CTRL_FLAG_GET_DEF)
> +		return -EINVAL;
> +
>  	if (!ctrl->cached) {
>  		ret = uvc_ctrl_populate_cache(chain, ctrl);
>  		if (ret < 0)
>  			return ret;
>  	}
>  
> -	return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_DEF, xctrl);
> +	return __uvc_ctrl_get_compound(mapping, ctrl, id, xctrl);
>  }
>  
>  int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> -			  struct v4l2_ext_control *xctrl)
> +			  struct v4l2_ext_control *xctrl, u32 v4l2_which)
>  {
>  	struct uvc_control *ctrl;
>  	struct uvc_control_mapping *mapping;
> @@ -2028,9 +2070,11 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
>  
>  	if (uvc_ctrl_mapping_is_compound(mapping))
>  		ret = __uvc_ctrl_get_boundary_compound(chain, ctrl, mapping,
> -						       xctrl);
> +						       v4l2_which, xctrl);
>  	else
> -		ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
> +		ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping,
> +						  v4l2_which, xctrl);
> +
>  
>  done:
>  	mutex_unlock(&chain->ctrl_mutex);
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index ff72caf7bc9e..352f62ef02f2 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -1094,9 +1094,12 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
>  	if (ret < 0)
>  		return ret;
>  
> -	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
> +	switch (ctrls->which) {
> +	case V4L2_CTRL_WHICH_DEF_VAL:
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +	case V4L2_CTRL_WHICH_MAX_VAL:
>  		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> -			ret = uvc_ctrl_get_boundary(chain, ctrl);
> +			ret = uvc_ctrl_get_boundary(chain, ctrl, ctrls->which);
>  			if (ret < 0) {
>  				ctrls->error_idx = i;
>  				return ret;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 11805b729c22..6fda40919e7f 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -790,7 +790,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
>  
>  int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> -			  struct v4l2_ext_control *xctrl);
> +			  struct v4l2_ext_control *xctrl,
> +			  u32 v4l2_which);
>  int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>  			   const struct v4l2_ext_controls *ctrls,

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 09/11] media: uvcvideo: implement UVC v1.5 ROI
  2023-12-01  7:19 ` [PATCH v14 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
@ 2023-12-08 15:43   ` Laurent Pinchart
  2024-03-14 13:24   ` Gergo Koteles
  1 sibling, 0 replies; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-08 15:43 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Fri, Dec 01, 2023 at 04:19:00PM +0900, Yunke Cao wrote:
> 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>
> ---
> Changelog since v11:
> - No change.
> Changelog since v10:
> - Moved after the patches adding support for MIN/MAX.
> - Clamp the set current value based on MIN/MAX.
> - Thanks, Daniel!
> Changelog since v9:
> - No change.
> Changelog since v8:
> - No change.
> Changelog since v7:
> - Fix a few style issues.
> - Only allow 4-byte aligned data.
> - Add control names.
> - Move initialization to 7/10.
> 
>  drivers/media/usb/uvc/uvc_ctrl.c   | 137 +++++++++++++++++++++++++++--
>  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, 167 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index c073c2a02102..d405b2a9d477 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[] = {
> @@ -459,6 +477,70 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
>  	data[first+1] = min_t(int, abs(value), 0xff);
>  }
>  
> +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;

This can only happen when the device is buggy. -EINVAL is not a great
error code in that case, as it usually indicates that a parameter passed
by the application is invalid.

> +
> +	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,
> @@ -748,6 +830,29 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
>  	},
> +	{
> +		.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",
> +	},
>  };
>  
>  const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
> @@ -900,12 +1005,12 @@ 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;
>  }
> -
>  static bool
>  uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
>  {
> @@ -2100,6 +2205,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);
> @@ -2224,6 +2331,13 @@ int uvc_ctrl_set(struct uvc_fh *handle,
>  	}
>  
>  	if (uvc_ctrl_mapping_is_compound(mapping)) {
> +		/* 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;
> @@ -2641,12 +2755,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)
> -			return -EINVAL;
> +		switch (map->v4l2_type) {
> +		case V4L2_CTRL_TYPE_RECT:
> +			/* Only supports 4 bytes-aligned data. */
> +			if (WARN_ON(map->offset % 32))
> +				return -EINVAL;

Why is there no need to check the size here ?

> +			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 352f62ef02f2..c0a7c0091099 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 6fda40919e7f..18da5e0b8cb2 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -133,8 +133,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);

I think this belongs to a separate patch.

>  };
>  
>  struct uvc_control {
> @@ -289,6 +290,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 2ff0e8a3a683..2afb4420e6c4 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 f86185456dc5..9d1e6085feba 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

Should this type be rejected in uvc_control_add_xu_mapping() ?

>  
>  /* 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 68db66d4aae8..6c7e03d18d79 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -1081,6 +1081,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)

All driver-specific controls are in the user class, is there a reason to
create driver-specific controls in the camera class here ?

> +
>  /* FM Modulator class control IDs */
>  
>  #define V4L2_CID_FM_TX_CLASS_BASE		(V4L2_CTRL_CLASS_FM_TX | 0x900)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 10/11] media: uvcvideo: initilaize ROI control to default value
  2023-12-01  7:19 ` [PATCH v14 10/11] media: uvcvideo: initilaize ROI control to default value Yunke Cao
@ 2023-12-08 15:50   ` Laurent Pinchart
  0 siblings, 0 replies; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-08 15:50 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

In the commit message, s/initilaize/Initialize/

On Fri, Dec 01, 2023 at 04:19:01PM +0900, Yunke Cao wrote:
> Add an init function to uvc_control_info. Use the function to
> initialize ROI control to default value.
> 
> Also moves utility functions to the top of uvc_ctrl.c, above
> the uvc_ctrls definition. uvc_ctrl_init_roi() calls uvc_ctrl_data()
> and need to be declared before uvc_ctrls.

Please move functions in a separate patch that does not change anything
else. It's otherwise difficult to review the changes.

> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> Changelog since v8:
> - No change.
> Changelog since v7:
> - Newly added patch. Split initializing from the previous patch.
> - Add an init operation to uvc_control_info and use it for ROI
>   initialization.
>   
>  drivers/media/usb/uvc/uvc_ctrl.c | 273 ++++++++++++++++++-------------
>  drivers/media/usb/uvc/uvcvideo.h |   3 +
>  2 files changed, 160 insertions(+), 116 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index d405b2a9d477..bda625c392c2 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -32,6 +32,158 @@
>  #define UVC_CTRL_DATA_DEF	5
>  #define UVC_CTRL_DATA_LAST	6
>  
> +/* ------------------------------------------------------------------------
> + * Utility functions
> + */
> +
> +static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
> +{
> +	return ctrl->uvc_data + id * ctrl->info.size;
> +}
> +
> +static inline int uvc_test_bit(const u8 *data, int bit)
> +{
> +	return (data[bit >> 3] >> (bit & 7)) & 1;
> +}
> +
> +static inline void uvc_clear_bit(u8 *data, int bit)
> +{
> +	data[bit >> 3] &= ~(1 << (bit & 7));
> +}
> +
> +/*
> + * Extract the bit string specified by mapping->offset and mapping->data_size
> + * from the little-endian data stored at 'data' and return the result as
> + * a signed 32bit integer. Sign extension will be performed if the mapping
> + * references a signed data type.
> + */
> +static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
> +			    u8 query, const u8 *data)
> +{
> +	int bits = mapping->data_size;
> +	int offset = mapping->offset;
> +	s32 value = 0;
> +	u8 mask;
> +
> +	data += offset / 8;
> +	offset &= 7;
> +	mask = ((1LL << bits) - 1) << offset;
> +
> +	while (1) {
> +		u8 byte = *data & mask;
> +
> +		value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
> +		bits -= 8 - (offset > 0 ? offset : 0);
> +		if (bits <= 0)
> +			break;
> +
> +		offset -= 8;
> +		mask = (1 << bits) - 1;
> +		data++;
> +	}
> +
> +	/* Sign-extend the value if needed. */
> +	if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
> +		value |= -(value & (1 << (mapping->data_size - 1)));
> +
> +	return value;
> +}
> +
> +/*
> + * Set the bit string specified by mapping->offset and mapping->data_size
> + * in the little-endian data stored at 'data' to the value 'value'.
> + */
> +static void uvc_set_le_value(struct uvc_control_mapping *mapping,
> +			     s32 value, u8 *data)
> +{
> +	int bits = mapping->data_size;
> +	int offset = mapping->offset;
> +	u8 mask;
> +
> +	/*
> +	 * According to the v4l2 spec, writing any value to a button control
> +	 * should result in the action belonging to the button control being
> +	 * triggered. UVC devices however want to see a 1 written -> override
> +	 * value.
> +	 */
> +	if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
> +		value = -1;
> +
> +	data += offset / 8;
> +	offset &= 7;
> +
> +	for (; bits > 0; data++) {
> +		mask = ((1LL << bits) - 1) << offset;
> +		*data = (*data & ~mask) | ((value << offset) & mask);
> +		value >>= offset ? offset : 8;
> +		bits -= 8 - offset;
> +		offset = 0;
> +	}
> +}
> +
> +/*
> + * Extract the byte array specified by mapping->offset and mapping->data_size
> + * stored at 'data' to the output array 'data_out'.
> + */
> +static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
> +			    u8 *data_out)
> +{
> +	memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
> +	return 0;
> +}
> +
> +/*
> + * Copy the byte array 'data_in' to the destination specified by mapping->offset
> + * and mapping->data_size stored at 'data'.
> + */
> +static int uvc_set_compound(struct uvc_control_mapping *mapping,
> +			    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;
> +}
> +
> +static bool
> +uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
> +{
> +	return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
> +}
> +
> +static int uvc_ctrl_init_roi(struct uvc_device *dev, struct uvc_control *ctrl)
> +{
> +	int ret;
> +
> +	ret = uvc_query_ctrl(dev, UVC_GET_DEF, ctrl->entity->id, dev->intfnum,
> +			     UVC_CT_REGION_OF_INTEREST_CONTROL,
> +			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> +			     ctrl->info.size);
> +	if (ret)
> +		goto out;
> +
> +	/*
> +	 * Most firmwares have wrong GET_CUR configuration. E.g. it's
> +	 * below GET_MIN, or have rectangle coordinates mixed up. This

Seriously :'-(

> +	 * causes problems sometimes, because we are unable to set
> +	 * auto-controls value without first setting ROI rectangle to
> +	 * valid configuration.
> +	 *
> +	 * We expect that default configuration is always correct and
> +	 * is within the GET_MIN / GET_MAX boundaries.
> +	 *
> +	 * Set current ROI configuration to GET_DEF, so that we will
> +	 * always have properly configured ROI.
> +	 */

You can reflow the text to 80 columns.

> +	ret = uvc_query_ctrl(dev, UVC_SET_CUR, 1, dev->intfnum,

Hardcoding the entity ID to 1 doesn't seem right.

> +			     UVC_CT_REGION_OF_INTEREST_CONTROL,
> +			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> +			     ctrl->info.size);
> +out:
> +	if (ret)
> +		dev_err(&dev->udev->dev, "Failed to fixup ROI (%d).\n", ret);
> +	return ret;
> +}
> +
>  /* ------------------------------------------------------------------------
>   * Controls
>   */
> @@ -375,6 +527,7 @@ static const struct uvc_control_info uvc_ctrls[] = {
>  				| UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
>  				| UVC_CTRL_FLAG_GET_DEF
>  				| UVC_CTRL_FLAG_AUTO_UPDATE,
> +		.init		= uvc_ctrl_init_roi,
>  	},
>  };
>  
> @@ -901,122 +1054,6 @@ static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = {
>  	NULL, /* Sentinel */
>  };
>  
> -/* ------------------------------------------------------------------------
> - * Utility functions
> - */
> -
> -static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
> -{
> -	return ctrl->uvc_data + id * ctrl->info.size;
> -}
> -
> -static inline int uvc_test_bit(const u8 *data, int bit)
> -{
> -	return (data[bit >> 3] >> (bit & 7)) & 1;
> -}
> -
> -static inline void uvc_clear_bit(u8 *data, int bit)
> -{
> -	data[bit >> 3] &= ~(1 << (bit & 7));
> -}
> -
> -/*
> - * Extract the bit string specified by mapping->offset and mapping->data_size
> - * from the little-endian data stored at 'data' and return the result as
> - * a signed 32bit integer. Sign extension will be performed if the mapping
> - * references a signed data type.
> - */
> -static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
> -	u8 query, const u8 *data)
> -{
> -	int bits = mapping->data_size;
> -	int offset = mapping->offset;
> -	s32 value = 0;
> -	u8 mask;
> -
> -	data += offset / 8;
> -	offset &= 7;
> -	mask = ((1LL << bits) - 1) << offset;
> -
> -	while (1) {
> -		u8 byte = *data & mask;
> -		value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
> -		bits -= 8 - (offset > 0 ? offset : 0);
> -		if (bits <= 0)
> -			break;
> -
> -		offset -= 8;
> -		mask = (1 << bits) - 1;
> -		data++;
> -	}
> -
> -	/* Sign-extend the value if needed. */
> -	if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
> -		value |= -(value & (1 << (mapping->data_size - 1)));
> -
> -	return value;
> -}
> -
> -/*
> - * Set the bit string specified by mapping->offset and mapping->data_size
> - * in the little-endian data stored at 'data' to the value 'value'.
> - */
> -static void uvc_set_le_value(struct uvc_control_mapping *mapping,
> -	s32 value, u8 *data)
> -{
> -	int bits = mapping->data_size;
> -	int offset = mapping->offset;
> -	u8 mask;
> -
> -	/*
> -	 * According to the v4l2 spec, writing any value to a button control
> -	 * should result in the action belonging to the button control being
> -	 * triggered. UVC devices however want to see a 1 written -> override
> -	 * value.
> -	 */
> -	if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
> -		value = -1;
> -
> -	data += offset / 8;
> -	offset &= 7;
> -
> -	for (; bits > 0; data++) {
> -		mask = ((1LL << bits) - 1) << offset;
> -		*data = (*data & ~mask) | ((value << offset) & mask);
> -		value >>= offset ? offset : 8;
> -		bits -= 8 - offset;
> -		offset = 0;
> -	}
> -}
> -
> -/*
> - * Extract the byte array specified by mapping->offset and mapping->data_size
> - * stored at 'data' to the output array 'data_out'.
> - */
> -static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
> -			    u8 *data_out)
> -{
> -	memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
> -	return 0;
> -}
> -
> -/*
> - * Copy the byte array 'data_in' to the destination specified by mapping->offset
> - * and mapping->data_size stored at 'data'.
> - */
> -static int uvc_set_compound(struct uvc_control_mapping *mapping,
> -			    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;
> -}

Looks like there was a missing blank line in a previous patch in the
series.

> -static bool
> -uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
> -{
> -	return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
> -}
> -
>  /* ------------------------------------------------------------------------
>   * Terminal and unit management
>   */
> @@ -2984,6 +3021,10 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
>  			 * GET_INFO on standard controls.
>  			 */
>  			uvc_ctrl_get_flags(chain->dev, ctrl, &ctrl->info);
> +
> +			if (info->init)
> +				info->init(chain->dev, ctrl);
> +
>  			break;
>  		 }
>  	}
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 18da5e0b8cb2..335b565da6de 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -85,6 +85,7 @@
>  struct gpio_desc;
>  struct sg_table;
>  struct uvc_device;
> +struct uvc_control;

Alphabetical order please.

>  
>  /*
>   * TODO: Put the most frequently accessed fields at the beginning of
> @@ -99,6 +100,8 @@ struct uvc_control_info {
>  
>  	u16 size;
>  	u32 flags;
> +
> +	int (*init)(struct uvc_device *dev, struct uvc_control *ctrl);
>  };
>  
>  struct uvc_control_mapping {

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 11/11] media: uvcvideo: document UVC v1.5 ROI
  2023-12-01  7:19 ` [PATCH v14 11/11] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
@ 2023-12-08 16:00   ` Laurent Pinchart
  2023-12-12  4:45     ` Yunke Cao
  0 siblings, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-08 16:00 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Fri, Dec 01, 2023 at 04:19:02PM +0900, Yunke Cao wrote:
> Added documentation of V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
> V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.

It would be useful to point here to the userspace code that uses those
controls. A link to the appropriate Chrome OS git tree would be useful,
to showcase a real user.

> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> Changelog since v11:
> - No change.
> Changelog since v10:
> - Added Reviewed-by from Sergey.
> Changelog since v9:
> - No change.
> Changelog since v8:
> - No change.
> Changelog since v7:
> - Fix documentation for automatic exposure based on comment in v7.
> 
>  .../userspace-api/media/drivers/uvcvideo.rst  | 62 +++++++++++++++++++
>  1 file changed, 62 insertions(+)
> 
> diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> index aab4304e6bb5..3dc062221f8b 100644
> --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
> +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> @@ -181,6 +181,7 @@ Argument: struct uvc_xu_control_mapping
>  	UVC_CTRL_DATA_TYPE_BOOLEAN	Boolean
>  	UVC_CTRL_DATA_TYPE_ENUM		Enumeration
>  	UVC_CTRL_DATA_TYPE_BITMASK	Bitmask
> +	UVC_CTRL_DATA_TYPE_RECT		Rectangular area
>  
>  
>  UVCIOC_CTRL_QUERY - Query a UVC XU control
> @@ -255,3 +256,64 @@ Argument: struct uvc_xu_control_query
>  	__u8	query		Request code to send to the device
>  	__u16	size		Control data size (in bytes)
>  	__u8	*data		Control value
> +
> +
> +Driver-specific V4L2 controls
> +-----------------------------
> +
> +The uvcvideo driver implements the following UVC-specific controls:
> +
> +``V4L2_CID_UVC_REGION_OF_INTEREST_RECT (struct)``
> +	This control determines the region of interest (ROI). ROI is a
> +	rectangular area represented by a struct :c:type:`v4l2_rect`. The
> +	rectangle is in global sensor coordinates and pixel units. It is
> +	independent of the field of view, not impacted by any cropping or
> +	scaling.
> +
> +	Use ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` to query
> +	the range of rectangle sizes. The left/top coordinates of a minimum or
> +	maximum rectangle are always 0. For example, a device can have a minimum
> +	ROI rectangle of 1x1@0x0 and a maximum of 640x480@0x0.

Is that actually true ? The UVC 1.5 specification states

    GET_MAX shall return the current Window as specified by
    CT_DIGITAL_WINDOW_CONTROL.

And the window can have non-zero left and right coordinates.

> +
> +	Setting a ROI allows the camera to optimize the capture for the region.
> +	The value of ``V4L2_CID_REGION_OF_INTEREST_AUTO`` control determines
> +	the detailed behavior.
> +
> +
> +``V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (bitmask)``
> +	This determines which, if any, on board features should track to the

s/on board/on-board/

> +	Region of Interest specified by the current value of
> +	``V4L2_CID_UVD__REGION_OF_INTEREST_RECT``.
> +
> +	Max value is a mask indicating all supported Auto Controls.
> +
> +.. flat-table::
> +    :header-rows:  0
> +    :stub-columns: 0
> +
> +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE``
> +      - Setting this to true causes automatic exposure to track the region of

Maybe "Setting this bit causes..." as those values are bit flags, not
booleans. Same below.

> +	interest instead of the whole image.
> +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS``
> +      - Setting this to true causes automatic iris to track the region of
> +	interest instead of the whole image.
> +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE``
> +      - Setting this to true causes automatic white balance to track the region
> +	of interest instead of the whole image.
> +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS``
> +      - Setting this to true causes automatic focus adjustment to track the
> +	region of interest instead of the whole image.
> +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT``
> +      - Setting this to true causes automatic face detection to track the
> +	region of interest instead of the whole image.
> +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK``
> +      - Setting this to true enables automatic face detection and tracking. The
> +	current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> +	the driver.
> +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION``
> +      - Setting this to true enables automatic image stabilization. The
> +	current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> +	the driver.
> +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY``
> +      - Setting this to true enables automatically capture the specified region

I wonder what this means, the UVC 1.5 specification doesn't document
this bit clearly :-(

> +	with higher quality if possible.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2023-12-08 13:41     ` Laurent Pinchart
@ 2023-12-12  1:33       ` Yunke Cao
  2023-12-12  1:37         ` Laurent Pinchart
  0 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-12  1:33 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Laurent, Hans,

On Fri, Dec 8, 2023 at 10:41 PM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Hans,
>
> On Fri, Dec 01, 2023 at 09:35:21AM +0100, Hans Verkuil wrote:
> > On 01/12/2023 08:18, Yunke Cao wrote:
> > > Add p_rect to struct v4l2_ext_control with basic support in
> > > v4l2-ctrls.
> > >
> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> > > Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> > > Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
> > > Signed-off-by: Yunke Cao <yunkec@google.com>
> > > Reviewed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> > > ---
> > > Changelog since v12:
> > > - No Change.
> > > Changelog since v11:
> > > - Added reviewed-by from Hans
> > > Changelog since v10:
> > > - Added reviewed-by from Sergey and Daniel.
> > > Changelog since v9:
> > > - No Change.
> > > Changelog since v8:
> > > - No change.
> > > Changelog since v7:
> > > - Document V4L2_CTRL_TYPE_RECT in vidioc-queryctrl.rst.
> > > - Rebased to media-stage master.
> > > - Do not assign each field in std_equal
> > >
> > >  .../media/v4l/vidioc-g-ext-ctrls.rst             |  4 ++++
> > >  .../userspace-api/media/v4l/vidioc-queryctrl.rst |  7 +++++++
> > >  .../media/videodev2.h.rst.exceptions             |  1 +
> > >  drivers/media/v4l2-core/v4l2-ctrls-core.c        | 16 +++++++++++++++-
> > >  include/media/v4l2-ctrls.h                       |  2 ++
> > >  include/uapi/linux/videodev2.h                   |  2 ++
> > >  6 files changed, 31 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > > index f9f73530a6be..7b1001d11f9c 100644
> > > --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > > +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > > @@ -199,6 +199,10 @@ still cause this situation.
> > >        - ``p_area``
> > >        - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is
> > >          of type ``V4L2_CTRL_TYPE_AREA``.
> > > +    * - struct :c:type:`v4l2_rect` *
> > > +      - ``p_rect``
> > > +      - A pointer to a struct :c:type:`v4l2_rect`. Valid if this control is
> > > +        of type ``V4L2_CTRL_TYPE_RECT``.
> > >      * - struct :c:type:`v4l2_ctrl_h264_sps` *
> > >        - ``p_h264_sps``
> > >        - A pointer to a struct :c:type:`v4l2_ctrl_h264_sps`. Valid if this control is
> > > diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > > index 4d38acafe8e1..56d5c8b0b88b 100644
> > > --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > > +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > > @@ -441,6 +441,13 @@ See also the examples in :ref:`control`.
> > >        - n/a
> > >        - A struct :c:type:`v4l2_area`, containing the width and the height
> > >          of a rectangular area. Units depend on the use case.
> > > +    * - ``V4L2_CTRL_TYPE_RECT``
> > > +      - n/a
> > > +      - n/a
> > > +      - n/a
> > > +      - A struct :c:type:`v4l2_rect`, containing a rectangle described by
> > > +   the position of its top-left corner, the width and the height. Units
> > > +   depend on the use case.
> > >      * - ``V4L2_CTRL_TYPE_H264_SPS``
> > >        - n/a
> > >        - n/a
> > > diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > > index 3e58aac4ef0b..c46082ef0e4d 100644
> > > --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > > +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > > @@ -150,6 +150,7 @@ replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
> > >  replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
> > >  replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
> > >  replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
> > > +replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type`
> > >  replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type`
> > >  replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type`
> > >  replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type`
> > > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > > index a662fb60f73f..f1486ab032cf 100644
> > > --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > > +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > > @@ -367,7 +367,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> > >     case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
> > >             pr_cont("AV1_FILM_GRAIN");
> > >             break;
> > > -
> > > +   case V4L2_CTRL_TYPE_RECT:
> > > +           pr_cont("%ux%u@%dx%d",
> > > +                   ptr.p_rect->width, ptr.p_rect->height,
> > > +                   ptr.p_rect->left, ptr.p_rect->top);
> > > +           break;
> > >     default:
> > >             pr_cont("unknown type %d", ctrl->type);
> > >             break;
> > > @@ -812,6 +816,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> > >     struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
> > >     struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
> > >     struct v4l2_area *area;
> > > +   struct v4l2_rect *rect;
> > >     void *p = ptr.p + idx * ctrl->elem_size;
> > >     unsigned int i;
> > >
> > > @@ -1169,6 +1174,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> > >                     return -EINVAL;
> > >             break;
> > >
> > > +   case V4L2_CTRL_TYPE_RECT:
> > > +           rect = p;
> > > +           if (!rect->width || !rect->height)
> > > +                   return -EINVAL;
> > > +           break;
> > > +
> > >     default:
> > >             return -EINVAL;
> > >     }
> > > @@ -1868,6 +1879,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> > >     case V4L2_CTRL_TYPE_AREA:
> > >             elem_size = sizeof(struct v4l2_area);
> > >             break;
> > > +   case V4L2_CTRL_TYPE_RECT:
> > > +           elem_size = sizeof(struct v4l2_rect);
> > > +           break;
> > >     default:
> > >             if (type < V4L2_CTRL_COMPOUND_TYPES)
> > >                     elem_size = sizeof(s32);
> > > diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> > > index 59679a42b3e7..b0db167a3ac4 100644
> > > --- a/include/media/v4l2-ctrls.h
> > > +++ b/include/media/v4l2-ctrls.h
> > > @@ -56,6 +56,7 @@ struct video_device;
> > >   * @p_av1_tile_group_entry:        Pointer to an AV1 tile group entry structure.
> > >   * @p_av1_frame:           Pointer to an AV1 frame structure.
> > >   * @p_av1_film_grain:              Pointer to an AV1 film grain structure.
> > > + * @p_rect:                        Pointer to a rectangle.
> > >   * @p:                             Pointer to a compound value.
> > >   * @p_const:                       Pointer to a constant compound value.
> > >   */
> > > @@ -89,6 +90,7 @@ union v4l2_ctrl_ptr {
> > >     struct v4l2_ctrl_av1_tile_group_entry *p_av1_tile_group_entry;
> > >     struct v4l2_ctrl_av1_frame *p_av1_frame;
> > >     struct v4l2_ctrl_av1_film_grain *p_av1_film_grain;
> > > +   struct v4l2_rect *p_rect;
> > >     void *p;
> > >     const void *p_const;
> > >  };
> > > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> > > index c3d4e490ce7c..82d86abcf89c 100644
> > > --- a/include/uapi/linux/videodev2.h
> > > +++ b/include/uapi/linux/videodev2.h
> > > @@ -1815,6 +1815,7 @@ struct v4l2_ext_control {
> > >             __s32 __user *p_s32;
> > >             __s64 __user *p_s64;
> > >             struct v4l2_area __user *p_area;
> > > +           struct v4l2_rect __user *p_rect;
> > >             struct v4l2_ctrl_h264_sps __user *p_h264_sps;
> > >             struct v4l2_ctrl_h264_pps *p_h264_pps;
> > >             struct v4l2_ctrl_h264_scaling_matrix __user *p_h264_scaling_matrix;
> >
> > This will probably not apply cleanly anymore to the latest staging tree due
> > to a change to this struct that was merged there.
> >
> > Laurent, are you planning to make a PR for this? If so, then you can fix this
> > up yourself, ditto for the very small typo in patch 06/11 that I found.
>
> 'git am' complained indeed, but 'patch' was happy with a bit of fuzz.
> I've checked the result and it's all fine.
>
> I'm reviewing the rest of the series now.
>

Thanks for reviewing the series!!!! I will prepare a v15.
Should I base v15 on top of git.linuxtv.org/media_stage.git ?

Best,
Yunke

> > I'm happy with the v4l2 control changes, so this is ready to go as far as I am
> > concerned.
> >
> > > @@ -1883,6 +1884,7 @@ enum v4l2_ctrl_type {
> > >     V4L2_CTRL_TYPE_U16           = 0x0101,
> > >     V4L2_CTRL_TYPE_U32           = 0x0102,
> > >     V4L2_CTRL_TYPE_AREA          = 0x0106,
> > > +   V4L2_CTRL_TYPE_RECT          = 0x0107,
> > >
> > >     V4L2_CTRL_TYPE_HDR10_CLL_INFO           = 0x0110,
> > >     V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY  = 0x0111,
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2023-12-12  1:33       ` Yunke Cao
@ 2023-12-12  1:37         ` Laurent Pinchart
  0 siblings, 0 replies; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-12  1:37 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

On Tue, Dec 12, 2023 at 10:33:02AM +0900, Yunke Cao wrote:
> On Fri, Dec 8, 2023 at 10:41 PM Laurent Pinchart wrote:
> > On Fri, Dec 01, 2023 at 09:35:21AM +0100, Hans Verkuil wrote:
> > > On 01/12/2023 08:18, Yunke Cao wrote:
> > > > Add p_rect to struct v4l2_ext_control with basic support in
> > > > v4l2-ctrls.
> > > >
> > > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> > > > Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> > > > Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
> > > > Signed-off-by: Yunke Cao <yunkec@google.com>
> > > > Reviewed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> > > > ---
> > > > Changelog since v12:
> > > > - No Change.
> > > > Changelog since v11:
> > > > - Added reviewed-by from Hans
> > > > Changelog since v10:
> > > > - Added reviewed-by from Sergey and Daniel.
> > > > Changelog since v9:
> > > > - No Change.
> > > > Changelog since v8:
> > > > - No change.
> > > > Changelog since v7:
> > > > - Document V4L2_CTRL_TYPE_RECT in vidioc-queryctrl.rst.
> > > > - Rebased to media-stage master.
> > > > - Do not assign each field in std_equal
> > > >
> > > >  .../media/v4l/vidioc-g-ext-ctrls.rst             |  4 ++++
> > > >  .../userspace-api/media/v4l/vidioc-queryctrl.rst |  7 +++++++
> > > >  .../media/videodev2.h.rst.exceptions             |  1 +
> > > >  drivers/media/v4l2-core/v4l2-ctrls-core.c        | 16 +++++++++++++++-
> > > >  include/media/v4l2-ctrls.h                       |  2 ++
> > > >  include/uapi/linux/videodev2.h                   |  2 ++
> > > >  6 files changed, 31 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > > > index f9f73530a6be..7b1001d11f9c 100644
> > > > --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > > > +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > > > @@ -199,6 +199,10 @@ still cause this situation.
> > > >        - ``p_area``
> > > >        - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is
> > > >          of type ``V4L2_CTRL_TYPE_AREA``.
> > > > +    * - struct :c:type:`v4l2_rect` *
> > > > +      - ``p_rect``
> > > > +      - A pointer to a struct :c:type:`v4l2_rect`. Valid if this control is
> > > > +        of type ``V4L2_CTRL_TYPE_RECT``.
> > > >      * - struct :c:type:`v4l2_ctrl_h264_sps` *
> > > >        - ``p_h264_sps``
> > > >        - A pointer to a struct :c:type:`v4l2_ctrl_h264_sps`. Valid if this control is
> > > > diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > > > index 4d38acafe8e1..56d5c8b0b88b 100644
> > > > --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > > > +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > > > @@ -441,6 +441,13 @@ See also the examples in :ref:`control`.
> > > >        - n/a
> > > >        - A struct :c:type:`v4l2_area`, containing the width and the height
> > > >          of a rectangular area. Units depend on the use case.
> > > > +    * - ``V4L2_CTRL_TYPE_RECT``
> > > > +      - n/a
> > > > +      - n/a
> > > > +      - n/a
> > > > +      - A struct :c:type:`v4l2_rect`, containing a rectangle described by
> > > > +   the position of its top-left corner, the width and the height. Units
> > > > +   depend on the use case.
> > > >      * - ``V4L2_CTRL_TYPE_H264_SPS``
> > > >        - n/a
> > > >        - n/a
> > > > diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > > > index 3e58aac4ef0b..c46082ef0e4d 100644
> > > > --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > > > +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > > > @@ -150,6 +150,7 @@ replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
> > > >  replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
> > > >  replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
> > > >  replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
> > > > +replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type`
> > > >  replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type`
> > > >  replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type`
> > > >  replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type`
> > > > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > > > index a662fb60f73f..f1486ab032cf 100644
> > > > --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > > > +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > > > @@ -367,7 +367,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> > > >     case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
> > > >             pr_cont("AV1_FILM_GRAIN");
> > > >             break;
> > > > -
> > > > +   case V4L2_CTRL_TYPE_RECT:
> > > > +           pr_cont("%ux%u@%dx%d",
> > > > +                   ptr.p_rect->width, ptr.p_rect->height,
> > > > +                   ptr.p_rect->left, ptr.p_rect->top);
> > > > +           break;
> > > >     default:
> > > >             pr_cont("unknown type %d", ctrl->type);
> > > >             break;
> > > > @@ -812,6 +816,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> > > >     struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
> > > >     struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
> > > >     struct v4l2_area *area;
> > > > +   struct v4l2_rect *rect;
> > > >     void *p = ptr.p + idx * ctrl->elem_size;
> > > >     unsigned int i;
> > > >
> > > > @@ -1169,6 +1174,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> > > >                     return -EINVAL;
> > > >             break;
> > > >
> > > > +   case V4L2_CTRL_TYPE_RECT:
> > > > +           rect = p;
> > > > +           if (!rect->width || !rect->height)
> > > > +                   return -EINVAL;
> > > > +           break;
> > > > +
> > > >     default:
> > > >             return -EINVAL;
> > > >     }
> > > > @@ -1868,6 +1879,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> > > >     case V4L2_CTRL_TYPE_AREA:
> > > >             elem_size = sizeof(struct v4l2_area);
> > > >             break;
> > > > +   case V4L2_CTRL_TYPE_RECT:
> > > > +           elem_size = sizeof(struct v4l2_rect);
> > > > +           break;
> > > >     default:
> > > >             if (type < V4L2_CTRL_COMPOUND_TYPES)
> > > >                     elem_size = sizeof(s32);
> > > > diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> > > > index 59679a42b3e7..b0db167a3ac4 100644
> > > > --- a/include/media/v4l2-ctrls.h
> > > > +++ b/include/media/v4l2-ctrls.h
> > > > @@ -56,6 +56,7 @@ struct video_device;
> > > >   * @p_av1_tile_group_entry:        Pointer to an AV1 tile group entry structure.
> > > >   * @p_av1_frame:           Pointer to an AV1 frame structure.
> > > >   * @p_av1_film_grain:              Pointer to an AV1 film grain structure.
> > > > + * @p_rect:                        Pointer to a rectangle.
> > > >   * @p:                             Pointer to a compound value.
> > > >   * @p_const:                       Pointer to a constant compound value.
> > > >   */
> > > > @@ -89,6 +90,7 @@ union v4l2_ctrl_ptr {
> > > >     struct v4l2_ctrl_av1_tile_group_entry *p_av1_tile_group_entry;
> > > >     struct v4l2_ctrl_av1_frame *p_av1_frame;
> > > >     struct v4l2_ctrl_av1_film_grain *p_av1_film_grain;
> > > > +   struct v4l2_rect *p_rect;
> > > >     void *p;
> > > >     const void *p_const;
> > > >  };
> > > > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> > > > index c3d4e490ce7c..82d86abcf89c 100644
> > > > --- a/include/uapi/linux/videodev2.h
> > > > +++ b/include/uapi/linux/videodev2.h
> > > > @@ -1815,6 +1815,7 @@ struct v4l2_ext_control {
> > > >             __s32 __user *p_s32;
> > > >             __s64 __user *p_s64;
> > > >             struct v4l2_area __user *p_area;
> > > > +           struct v4l2_rect __user *p_rect;
> > > >             struct v4l2_ctrl_h264_sps __user *p_h264_sps;
> > > >             struct v4l2_ctrl_h264_pps *p_h264_pps;
> > > >             struct v4l2_ctrl_h264_scaling_matrix __user *p_h264_scaling_matrix;
> > >
> > > This will probably not apply cleanly anymore to the latest staging tree due
> > > to a change to this struct that was merged there.
> > >
> > > Laurent, are you planning to make a PR for this? If so, then you can fix this
> > > up yourself, ditto for the very small typo in patch 06/11 that I found.
> >
> > 'git am' complained indeed, but 'patch' was happy with a bit of fuzz.
> > I've checked the result and it's all fine.
> >
> > I'm reviewing the rest of the series now.
> >
> 
> Thanks for reviewing the series!!!! I will prepare a v15.
> Should I base v15 on top of git.linuxtv.org/media_stage.git ?

Yes, that's the best base I think.

> > > I'm happy with the v4l2 control changes, so this is ready to go as far as I am
> > > concerned.
> > >
> > > > @@ -1883,6 +1884,7 @@ enum v4l2_ctrl_type {
> > > >     V4L2_CTRL_TYPE_U16           = 0x0101,
> > > >     V4L2_CTRL_TYPE_U32           = 0x0102,
> > > >     V4L2_CTRL_TYPE_AREA          = 0x0106,
> > > > +   V4L2_CTRL_TYPE_RECT          = 0x0107,
> > > >
> > > >     V4L2_CTRL_TYPE_HDR10_CLL_INFO           = 0x0110,
> > > >     V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY  = 0x0111,

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 11/11] media: uvcvideo: document UVC v1.5 ROI
  2023-12-08 16:00   ` Laurent Pinchart
@ 2023-12-12  4:45     ` Yunke Cao
  2023-12-18  3:44       ` Laurent Pinchart
  0 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-12  4:45 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Laurent,

Thanks for the review!

On Sat, Dec 9, 2023 at 1:00 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke,
>
> Thank you for the patch.
>
> On Fri, Dec 01, 2023 at 04:19:02PM +0900, Yunke Cao wrote:
> > Added documentation of V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
> > V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.
>
> It would be useful to point here to the userspace code that uses those
> controls. A link to the appropriate Chrome OS git tree would be useful,
> to showcase a real user.

Does https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/camera/hal/usb/
work?
Do you think we need the link in both the commit message and this .rst file?

>
> > Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> > Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> > Changelog since v11:
> > - No change.
> > Changelog since v10:
> > - Added Reviewed-by from Sergey.
> > Changelog since v9:
> > - No change.
> > Changelog since v8:
> > - No change.
> > Changelog since v7:
> > - Fix documentation for automatic exposure based on comment in v7.
> >
> >  .../userspace-api/media/drivers/uvcvideo.rst  | 62 +++++++++++++++++++
> >  1 file changed, 62 insertions(+)
> >
> > diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > index aab4304e6bb5..3dc062221f8b 100644
> > --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > @@ -181,6 +181,7 @@ Argument: struct uvc_xu_control_mapping
> >       UVC_CTRL_DATA_TYPE_BOOLEAN      Boolean
> >       UVC_CTRL_DATA_TYPE_ENUM         Enumeration
> >       UVC_CTRL_DATA_TYPE_BITMASK      Bitmask
> > +     UVC_CTRL_DATA_TYPE_RECT         Rectangular area
> >
> >
> >  UVCIOC_CTRL_QUERY - Query a UVC XU control
> > @@ -255,3 +256,64 @@ Argument: struct uvc_xu_control_query
> >       __u8    query           Request code to send to the device
> >       __u16   size            Control data size (in bytes)
> >       __u8    *data           Control value
> > +
> > +
> > +Driver-specific V4L2 controls
> > +-----------------------------
> > +
> > +The uvcvideo driver implements the following UVC-specific controls:
> > +
> > +``V4L2_CID_UVC_REGION_OF_INTEREST_RECT (struct)``
> > +     This control determines the region of interest (ROI). ROI is a
> > +     rectangular area represented by a struct :c:type:`v4l2_rect`. The
> > +     rectangle is in global sensor coordinates and pixel units. It is
> > +     independent of the field of view, not impacted by any cropping or
> > +     scaling.
> > +
> > +     Use ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` to query
> > +     the range of rectangle sizes. The left/top coordinates of a minimum or
> > +     maximum rectangle are always 0. For example, a device can have a minimum
> > +     ROI rectangle of 1x1@0x0 and a maximum of 640x480@0x0.
>
> Is that actually true ? The UVC 1.5 specification states
>
>     GET_MAX shall return the current Window as specified by
>     CT_DIGITAL_WINDOW_CONTROL.
>
> And the window can have non-zero left and right coordinates.
>

Ah, you are right. I think I will just remove "The left/top
coordinates of a minimum or maximum rectangle are always 0."

> > +
> > +     Setting a ROI allows the camera to optimize the capture for the region.
> > +     The value of ``V4L2_CID_REGION_OF_INTEREST_AUTO`` control determines
> > +     the detailed behavior.
> > +
> > +
> > +``V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (bitmask)``
> > +     This determines which, if any, on board features should track to the
>
> s/on board/on-board/
>
> > +     Region of Interest specified by the current value of
> > +     ``V4L2_CID_UVD__REGION_OF_INTEREST_RECT``.
> > +
> > +     Max value is a mask indicating all supported Auto Controls.
> > +
> > +.. flat-table::
> > +    :header-rows:  0
> > +    :stub-columns: 0
> > +
> > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE``
> > +      - Setting this to true causes automatic exposure to track the region of
>
> Maybe "Setting this bit causes..." as those values are bit flags, not
> booleans. Same below.
>

Sounds good.

> > +     interest instead of the whole image.
> > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS``
> > +      - Setting this to true causes automatic iris to track the region of
> > +     interest instead of the whole image.
> > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE``
> > +      - Setting this to true causes automatic white balance to track the region
> > +     of interest instead of the whole image.
> > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS``
> > +      - Setting this to true causes automatic focus adjustment to track the
> > +     region of interest instead of the whole image.
> > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT``
> > +      - Setting this to true causes automatic face detection to track the
> > +     region of interest instead of the whole image.
> > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK``
> > +      - Setting this to true enables automatic face detection and tracking. The
> > +     current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> > +     the driver.
> > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION``
> > +      - Setting this to true enables automatic image stabilization. The
> > +     current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> > +     the driver.
> > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY``
> > +      - Setting this to true enables automatically capture the specified region
>
> I wonder what this means, the UVC 1.5 specification doesn't document
> this bit clearly :-(
>

I have no idea :-( . I haven't seen any camera module in Chrome OS
attempt to implement this bit, either.

Best,
Yunke

> > +     with higher quality if possible.
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size
  2023-12-08 14:15   ` Laurent Pinchart
@ 2023-12-12  7:59     ` Yunke Cao
  2023-12-18  3:17       ` Laurent Pinchart
  0 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-12  7:59 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Laurent,

Thanks for the review!


On Fri, Dec 8, 2023 at 11:15 PM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke,
>
> Thank you for the patch.
>
> On Fri, Dec 01, 2023 at 04:18:55PM +0900, Yunke Cao wrote:
> > Rename the existing size to data_size to represent uvc control data size,
> > add a separate field for v4l2 control size. v4l2 control size will be
> > used the compound controls.
>
> s/uvc/UVC/ and s/v4l2/V4L2/ in the whole commit message.
>
> > Also modify the uvc driver documents to clarify the size in
> > uvc_xu_control_mapping corresponds to the uvc control data size.
> >
> > Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> > Changelog since v11:
> > - No change.
> > Changelog since v10:
> > - Added Reviewed-by from Daniel Scally.
> > Changelog since v9:
> > - No change.
> > Changelog since v8:
> > - No change.
> > Changelog since v7:
> > - Newly added patch.
> >
> >  .../userspace-api/media/drivers/uvcvideo.rst  |  2 +-
> >  drivers/media/usb/uvc/uvc_ctrl.c              | 80 +++++++++----------
> >  drivers/media/usb/uvc/uvc_v4l2.c              |  2 +-
> >  drivers/media/usb/uvc/uvcvideo.h              |  6 +-
> >  4 files changed, 47 insertions(+), 43 deletions(-)
> >
> > diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > index a290f9fadae9..aab4304e6bb5 100644
> > --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > @@ -157,7 +157,7 @@ Argument: struct uvc_xu_control_mapping
> >       __u8    name[32]        V4L2 control name
> >       __u8    entity[16]      UVC extension unit GUID
> >       __u8    selector        UVC control selector
> > -     __u8    size            V4L2 control size (in bits)
> > +     __u8    size            UVC control data size (in bits)
>
> The V4L2 and UVC sizes are identical for all controls mapped through
> this mechanism, right ?
>

Yes, I think that's right in the current implementation.
Do you think we need to support UVCIOC_CTRL_MAP for the compound controls?

I wanted to change this because simply we assign
+     map->data_size = xmap->size;
in uvc_ioctl_xu_ctrl_map().

Is this okay?

> >       __u8    offset          V4L2 control offset (in bits)
>
> If the size if the "UVC control data size", shouldn't this be the "UVC
> control data offset" ?
>

Ah, right. I will change this in the v15 if we keep the "UVC control data size".

> >       enum v4l2_ctrl_type
> >               v4l2_type       V4L2 control type
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index 4a685532f7eb..98254b93eb46 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -464,7 +464,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_BRIGHTNESS,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_BRIGHTNESS_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > @@ -473,7 +473,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_CONTRAST,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_CONTRAST_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -482,7 +482,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_HUE,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_HUE_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > @@ -493,7 +493,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_SATURATION,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_SATURATION_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -502,7 +502,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_SHARPNESS,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_SHARPNESS_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -511,7 +511,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_GAMMA,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_GAMMA_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -520,7 +520,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_BACKLIGHT_COMPENSATION,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -529,7 +529,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_GAIN,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_GAIN_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -538,7 +538,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_HUE_AUTO,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_HUE_AUTO_CONTROL,
> > -             .size           = 1,
> > +             .data_size      = 1,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > @@ -548,7 +548,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_EXPOSURE_AUTO,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_AE_MODE_CONTROL,
> > -             .size           = 4,
> > +             .data_size      = 4,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_MENU,
> >               .data_type      = UVC_CTRL_DATA_TYPE_BITMASK,
> > @@ -561,7 +561,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_AE_PRIORITY_CONTROL,
> > -             .size           = 1,
> > +             .data_size      = 1,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > @@ -570,7 +570,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_EXPOSURE_ABSOLUTE,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
> > -             .size           = 32,
> > +             .data_size      = 32,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -581,7 +581,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_AUTO_WHITE_BALANCE,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
> > -             .size           = 1,
> > +             .data_size      = 1,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > @@ -591,7 +591,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -602,7 +602,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_AUTO_WHITE_BALANCE,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
> > -             .size           = 1,
> > +             .data_size      = 1,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > @@ -613,7 +613,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_BLUE_BALANCE,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > @@ -624,7 +624,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_RED_BALANCE,
> >               .entity         = UVC_GUID_UVC_PROCESSING,
> >               .selector       = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 16,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > @@ -635,7 +635,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_FOCUS_ABSOLUTE,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -646,7 +646,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_FOCUS_AUTO,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_FOCUS_AUTO_CONTROL,
> > -             .size           = 1,
> > +             .data_size      = 1,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > @@ -656,7 +656,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_IRIS_ABSOLUTE,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_IRIS_ABSOLUTE_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -665,7 +665,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_IRIS_RELATIVE,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_IRIS_RELATIVE_CONTROL,
> > -             .size           = 8,
> > +             .data_size      = 8,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > @@ -674,7 +674,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_ZOOM_ABSOLUTE,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > @@ -683,7 +683,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_ZOOM_CONTINUOUS,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_ZOOM_RELATIVE_CONTROL,
> > -             .size           = 0,
> > +             .data_size      = 0,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > @@ -694,7 +694,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_PAN_ABSOLUTE,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
> > -             .size           = 32,
> > +             .data_size      = 32,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > @@ -703,7 +703,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_TILT_ABSOLUTE,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
> > -             .size           = 32,
> > +             .data_size      = 32,
> >               .offset         = 32,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > @@ -712,7 +712,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_PAN_SPEED,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_PANTILT_RELATIVE_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > @@ -723,7 +723,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_TILT_SPEED,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_PANTILT_RELATIVE_CONTROL,
> > -             .size           = 16,
> > +             .data_size      = 16,
> >               .offset         = 16,
> >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > @@ -734,7 +734,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_PRIVACY,
> >               .entity         = UVC_GUID_UVC_CAMERA,
> >               .selector       = UVC_CT_PRIVACY_CONTROL,
> > -             .size           = 1,
> > +             .data_size      = 1,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > @@ -743,7 +743,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .id             = V4L2_CID_PRIVACY,
> >               .entity         = UVC_GUID_EXT_GPIO_CONTROLLER,
> >               .selector       = UVC_CT_PRIVACY_CONTROL,
> > -             .size           = 1,
> > +             .data_size      = 1,
> >               .offset         = 0,
> >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > @@ -754,7 +754,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
> >       .id             = V4L2_CID_POWER_LINE_FREQUENCY,
> >       .entity         = UVC_GUID_UVC_PROCESSING,
> >       .selector       = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
> > -     .size           = 2,
> > +     .data_size      = 2,
> >       .offset         = 0,
> >       .v4l2_type      = V4L2_CTRL_TYPE_MENU,
> >       .data_type      = UVC_CTRL_DATA_TYPE_ENUM,
> > @@ -766,7 +766,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
> >       .id             = V4L2_CID_POWER_LINE_FREQUENCY,
> >       .entity         = UVC_GUID_UVC_PROCESSING,
> >       .selector       = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
> > -     .size           = 2,
> > +     .data_size      = 2,
> >       .offset         = 0,
> >       .v4l2_type      = V4L2_CTRL_TYPE_MENU,
> >       .data_type      = UVC_CTRL_DATA_TYPE_ENUM,
> > @@ -783,7 +783,7 @@ static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
> >       .id             = V4L2_CID_POWER_LINE_FREQUENCY,
> >       .entity         = UVC_GUID_UVC_PROCESSING,
> >       .selector       = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
> > -     .size           = 2,
> > +     .data_size      = 2,
> >       .offset         = 0,
> >       .v4l2_type      = V4L2_CTRL_TYPE_MENU,
> >       .data_type      = UVC_CTRL_DATA_TYPE_ENUM,
> > @@ -816,7 +816,7 @@ static inline void uvc_clear_bit(u8 *data, int bit)
> >  }
> >
> >  /*
> > - * Extract the bit string specified by mapping->offset and mapping->size
> > + * Extract the bit string specified by mapping->offset and mapping->data_size
> >   * from the little-endian data stored at 'data' and return the result as
> >   * a signed 32bit integer. Sign extension will be performed if the mapping
> >   * references a signed data type.
> > @@ -824,7 +824,7 @@ static inline void uvc_clear_bit(u8 *data, int bit)
> >  static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
> >       u8 query, const u8 *data)
> >  {
> > -     int bits = mapping->size;
> > +     int bits = mapping->data_size;
> >       int offset = mapping->offset;
> >       s32 value = 0;
> >       u8 mask;
> > @@ -847,19 +847,19 @@ static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
> >
> >       /* Sign-extend the value if needed. */
> >       if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
> > -             value |= -(value & (1 << (mapping->size - 1)));
> > +             value |= -(value & (1 << (mapping->data_size - 1)));
> >
> >       return value;
> >  }
> >
> >  /*
> > - * Set the bit string specified by mapping->offset and mapping->size
> > + * Set the bit string specified by mapping->offset and mapping->data_size
> >   * in the little-endian data stored at 'data' to the value 'value'.
> >   */
> >  static void uvc_set_le_value(struct uvc_control_mapping *mapping,
> >       s32 value, u8 *data)
> >  {
> > -     int bits = mapping->size;
> > +     int bits = mapping->data_size;
> >       int offset = mapping->offset;
> >       u8 mask;
> >
> > @@ -2039,7 +2039,7 @@ int uvc_ctrl_set(struct uvc_fh *handle,
> >        * needs to be loaded from the device to perform the read-modify-write
> >        * operation.
> >        */
> > -     if ((ctrl->info.size * 8) != mapping->size) {
> > +     if ((ctrl->info.size * 8) != mapping->data_size) {
> >               ret = __uvc_ctrl_load_cur(chain, ctrl);
> >               if (ret < 0)
> >                       return ret;
> > @@ -2546,8 +2546,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> >       }
> >
> >       /* Validate the user-provided bit-size and offset */
> > -     if (mapping->size > 32 ||
> > -         mapping->offset + mapping->size > ctrl->info.size * 8) {
> > +     if (mapping->data_size > 32 ||
> > +         mapping->offset + mapping->data_size > ctrl->info.size * 8) {
> >               ret = -EINVAL;
> >               goto done;
> >       }
> > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> > index 5a88847bfbfe..ff72caf7bc9e 100644
> > --- a/drivers/media/usb/uvc/uvc_v4l2.c
> > +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> > @@ -122,7 +122,7 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
> >       }
> >       memcpy(map->entity, xmap->entity, sizeof(map->entity));
> >       map->selector = xmap->selector;
> > -     map->size = xmap->size;
> > +     map->data_size = xmap->size;
> >       map->offset = xmap->offset;
> >       map->v4l2_type = xmap->v4l2_type;
> >       map->data_type = xmap->data_type;
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index 5091085fcfb0..7bc41270ed94 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -110,7 +110,11 @@ struct uvc_control_mapping {
> >       u8 entity[16];
> >       u8 selector;
> >
> > -     u8 size;
> > +     /* Size of the v4l2 control. Required for compound controls. */
> > +     u8 v4l2_size;
>
> Let's introduce this field in the patch that uses it. The commit message
> needs to be updated to explain that this patch renames the size field to
> data_size to prepare for adding another size field for compound
> controls.
>

Sounds good.

> > +     /* UVC data size. Required for all controls. */
>
> "UVC data size" is not very clear. Let me attempt to write a more
> precise description:
>
>         /*
>          * Size of the control data in the payload of the UVC control GET and
>          * SET requests, expressed in bits.
>          */
>
> Is this correct ?
>

Yes, that sounds much better indeed.

Best,
Yunke

> > +     u8 data_size;
> > +
> >       u8 offset;
> >       enum v4l2_ctrl_type v4l2_type;
> >       u32 data_type;
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-12-08 15:20   ` Laurent Pinchart
@ 2023-12-13  4:06     ` Yunke Cao
  0 siblings, 0 replies; 40+ messages in thread
From: Yunke Cao @ 2023-12-13  4:06 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Laurent,

On Sat, Dec 9, 2023 at 12:20 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke and Hans,
>
> Thank you for the patch.
>
> On Fri, Dec 01, 2023 at 04:18:57PM +0900, Yunke Cao wrote:
> > From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> >
> > Add the capability of retrieving the min and max values of a
> > compound control.
> >
> > Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> > Changelog since v13:
> > - Updated comments of v4l2_ctrl_new_std_compound()
> > Changelog since v12:
> > - Addressed comments from Hans.
> > Changelog since v11:
> > - Added a flag V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
> > - Modified std_min/max_compound() to be void function. Moved the check of
> >   whether WHICH_MIN/MAX_VAL into prepare_ext_ctrls(), and return EINVAL.
> > - Modified documentations to reflect this change.
> > Changelog since v10:
> > - No change.
> > Changelog since v9:
> > - No change.
> > Changelog since v8:
> > - Return ENODATA when min/max is not implemented. Document this behavior.
> > - Created a shared helper function __v4l2_ctrl_type_op_init that takes "which"
> >   as a parameter. Call it in def, min and max operations.
> > Changelog since v7:
> > - Document that the definition of the min/max are provided by compound controls
> >   are defined in control documentation.
> > - Return error, instead of zeroed memory for v4l2_ctrl_ptr_create(NULL).
> >
> >  .../media/v4l/vidioc-g-ext-ctrls.rst          |  22 ++-
> >  .../media/v4l/vidioc-queryctrl.rst            |   9 +-
> >  .../media/videodev2.h.rst.exceptions          |   3 +
> >  drivers/media/i2c/imx214.c                    |   5 +-
> >  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
> >  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  54 +++++--
> >  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 151 +++++++++++++++---
> >  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
> >  include/media/v4l2-ctrls.h                    |  36 ++++-
> >  include/uapi/linux/videodev2.h                |   3 +
> >  10 files changed, 248 insertions(+), 48 deletions(-)
>
> I'm wondering, would it make sense to include in this series, before
> this patch, the following (or a similar) change ?
>
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index f4988f03640a..52208f2d71f3 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -1094,7 +1094,8 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
>         if (ret < 0)
>                 return ret;
>
> -       if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
> +       switch (ctrls->which) {
> +       case V4L2_CTRL_WHICH_DEF_VAL:
>                 for (i = 0; i < ctrls->count; ++ctrl, ++i) {
>                         struct v4l2_queryctrl qc = { .id = ctrl->id };
>
> @@ -1108,24 +1109,28 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
>                 }
>
>                 return 0;
> -       }
>
> -       ret = uvc_ctrl_begin(chain);
> -       if (ret < 0)
> -               return ret;
> -
> -       for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> -               ret = uvc_ctrl_get(chain, ctrl);
> -               if (ret < 0) {
> -                       uvc_ctrl_rollback(handle);
> -                       ctrls->error_idx = i;
> +       case V4L2_CTRL_WHICH_CUR_VAL:
> +               ret = uvc_ctrl_begin(chain);
> +               if (ret < 0)
>                         return ret;
> +
> +               for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> +                       ret = uvc_ctrl_get(chain, ctrl);
> +                       if (ret < 0) {
> +                               uvc_ctrl_rollback(handle);
> +                               ctrls->error_idx = i;
> +                               return ret;
> +                       }
>                 }
> +
> +               ctrls->error_idx = 0;
> +
> +               return uvc_ctrl_rollback(handle);
> +
> +       default:
> +               return -EINVAL;
>         }
> -
> -       ctrls->error_idx = 0;
> -
> -       return uvc_ctrl_rollback(handle);
>  }
>
>  static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
>

Thanks for proposing the change. It makes sense. I will include it in
v15 before this patch.

Best,
Yunke


> > diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > index 7b1001d11f9c..0b87c23e66ff 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > @@ -330,14 +330,26 @@ still cause this situation.
> >        - Which value of the control to get/set/try.
> >      * - :cspan:`2` ``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of
> >       the control, ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
> > -     value of the control and ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
> > -     these controls have to be retrieved from a request or tried/set for
> > -     a request. In the latter case the ``request_fd`` field contains the
> > +     value of the control, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
> > +     value of the control, and ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
> > +     value of the control. ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
> > +     the control value has to be retrieved from a request or tried/set for
> > +     a request. In that case the ``request_fd`` field contains the
> >       file descriptor of the request that should be used. If the device
> >       does not support requests, then ``EACCES`` will be returned.
> >
> > -     When using ``V4L2_CTRL_WHICH_DEF_VAL`` be aware that you can only
> > -     get the default value of the control, you cannot set or try it.
> > +     When using ``V4L2_CTRL_WHICH_DEF_VAL``, ``V4L2_CTRL_WHICH_MIN_VAL``
> > +     or ``V4L2_CTRL_WHICH_MAX_VAL`` be aware that you can only get the
> > +     default/minimum/maximum value of the control, you cannot set or try it.
> > +
> > +     Whether a control supports querying the minimum and maximum values using
> > +     ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` is indicated
> > +     by the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. Most non-compound
> > +     control types support this. For controls with compound types, the
> > +     definition of minimum/maximum values are provided by
> > +     the control documentation. If a compound control does not document the
> > +     meaning of minimum/maximum value, then querying the minimum or maximum
> > +     value will result in the error code -EINVAL.
> >
> >       For backwards compatibility you can also use a control class here
> >       (see :ref:`ctrl-class`). In that case all controls have to
> > diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > index 56d5c8b0b88b..b39f7e27bbbe 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > @@ -447,7 +447,10 @@ See also the examples in :ref:`control`.
> >        - n/a
> >        - A struct :c:type:`v4l2_rect`, containing a rectangle described by
> >       the position of its top-left corner, the width and the height. Units
> > -     depend on the use case.
> > +     depend on the use case. Support for ``V4L2_CTRL_WHICH_MIN_VAL`` and
> > +     ``V4L2_CTRL_WHICH_MAX_VAL`` is optional and depends on the
> > +     ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. See the documentation of
> > +     the specific control on how to interpret the minimum and maximum values.
> >      * - ``V4L2_CTRL_TYPE_H264_SPS``
> >        - n/a
> >        - n/a
> > @@ -664,6 +667,10 @@ See also the examples in :ref:`control`.
> >       ``dims[0]``. So setting the control with a differently sized
> >       array will change the ``elems`` field when the control is
> >       queried afterwards.
> > +    * - ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
> > +      - 0x1000
> > +      - This control supports getting minimum and maximum values using
> > +      vidioc_g_ext_ctrls with V4L2_CTRL_WHICH_MIN/MAX_VAL.
> >
> >  Return Value
> >  ============
> > diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > index c46082ef0e4d..a417af25e9a4 100644
> > --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > @@ -393,6 +393,7 @@ replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags
> >  replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags
> >  replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags
> >  replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags
> > +replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags
> >
> >  replace define V4L2_CTRL_FLAG_NEXT_CTRL control
> >  replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control
> > @@ -567,6 +568,8 @@ ignore define V4L2_CTRL_DRIVER_PRIV
> >  ignore define V4L2_CTRL_MAX_DIMS
> >  ignore define V4L2_CTRL_WHICH_CUR_VAL
> >  ignore define V4L2_CTRL_WHICH_DEF_VAL
> > +ignore define V4L2_CTRL_WHICH_MIN_VAL
> > +ignore define V4L2_CTRL_WHICH_MAX_VAL
> >  ignore define V4L2_CTRL_WHICH_REQUEST_VAL
> >  ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS
> >  ignore define V4L2_CID_MAX_CTRLS
> > diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
> > index 4f77ea02cc27..926b5cae12d8 100644
> > --- a/drivers/media/i2c/imx214.c
> > +++ b/drivers/media/i2c/imx214.c
> > @@ -998,7 +998,10 @@ static int imx214_probe(struct i2c_client *client)
> >       imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls,
> >                               NULL,
> >                               V4L2_CID_UNIT_CELL_SIZE,
> > -                             v4l2_ctrl_ptr_create((void *)&unit_size));
> > +                             v4l2_ctrl_ptr_create((void *)&unit_size),
> > +                             v4l2_ctrl_ptr_create(NULL),
> > +                             v4l2_ctrl_ptr_create(NULL));
> > +
> >       ret = imx214->ctrls.error;
> >       if (ret) {
> >               dev_err(&client->dev, "%s control init failed (%d)\n",
> > diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
> > index d9d2a293f3ef..7f370438d655 100644
> > --- a/drivers/media/platform/qcom/venus/venc_ctrls.c
> > +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
> > @@ -607,11 +607,16 @@ int venc_ctrl_init(struct venus_inst *inst)
> >
> >       v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
> >                                  V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
> > -                                v4l2_ctrl_ptr_create(&p_hdr10_cll));
> > +                                v4l2_ctrl_ptr_create(&p_hdr10_cll),
> > +                                v4l2_ctrl_ptr_create(NULL),
> > +                                v4l2_ctrl_ptr_create(NULL));
> >
> >       v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
> >                                  V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
> > -                                v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering));
> > +                                v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
> > +                                v4l2_ctrl_ptr_create(NULL),
> > +                                v4l2_ctrl_ptr_create(NULL));
> > +
> >
> >       v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
> >                              V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
> > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > index 002ea6588edf..d022e1ed4835 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > @@ -94,6 +94,22 @@ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> >       return ptr_to_user(c, ctrl, ctrl->p_new);
> >  }
> >
> > +/* Helper function: copy the minimum control value back to the caller */
> > +static int min_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> > +{
> > +     ctrl->type_ops->minimum(ctrl, 0, ctrl->p_new);
> > +
> > +     return ptr_to_user(c, ctrl, ctrl->p_new);
> > +}
> > +
> > +/* Helper function: copy the maximum control value back to the caller */
> > +static int max_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> > +{
> > +     ctrl->type_ops->maximum(ctrl, 0, ctrl->p_new);
> > +
> > +     return ptr_to_user(c, ctrl, ctrl->p_new);
> > +}
> > +
> >  /* Helper function: copy the caller-provider value as the new control value */
> >  static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> >  {
> > @@ -229,8 +245,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> >               cs->error_idx = i;
> >
> >               if (cs->which &&
> > -                 cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
> > -                 cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
> > +                 (cs->which < V4L2_CTRL_WHICH_DEF_VAL ||
> > +                  cs->which > V4L2_CTRL_WHICH_MAX_VAL) &&
> >                   V4L2_CTRL_ID2WHICH(id) != cs->which) {
> >                       dprintk(vdev,
> >                               "invalid which 0x%x or control id 0x%x\n",
> > @@ -259,6 +275,15 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> >                       return -EINVAL;
> >               }
> >
> > +             if (!(ctrl->flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) &&
> > +                 (cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> > +                  cs->which == V4L2_CTRL_WHICH_MAX_VAL)) {
> > +                     dprintk(vdev,
> > +                             "invalid which 0x%x or control id 0x%x\n",
> > +                             cs->which, id);
> > +                     return -EINVAL;
> > +             }
> > +
> >               if (ctrl->cluster[0]->ncontrols > 1)
> >                       have_clusters = true;
> >               if (ctrl->cluster[0] != ctrl)
> > @@ -368,8 +393,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> >   */
> >  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
> >  {
> > -     if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
> > -         which == V4L2_CTRL_WHICH_REQUEST_VAL)
> > +     if (which == 0 || (which >= V4L2_CTRL_WHICH_DEF_VAL &&
> > +                        which <= V4L2_CTRL_WHICH_MAX_VAL))
> >               return 0;
> >       return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
> >  }
> > @@ -389,10 +414,12 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
> >       struct v4l2_ctrl_helper *helpers = helper;
> >       int ret;
> >       int i, j;
> > -     bool is_default, is_request;
> > +     bool is_default, is_request, is_min, is_max;
> >
> >       is_default = (cs->which == V4L2_CTRL_WHICH_DEF_VAL);
> >       is_request = (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL);
> > +     is_min = (cs->which == V4L2_CTRL_WHICH_MIN_VAL);
> > +     is_max = (cs->which == V4L2_CTRL_WHICH_MAX_VAL);
> >
> >       cs->error_idx = cs->count;
> >       cs->which = V4L2_CTRL_ID2WHICH(cs->which);
> > @@ -432,13 +459,14 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
> >
> >               /*
> >                * g_volatile_ctrl will update the new control values.
> > -              * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL and
> > +              * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL,
> > +              * V4L2_CTRL_WHICH_MIN_VAL, V4L2_CTRL_WHICH_MAX_VAL and
> >                * V4L2_CTRL_WHICH_REQUEST_VAL. In the case of requests
> >                * it is v4l2_ctrl_request_complete() that copies the
> >                * volatile controls at the time of request completion
> >                * to the request, so you don't want to do that again.
> >                */
> > -             if (!is_default && !is_request &&
> > +             if (!is_default && !is_request && !is_min && !is_max &&
> >                   ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
> >                   (master->has_volatiles && !is_cur_manual(master)))) {
> >                       for (j = 0; j < master->ncontrols; j++)
> > @@ -467,6 +495,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
> >                               ret = -ENOMEM;
> >                       else if (is_request && ref->p_req_valid)
> >                               ret = req_to_user(cs->controls + idx, ref);
> > +                     else if (is_min)
> > +                             ret = min_to_user(cs->controls + idx, ref->ctrl);
> > +                     else if (is_max)
> > +                             ret = max_to_user(cs->controls + idx, ref->ctrl);
> >                       else if (is_volatile)
> >                               ret = new_to_user(cs->controls + idx, ref->ctrl);
> >                       else
> > @@ -564,9 +596,11 @@ int try_set_ext_ctrls_common(struct v4l2_fh *fh,
> >
> >       cs->error_idx = cs->count;
> >
> > -     /* Default value cannot be changed */
> > -     if (cs->which == V4L2_CTRL_WHICH_DEF_VAL) {
> > -             dprintk(vdev, "%s: cannot change default value\n",
> > +     /* Default/minimum/maximum values cannot be changed */
> > +     if (cs->which == V4L2_CTRL_WHICH_DEF_VAL ||
> > +         cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> > +         cs->which == V4L2_CTRL_WHICH_MAX_VAL) {
> > +             dprintk(vdev, "%s: cannot change default/min/max value\n",
> >                       video_device_node_name(vdev));
> >               return -EINVAL;
> >       }
> > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > index f1486ab032cf..ef418165e88d 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > @@ -182,29 +182,66 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> >       }
> >  }
> >
> > -void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > +static void std_min_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> > +                         union v4l2_ctrl_ptr ptr)
> > +{
> > +     void *p = ptr.p + idx * ctrl->elem_size;
> > +
> > +     if (ctrl->p_min.p_const)
> > +             memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
> > +     else
> > +             memset(p, 0, ctrl->elem_size);
> > +}
> > +
> > +static void std_max_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> >                           union v4l2_ctrl_ptr ptr)
> > +{
> > +     void *p = ptr.p + idx * ctrl->elem_size;
> > +
> > +     if (ctrl->p_max.p_const)
> > +             memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> > +     else
> > +             memset(p, 0, ctrl->elem_size);
> > +}
> > +
> > +static void __v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > +                                  u32 which, union v4l2_ctrl_ptr ptr)
> >  {
> >       unsigned int i;
> >       u32 tot_elems = ctrl->elems;
> >       u32 elems = tot_elems - from_idx;
> > +     s64 value;
> >
> > -     if (from_idx >= tot_elems)
> > +     switch (which) {
> > +     case V4L2_CTRL_WHICH_DEF_VAL:
> > +             value = ctrl->default_value;
> > +             break;
> > +     case V4L2_CTRL_WHICH_MAX_VAL:
> > +             value = ctrl->maximum;
> > +             break;
> > +     case V4L2_CTRL_WHICH_MIN_VAL:
> > +             value = ctrl->minimum;
> > +             break;
> > +     default:
> >               return;
> > +     }
> >
> >       switch (ctrl->type) {
> >       case V4L2_CTRL_TYPE_STRING:
> > +             if (which == V4L2_CTRL_WHICH_DEF_VAL)
> > +                     value = ctrl->minimum;
> > +
> >               for (i = from_idx; i < tot_elems; i++) {
> >                       unsigned int offset = i * ctrl->elem_size;
> >
> > -                     memset(ptr.p_char + offset, ' ', ctrl->minimum);
> > -                     ptr.p_char[offset + ctrl->minimum] = '\0';
> > +                     memset(ptr.p_char + offset, ' ', value);
> > +                     ptr.p_char[offset + value] = '\0';
> >               }
> >               break;
> >       case V4L2_CTRL_TYPE_INTEGER64:
> > -             if (ctrl->default_value) {
> > +             if (value) {
> >                       for (i = from_idx; i < tot_elems; i++)
> > -                             ptr.p_s64[i] = ctrl->default_value;
> > +                             ptr.p_s64[i] = value;
> >               } else {
> >                       memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
> >               }
> > @@ -214,9 +251,9 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> >       case V4L2_CTRL_TYPE_MENU:
> >       case V4L2_CTRL_TYPE_BITMASK:
> >       case V4L2_CTRL_TYPE_BOOLEAN:
> > -             if (ctrl->default_value) {
> > +             if (value) {
> >                       for (i = from_idx; i < tot_elems; i++)
> > -                             ptr.p_s32[i] = ctrl->default_value;
> > +                             ptr.p_s32[i] = value;
> >               } else {
> >                       memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
> >               }
> > @@ -226,32 +263,61 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> >               memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
> >               break;
> >       case V4L2_CTRL_TYPE_U8:
> > -             memset(ptr.p_u8 + from_idx, ctrl->default_value, elems);
> > +             memset(ptr.p_u8 + from_idx, value, elems);
> >               break;
> >       case V4L2_CTRL_TYPE_U16:
> > -             if (ctrl->default_value) {
> > +             if (value) {
> >                       for (i = from_idx; i < tot_elems; i++)
> > -                             ptr.p_u16[i] = ctrl->default_value;
> > +                             ptr.p_u16[i] = value;
> >               } else {
> >                       memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
> >               }
> >               break;
> >       case V4L2_CTRL_TYPE_U32:
> > -             if (ctrl->default_value) {
> > +             if (value) {
> >                       for (i = from_idx; i < tot_elems; i++)
> > -                             ptr.p_u32[i] = ctrl->default_value;
> > +                             ptr.p_u32[i] = value;
> >               } else {
> >                       memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
> >               }
> >               break;
> >       default:
> > -             for (i = from_idx; i < tot_elems; i++)
> > -                     std_init_compound(ctrl, i, ptr);
> > +             for (i = from_idx; i < tot_elems; i++) {
> > +                     switch (which) {
> > +                     case V4L2_CTRL_WHICH_DEF_VAL:
> > +                             std_init_compound(ctrl, i, ptr);
> > +                             break;
> > +                     case V4L2_CTRL_WHICH_MAX_VAL:
> > +                             std_max_compound(ctrl, i, ptr);
> > +                             break;
> > +                     case V4L2_CTRL_WHICH_MIN_VAL:
> > +                             std_min_compound(ctrl, i, ptr);
> > +                             break;
> > +                     }
> > +             }
> >               break;
> >       }
> >  }
> > +
> > +void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > +                         union v4l2_ctrl_ptr ptr)
> > +{
> > +     __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_DEF_VAL, ptr);
> > +}
> >  EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
> >
> > +void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > +                            union v4l2_ctrl_ptr ptr)
> > +{
> > +     __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
> > +}
> > +
> > +void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > +                            union v4l2_ctrl_ptr ptr)
> > +{
> > +     __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
> > +}
> > +
> >  void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> >  {
> >       union v4l2_ctrl_ptr ptr = ctrl->p_cur;
> > @@ -1293,6 +1359,8 @@ EXPORT_SYMBOL(v4l2_ctrl_type_op_validate);
> >  static const struct v4l2_ctrl_type_ops std_type_ops = {
> >       .equal = v4l2_ctrl_type_op_equal,
> >       .init = v4l2_ctrl_type_op_init,
> > +     .minimum = v4l2_ctrl_type_op_minimum,
> > +     .maximum = v4l2_ctrl_type_op_maximum,
> >       .log = v4l2_ctrl_type_op_log,
> >       .validate = v4l2_ctrl_type_op_validate,
> >  };
> > @@ -1764,7 +1832,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >                       s64 min, s64 max, u64 step, s64 def,
> >                       const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
> >                       u32 flags, const char * const *qmenu,
> > -                     const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def,
> > +                     const s64 *qmenu_int,
> > +                     const union v4l2_ctrl_ptr p_def,
> > +                     const union v4l2_ctrl_ptr p_min,
> > +                     const union v4l2_ctrl_ptr p_max,
> >                       void *priv)
> >  {
> >       struct v4l2_ctrl *ctrl;
> > @@ -1888,6 +1959,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >               break;
> >       }
> >
> > +     if (type < V4L2_CTRL_COMPOUND_TYPES &&
> > +         type != V4L2_CTRL_TYPE_BUTTON &&
> > +         type != V4L2_CTRL_TYPE_CTRL_CLASS &&
> > +         type != V4L2_CTRL_TYPE_STRING)
> > +             flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
> > +
> >       /* Sanity checks */
> >       if (id == 0 || name == NULL || !elem_size ||
> >           id >= V4L2_CID_PRIVATE_BASE ||
> > @@ -1896,6 +1973,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >               handler_set_err(hdl, -ERANGE);
> >               return NULL;
> >       }
> > +
> >       err = check_range(type, min, max, step, def);
> >       if (err) {
> >               handler_set_err(hdl, err);
> > @@ -1937,6 +2015,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >
> >       if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
> >               sz_extra += elem_size;
> > +     if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
> > +             sz_extra += elem_size;
> > +     if (type >= V4L2_CTRL_COMPOUND_TYPES && p_max.p_const)
> > +             sz_extra += elem_size;
> >
> >       ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
> >       if (ctrl == NULL) {
> > @@ -2002,6 +2084,22 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >               memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
> >       }
> >
> > +     if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) {
> > +             void *ptr = ctrl->p_def.p;
> > +
> > +             if (p_min.p_const) {
> > +                     ptr += elem_size;
> > +                     ctrl->p_min.p = ptr;
> > +                     memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> > +             }
> > +
> > +             if (p_max.p_const) {
> > +                     ptr += elem_size;
> > +                     ctrl->p_max.p = ptr;
> > +                     memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> > +             }
> > +     }
> > +
> >       ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
> >       cur_to_new(ctrl);
> >
> > @@ -2052,7 +2150,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
> >                       type, min, max,
> >                       is_menu ? cfg->menu_skip_mask : step, def,
> >                       cfg->dims, cfg->elem_size,
> > -                     flags, qmenu, qmenu_int, cfg->p_def, priv);
> > +                     flags, qmenu, qmenu_int, cfg->p_def, cfg->p_min,
> > +                     cfg->p_max, priv);
> >       if (ctrl)
> >               ctrl->is_private = cfg->is_private;
> >       return ctrl;
> > @@ -2077,7 +2176,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
> >       }
> >       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> >                            min, max, step, def, NULL, 0,
> > -                          flags, NULL, NULL, ptr_null, NULL);
> > +                          flags, NULL, NULL, ptr_null, ptr_null,
> > +                          ptr_null, NULL);
> >  }
> >  EXPORT_SYMBOL(v4l2_ctrl_new_std);
> >
> > @@ -2110,7 +2210,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
> >       }
> >       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> >                            0, max, mask, def, NULL, 0,
> > -                          flags, qmenu, qmenu_int, ptr_null, NULL);
> > +                          flags, qmenu, qmenu_int, ptr_null, ptr_null,
> > +                          ptr_null, NULL);
> >  }
> >  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
> >
> > @@ -2142,7 +2243,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
> >       }
> >       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> >                            0, max, mask, def, NULL, 0,
> > -                          flags, qmenu, NULL, ptr_null, NULL);
> > +                          flags, qmenu, NULL, ptr_null, ptr_null,
> > +                          ptr_null, NULL);
> >
> >  }
> >  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
> > @@ -2150,7 +2252,9 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
> >  /* Helper function for standard compound controls */
> >  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
> >                               const struct v4l2_ctrl_ops *ops, u32 id,
> > -                             const union v4l2_ctrl_ptr p_def)
> > +                             const union v4l2_ctrl_ptr p_def,
> > +                             const union v4l2_ctrl_ptr p_min,
> > +                             const union v4l2_ctrl_ptr p_max)
> >  {
> >       const char *name;
> >       enum v4l2_ctrl_type type;
> > @@ -2164,7 +2268,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
> >       }
> >       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> >                            min, max, step, def, NULL, 0,
> > -                          flags, NULL, NULL, p_def, NULL);
> > +                          flags, NULL, NULL, p_def, p_min, p_max, NULL);
> >  }
> >  EXPORT_SYMBOL(v4l2_ctrl_new_std_compound);
> >
> > @@ -2188,7 +2292,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
> >       }
> >       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> >                            0, max, 0, def, NULL, 0,
> > -                          flags, NULL, qmenu_int, ptr_null, NULL);
> > +                          flags, NULL, qmenu_int, ptr_null, ptr_null,
> > +                          ptr_null, NULL);
> >  }
> >  EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
> >
> > diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> > index 9b1de54ce379..db5bd765db3c 100644
> > --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> > +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> > @@ -884,7 +884,9 @@ static bool check_ext_ctrls(struct v4l2_ext_controls *c, unsigned long ioctl)
> >                       return false;
> >               break;
> >       case V4L2_CTRL_WHICH_DEF_VAL:
> > -             /* Default value cannot be changed */
> > +     case V4L2_CTRL_WHICH_MIN_VAL:
> > +     case V4L2_CTRL_WHICH_MAX_VAL:
> > +             /* Default, minimum or maximum value cannot be changed */
> >               if (ioctl == VIDIOC_S_EXT_CTRLS ||
> >                   ioctl == VIDIOC_TRY_EXT_CTRLS) {
> >                       c->error_idx = c->count;
> > diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> > index b0db167a3ac4..9ed7be1e696f 100644
> > --- a/include/media/v4l2-ctrls.h
> > +++ b/include/media/v4l2-ctrls.h
> > @@ -133,6 +133,8 @@ struct v4l2_ctrl_ops {
> >   *
> >   * @equal: return true if all ctrl->elems array elements are equal.
> >   * @init: initialize the value for array elements from from_idx to ctrl->elems.
> > + * @minimum: set the value to the minimum value of the control.
> > + * @maximum: set the value to the maximum value of the control.
> >   * @log: log the value.
> >   * @validate: validate the value for ctrl->new_elems array elements.
> >   *   Return 0 on success and a negative value otherwise.
> > @@ -142,6 +144,10 @@ struct v4l2_ctrl_type_ops {
> >                     union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2);
> >       void (*init)(const struct v4l2_ctrl *ctrl, u32 from_idx,
> >                    union v4l2_ctrl_ptr ptr);
> > +     void (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
> > +                     union v4l2_ctrl_ptr ptr);
> > +     void (*maximum)(const struct v4l2_ctrl *ctrl, u32 idx,
> > +                     union v4l2_ctrl_ptr ptr);
> >       void (*log)(const struct v4l2_ctrl *ctrl);
> >       int (*validate)(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr ptr);
> >  };
> > @@ -247,6 +253,12 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
> >   * @p_def:   The control's default value represented via a union which
> >   *           provides a standard way of accessing control types
> >   *           through a pointer (for compound controls only).
> > + * @p_min:   The control's minimum value represented via a union which
> > + *           provides a standard way of accessing control types
> > + *           through a pointer (for compound controls only).
> > + * @p_max:   The control's maximum value represented via a union which
> > + *           provides a standard way of accessing control types
> > + *           through a pointer (for compound controls only).
> >   * @p_cur:   The control's current value represented via a union which
> >   *           provides a standard way of accessing control types
> >   *           through a pointer.
> > @@ -306,6 +318,8 @@ struct v4l2_ctrl {
> >       } cur;
> >
> >       union v4l2_ctrl_ptr p_def;
> > +     union v4l2_ctrl_ptr p_min;
> > +     union v4l2_ctrl_ptr p_max;
> >       union v4l2_ctrl_ptr p_new;
> >       union v4l2_ctrl_ptr p_cur;
> >  };
> > @@ -425,6 +439,8 @@ struct v4l2_ctrl_handler {
> >   * @step:    The control's step value for non-menu controls.
> >   * @def:     The control's default value.
> >   * @p_def:   The control's default value for compound controls.
> > + * @p_min:   The control's minimum value for compound controls.
> > + * @p_max:   The control's maximum value for compound controls.
> >   * @dims:    The size of each dimension.
> >   * @elem_size:       The size in bytes of the control.
> >   * @flags:   The control's flags.
> > @@ -454,6 +470,8 @@ struct v4l2_ctrl_config {
> >       u64 step;
> >       s64 def;
> >       union v4l2_ctrl_ptr p_def;
> > +     union v4l2_ctrl_ptr p_min;
> > +     union v4l2_ctrl_ptr p_max;
> >       u32 dims[V4L2_CTRL_MAX_DIMS];
> >       u32 elem_size;
> >       u32 flags;
> > @@ -723,17 +741,25 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
> >   * @ops:       The control ops.
> >   * @id:        The control ID.
> >   * @p_def:     The control's default value.
> > + * @p_min:     The control's minimum value.
> > + * @p_max:     The control's maximum value.
> >   *
> > - * Sames as v4l2_ctrl_new_std(), but with support to compound controls, thanks
> > - * to the @p_def field. Use v4l2_ctrl_ptr_create() to create @p_def from a
> > - * pointer. Use v4l2_ctrl_ptr_create(NULL) if the default value of the
> > - * compound control should be all zeroes.
> > + * Same as v4l2_ctrl_new_std(), but with support for compound controls.
> > + * To fill in the @p_def, @p_min and @p_max fields, use v4l2_ctrl_ptr_create()
> > + * to convert a pointer to a const union v4l2_ctrl_ptr.
> > + * Use v4l2_ctrl_ptr_create(NULL) if you want the default, minimum or maximum
> > + * value of the compound control to be all zeroes.
> > + * If the compound control does not set the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
> > + * flag, then it does not has minimum and maximum values. In that case just use
> > + * v4l2_ctrl_ptr_create(NULL) for the @p_min and @p_max arguments.
> >   *
> >   */
> >  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
> >                                            const struct v4l2_ctrl_ops *ops,
> >                                            u32 id,
> > -                                          const union v4l2_ctrl_ptr p_def);
> > +                                          const union v4l2_ctrl_ptr p_def,
> > +                                          const union v4l2_ctrl_ptr p_min,
> > +                                          const union v4l2_ctrl_ptr p_max);
> >
> >  /**
> >   * v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control.
> > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> > index 82d86abcf89c..8fdeb5188af5 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -1866,6 +1866,8 @@ struct v4l2_ext_controls {
> >  #define V4L2_CTRL_WHICH_CUR_VAL   0
> >  #define V4L2_CTRL_WHICH_DEF_VAL   0x0f000000
> >  #define V4L2_CTRL_WHICH_REQUEST_VAL 0x0f010000
> > +#define V4L2_CTRL_WHICH_MIN_VAL   0x0f020000
> > +#define V4L2_CTRL_WHICH_MAX_VAL   0x0f030000
> >
> >  enum v4l2_ctrl_type {
> >       V4L2_CTRL_TYPE_INTEGER       = 1,
> > @@ -1973,6 +1975,7 @@ struct v4l2_querymenu {
> >  #define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE      0x0200
> >  #define V4L2_CTRL_FLAG_MODIFY_LAYOUT 0x0400
> >  #define V4L2_CTRL_FLAG_DYNAMIC_ARRAY 0x0800
> > +#define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
> >
> >  /*  Query flags, to be ORed with the control ID */
> >  #define V4L2_CTRL_FLAG_NEXT_CTRL     0x80000000
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 05/11] media: uvcvideo: Add support for compound controls
  2023-12-08 15:07   ` Laurent Pinchart
@ 2023-12-13  7:38     ` Yunke Cao
  2023-12-18  3:27       ` Laurent Pinchart
  0 siblings, 1 reply; 40+ messages in thread
From: Yunke Cao @ 2023-12-13  7:38 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Laurent,

Thank you for the review!

On Sat, Dec 9, 2023 at 12:07 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke,
>
> Thank you for the patch.
>
> On Fri, Dec 01, 2023 at 04:18:56PM +0900, Yunke Cao wrote:
> > Supports getting/setting current value.
> > Supports getting default value.
> > Handles V4L2_CTRL_FLAG_NEXT_COMPOUND.
>
> Please write a better commit message.
>
> > Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> > Changelog since v11:
> > - No change.
> > Changelog since v10:
> > - Rewrite some logic in __uvc_find_control().
> > - Remove a duplicate check in __uvc_ctrl_get_compound_cur().
> > - Thanks, Daniel!
> > Changelog since v9:
> > - Make __uvc_ctrl_set_compound() static.
> > Changelog since v8:
> > - No change.
> > Changelog since v7:
> > - Fixed comments styles, indentation and a few other style issues.
> > - Renamed uvc_g/set_array() to uvc_g/set_compound().
> > - Moved size check to __uvc_ctrl_add_mapping().
> > - After setting a new value, copy it back to user.
> > - In __uvc_ctrl_set_compound(), check size before allocating.
> >
> >  drivers/media/usb/uvc/uvc_ctrl.c | 179 +++++++++++++++++++++++++++----
> >  drivers/media/usb/uvc/uvcvideo.h |   4 +
> >  2 files changed, 164 insertions(+), 19 deletions(-)
> >
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index 98254b93eb46..aae2480496b7 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -884,6 +884,28 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
> >       }
> >  }
> >
> > +/*
> > + * Extract the byte array specified by mapping->offset and mapping->data_size
> > + * stored at 'data' to the output array 'data_out'.
> > + */
> > +static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
> > +                         u8 *data_out)
> > +{
> > +     memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Copy the byte array 'data_in' to the destination specified by mapping->offset
> > + * and mapping->data_size stored at 'data'.
> > + */
> > +static int uvc_set_compound(struct uvc_control_mapping *mapping,
> > +                         const u8 *data_in, u8 *data)
> > +{
> > +     memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
> > +     return 0;
> > +}
> > +
>
> I may be missing something, but isn't this essentially dead code ? This
> series adds support for a single compound control, with custom get/set
> handlers, so these two default handlers will never be called.
>
> I don't think it makes sense to have default handlers for compound
> controls, I don't foresee any use case where a simple mempcy() will do
> the right thing.
>

Yes, they are dead code. I thought they would be useful for some
controls. I will remove them in v15.

> >  static bool
> >  uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
> >  {
> > @@ -906,7 +928,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
> >
> >  static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
> >       struct uvc_control_mapping **mapping, struct uvc_control **control,
> > -     int next)
> > +     int next, int next_compound)
> >  {
> >       struct uvc_control *ctrl;
> >       struct uvc_control_mapping *map;
> > @@ -921,14 +943,16 @@ static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
> >                       continue;
> >
> >               list_for_each_entry(map, &ctrl->info.mappings, list) {
> > -                     if ((map->id == v4l2_id) && !next) {
> > +                     if (map->id == v4l2_id && !next && !next_compound) {
> >                               *control = ctrl;
> >                               *mapping = map;
> >                               return;
> >                       }
> >
> >                       if ((*mapping == NULL || (*mapping)->id > map->id) &&
> > -                         (map->id > v4l2_id) && next) {
> > +                         (map->id > v4l2_id) &&
> > +                         (uvc_ctrl_mapping_is_compound(map) ?
> > +                          next_compound : next)) {
> >                               *control = ctrl;
> >                               *mapping = map;
> >                       }
> > @@ -942,6 +966,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
> >       struct uvc_control *ctrl = NULL;
> >       struct uvc_entity *entity;
> >       int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> > +     int next_compound = v4l2_id & V4L2_CTRL_FLAG_NEXT_COMPOUND;
> >
> >       *mapping = NULL;
> >
> > @@ -950,12 +975,13 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
> >
> >       /* Find the control. */
> >       list_for_each_entry(entity, &chain->entities, chain) {
> > -             __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
> > -             if (ctrl && !next)
> > +             __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next,
> > +                                next_compound);
> > +             if (ctrl && !next && !next_compound)
> >                       return ctrl;
> >       }
> >
> > -     if (ctrl == NULL && !next)
> > +     if (!ctrl && !next && !next_compound)
> >               uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
> >                       v4l2_id);
> >
> > @@ -1101,12 +1127,59 @@ static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
> >       return 0;
> >  }
> >
> > +static int __uvc_ctrl_get_compound(struct uvc_control_mapping *mapping,
> > +                                struct uvc_control *ctrl,
> > +                                int id,
> > +                                struct v4l2_ext_control *xctrl)
> > +{
> > +     u8 size;
> > +     u8 *data;
> > +     int ret;
> > +
> > +     size = mapping->v4l2_size / 8;
> > +     if (xctrl->size < size) {
> > +             xctrl->size = size;
> > +             return -ENOSPC;
> > +     }
> > +
> > +     data = kmalloc(size, GFP_KERNEL);
> > +     if (!data)
> > +             return -ENOMEM;
> > +
> > +     ret = mapping->get_compound(mapping, uvc_ctrl_data(ctrl, id), data);
> > +     if (ret < 0)
> > +             goto out;
> > +
> > +     ret = copy_to_user(xctrl->ptr, data, size) ? -EFAULT : 0;
> > +
> > +out:
> > +     kfree(data);
> > +     return ret;
> > +}
> > +
> > +static int __uvc_ctrl_get_compound_cur(struct uvc_video_chain *chain,
> > +                                    struct uvc_control *ctrl,
> > +                                    struct uvc_control_mapping *mapping,
> > +                                    struct v4l2_ext_control *xctrl)
> > +{
> > +     int ret;
> > +
> > +     ret = __uvc_ctrl_load_cur(chain, ctrl);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT,
> > +                                    xctrl);
> > +}
> > +
> >  static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
> >                                 u32 found_id)
> >  {
> > -     bool find_next = req_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> >       unsigned int i;
> >
> > +     bool find_next = req_id &
> > +             (V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND);
> > +
> >       req_id &= V4L2_CTRL_ID_MASK;
> >
> >       for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
> > @@ -1194,7 +1267,7 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
> >       }
> >
> >       __uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
> > -                        &master_ctrl, 0);
> > +                        &master_ctrl, 0, 0);
> >
> >       if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
> >               return 0;
> > @@ -1262,7 +1335,7 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> >
> >       if (mapping->master_id)
> >               __uvc_find_control(ctrl->entity, mapping->master_id,
> > -                                &master_map, &master_ctrl, 0);
> > +                                &master_map, &master_ctrl, 0, 0);
> >       if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
> >               s32 val = 0;
> >               int ret;
> > @@ -1278,6 +1351,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> >                               v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
> >       }
> >
> > +     if (v4l2_ctrl->type >= V4L2_CTRL_COMPOUND_TYPES) {
> > +             v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
> > +             v4l2_ctrl->default_value = 0;
> > +             v4l2_ctrl->minimum = 0;
> > +             v4l2_ctrl->maximum = 0;
> > +             v4l2_ctrl->step = 0;
> > +             return 0;
> > +     }
> > +
> >       if (!ctrl->cached) {
> >               int ret = uvc_ctrl_populate_cache(chain, ctrl);
> >               if (ret < 0)
> > @@ -1533,7 +1615,7 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
> >       u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
> >       s32 val = 0;
> >
> > -     __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
> > +     __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0, 0);
> >       if (ctrl == NULL)
> >               return;
> >
> > @@ -1843,7 +1925,7 @@ static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
> >
> >       for (i = 0; i < ctrls->count; i++) {
> >               __uvc_find_control(entity, ctrls->controls[i].id, &mapping,
> > -                                &ctrl_found, 0);
> > +                                &ctrl_found, 0, 0);
> >               if (uvc_control == ctrl_found)
> >                       return i;
> >       }
> > @@ -1891,7 +1973,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
> >               return -EINVAL;
> >
> >       if (uvc_ctrl_mapping_is_compound(mapping))
> > -             return -EINVAL;
> > +             return __uvc_ctrl_get_compound_cur(chain, ctrl, mapping, xctrl);
> >       else
> >               return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
> >  }
> > @@ -1912,6 +1994,22 @@ static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
> >       return 0;
> >  }
> >
> > +static int __uvc_ctrl_get_boundary_compound(struct uvc_video_chain *chain,
> > +                                         struct uvc_control *ctrl,
> > +                                         struct uvc_control_mapping *mapping,
> > +                                         struct v4l2_ext_control *xctrl)
> > +{
> > +     int ret;
> > +
> > +     if (!ctrl->cached) {
> > +             ret = uvc_ctrl_populate_cache(chain, ctrl);
> > +             if (ret < 0)
> > +                     return ret;
> > +     }
> > +
> > +     return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_DEF, xctrl);
> > +}
> > +
> >  int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> >                         struct v4l2_ext_control *xctrl)
> >  {
> > @@ -1929,7 +2027,8 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> >       }
> >
> >       if (uvc_ctrl_mapping_is_compound(mapping))
> > -             ret = -EINVAL;
> > +             ret = __uvc_ctrl_get_boundary_compound(chain, ctrl, mapping,
> > +                                                    xctrl);
> >       else
> >               ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
> >
> > @@ -1938,6 +2037,34 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> >       return ret;
> >  }
> >
> > +static int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> > +                                struct v4l2_ext_control *xctrl,
> > +                                struct uvc_control *ctrl)
> > +{
> > +     u8 *data;
> > +     int ret;
> > +
> > +     if (xctrl->size != mapping->v4l2_size / 8)
> > +             return -EINVAL;
> > +
> > +     data = kmalloc(xctrl->size, GFP_KERNEL);
> > +     if (!data)
> > +             return -ENOMEM;
> > +
> > +     ret = copy_from_user(data, xctrl->ptr, xctrl->size);
> > +     if (ret < 0)
> > +             goto out;
> > +
> > +     ret = mapping->set_compound(mapping, data,
> > +                     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> > +
> > +     __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT, xctrl);
>
> Why do you need to call __uvc_ctrl_get_compound() here ?
>

I was trying to copy the clamped control value back to the user.
I guess we should do it in uvc_ctrl_set() after the refactor.

> > +
> > +out:
> > +     kfree(data);
> > +     return ret;
> > +}
> > +
> >  int uvc_ctrl_set(struct uvc_fh *handle,
> >       struct v4l2_ext_control *xctrl)
> >  {
> > @@ -2052,13 +2179,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
> >                      ctrl->info.size);
> >       }
> >
> > -     if (uvc_ctrl_mapping_is_compound(mapping))
> > -             return -EINVAL;
> > -     else
> > +     if (uvc_ctrl_mapping_is_compound(mapping)) {
> > +             ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
> > +             if (ret < 0)
> > +                     return ret;
> > +     } else
> >               mapping->set(mapping, value,
> >                            uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
>
> I really don't like how handling of compound controls is scattered
> everywhere :-( That makes the code difficult to read, and thus more
> error-prone.
>
> Could this be refactored to simplify the implementation ? In particular,
> I'm thinking about
>
> - Moving the copy_from_user() and copy_to_user() towards the top of the
>   call stack, directly in uvc_ctrl_get(), uvc_ctrl_get_boundary() and
>   uvc_ctrl_set()
>

Sorry, I'm not sure I understand it.
For the uvc_ctrl_get() case, do you mean we should get rid of
__uvc_ctrl_get_compound_cur()
and call __uvc_ctrl_load_cur(), mapping->get() and copy_to_user()
directly in uvc_ctrl_get()?

Best,
Yunke


> - Merging the .[gs]et() and .[gs]et_compound() functions (see below)
>
> - Moving the value clamping from uvc_set_compound_rect() to
>   uvc_ctrl_set(), with the rest of the clamping code
>
> >
> > -
> >       if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
> >               ctrl->handle = handle;
> >
> > @@ -2468,10 +2596,23 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> >                       goto err_nomem;
> >       }
> >
> > -     if (map->get == NULL)
> > +     if (uvc_ctrl_mapping_is_compound(map)) {
> > +             if (map->data_size != map->v4l2_size)
> > +                     return -EINVAL;
>
> If the two values have to be the same, why do we have two fields ?
>
> > +
> > +             /* 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))
> >               map->get = uvc_get_le_value;
> > -     if (map->set == NULL)
> > +     if (!map->set && !uvc_ctrl_mapping_is_compound(map))
> >               map->set = uvc_set_le_value;
> > +     if (!map->get_compound && uvc_ctrl_mapping_is_compound(map))
> > +             map->get_compound = uvc_get_compound;
> > +     if (!map->set_compound && uvc_ctrl_mapping_is_compound(map))
> > +             map->set_compound = uvc_set_compound;
> >
> >       for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
> >               if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index 7bc41270ed94..11805b729c22 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -129,8 +129,12 @@ struct uvc_control_mapping {
> >
> >       s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
> >                  const u8 *data);
> > +     int (*get_compound)(struct uvc_control_mapping *mapping, const u8 *data,
> > +                         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);
>
> Instead of adding new functions, I think we could modify the existing
> .get() and .set() handlers to be usable for compound controls.
>
>         int (*get)(struct uvc_control_mapping *mapping, u8 query,
>                    const u8 *data_in, void *data_out);
>         void (*set)(struct uvc_control_mapping *mapping, const void *data_in,
>                     u8 *data_out);
>
> For additional safety, you could pass the size of the void * buffer to
> the functions.
>
> >  };
> >
> >  struct uvc_control {
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-12-01  7:18 ` [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
                     ` (4 preceding siblings ...)
  2023-12-08 15:20   ` Laurent Pinchart
@ 2023-12-14 17:34   ` kernel test robot
  5 siblings, 0 replies; 40+ messages in thread
From: kernel test robot @ 2023-12-14 17:34 UTC (permalink / raw)
  To: Yunke Cao, Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: oe-kbuild-all, Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda,
	linux-media, Yunke Cao

Hi Yunke,

kernel test robot noticed the following build warnings:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on usb/usb-next usb/usb-linus linus/master v6.7-rc5 next-20231214]
[cannot apply to media-tree/master linuxtv-media-stage/master sailus-media-tree/streams]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Yunke-Cao/media-v4l2_ctrl-Add-V4L2_CTRL_TYPE_RECT/20231201-152501
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link:    https://lore.kernel.org/r/20231201071907.3080126-7-yunkec%40google.com
patch subject: [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
config: alpha-randconfig-r122-20231202 (https://download.01.org/0day-ci/archive/20231215/202312150147.Kl7pyrrG-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 13.2.0
reproduce: (https://download.01.org/0day-ci/archive/20231215/202312150147.Kl7pyrrG-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202312150147.Kl7pyrrG-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/media/v4l2-core/v4l2-ctrls-core.c:309:6: sparse: sparse: symbol 'v4l2_ctrl_type_op_minimum' was not declared. Should it be static?
>> drivers/media/v4l2-core/v4l2-ctrls-core.c:315:6: sparse: sparse: symbol 'v4l2_ctrl_type_op_maximum' was not declared. Should it be static?
   drivers/media/v4l2-core/v4l2-ctrls-core.c: note: in included file (through include/linux/preempt.h, include/linux/spinlock.h, include/linux/mmzone.h, ...):
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true

vim +/v4l2_ctrl_type_op_minimum +309 drivers/media/v4l2-core/v4l2-ctrls-core.c

   308	
 > 309	void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
   310				       union v4l2_ctrl_ptr ptr)
   311	{
   312		__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
   313	}
   314	
 > 315	void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
   316				       union v4l2_ctrl_ptr ptr)
   317	{
   318		__v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
   319	}
   320	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size
  2023-12-12  7:59     ` Yunke Cao
@ 2023-12-18  3:17       ` Laurent Pinchart
  0 siblings, 0 replies; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-18  3:17 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

On Tue, Dec 12, 2023 at 04:59:59PM +0900, Yunke Cao wrote:
> On Fri, Dec 8, 2023 at 11:15 PM Laurent Pinchart wrote:
> > On Fri, Dec 01, 2023 at 04:18:55PM +0900, Yunke Cao wrote:
> > > Rename the existing size to data_size to represent uvc control data size,
> > > add a separate field for v4l2 control size. v4l2 control size will be
> > > used the compound controls.
> >
> > s/uvc/UVC/ and s/v4l2/V4L2/ in the whole commit message.
> >
> > > Also modify the uvc driver documents to clarify the size in
> > > uvc_xu_control_mapping corresponds to the uvc control data size.
> > >
> > > Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
> > > Signed-off-by: Yunke Cao <yunkec@google.com>
> > > ---
> > > Changelog since v11:
> > > - No change.
> > > Changelog since v10:
> > > - Added Reviewed-by from Daniel Scally.
> > > Changelog since v9:
> > > - No change.
> > > Changelog since v8:
> > > - No change.
> > > Changelog since v7:
> > > - Newly added patch.
> > >
> > >  .../userspace-api/media/drivers/uvcvideo.rst  |  2 +-
> > >  drivers/media/usb/uvc/uvc_ctrl.c              | 80 +++++++++----------
> > >  drivers/media/usb/uvc/uvc_v4l2.c              |  2 +-
> > >  drivers/media/usb/uvc/uvcvideo.h              |  6 +-
> > >  4 files changed, 47 insertions(+), 43 deletions(-)
> > >
> > > diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > > index a290f9fadae9..aab4304e6bb5 100644
> > > --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > > +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > > @@ -157,7 +157,7 @@ Argument: struct uvc_xu_control_mapping
> > >       __u8    name[32]        V4L2 control name
> > >       __u8    entity[16]      UVC extension unit GUID
> > >       __u8    selector        UVC control selector
> > > -     __u8    size            V4L2 control size (in bits)
> > > +     __u8    size            UVC control data size (in bits)
> >
> > The V4L2 and UVC sizes are identical for all controls mapped through
> > this mechanism, right ?
> 
> Yes, I think that's right in the current implementation.
> Do you think we need to support UVCIOC_CTRL_MAP for the compound controls?

No, I wouldn't do so. When I introduced the UVCIOC_CTRL_MAP ioctl, I was
envisioning vendors submitting descriptions for a large number of XU
controls. I thought it would be inconvenient to constantly update the
uvcvideo driver with new mappings.

Years have gone by, and the tsunami of XU control mappings never came.
In restrospect it may have been better to simply add mappings to the
kernel driver (or maybe today we would use BPF, it seems to be popular).
I wouldn't extend the mechanism to support compound controls unless
there's a reason to believe the situation will change and lots of XU
compound controls will need to be mapped.

> I wanted to change this because simply we assign
> +     map->data_size = xmap->size;
> in uvc_ioctl_xu_ctrl_map().
> 
> Is this okay?

I'm fine with that. Maybe the comment could state

	V4L2 and UVC control data size (in bits)

?

> > >       __u8    offset          V4L2 control offset (in bits)
> >
> > If the size if the "UVC control data size", shouldn't this be the "UVC
> > control data offset" ?
> 
> Ah, right. I will change this in the v15 if we keep the "UVC control data size".
> 
> > >       enum v4l2_ctrl_type
> > >               v4l2_type       V4L2 control type
> > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > index 4a685532f7eb..98254b93eb46 100644
> > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > @@ -464,7 +464,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_BRIGHTNESS,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_BRIGHTNESS_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > > @@ -473,7 +473,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_CONTRAST,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_CONTRAST_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -482,7 +482,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_HUE,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_HUE_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > > @@ -493,7 +493,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_SATURATION,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_SATURATION_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -502,7 +502,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_SHARPNESS,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_SHARPNESS_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -511,7 +511,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_GAMMA,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_GAMMA_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -520,7 +520,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_BACKLIGHT_COMPENSATION,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -529,7 +529,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_GAIN,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_GAIN_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -538,7 +538,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_HUE_AUTO,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_HUE_AUTO_CONTROL,
> > > -             .size           = 1,
> > > +             .data_size      = 1,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > > @@ -548,7 +548,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_EXPOSURE_AUTO,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_AE_MODE_CONTROL,
> > > -             .size           = 4,
> > > +             .data_size      = 4,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_MENU,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_BITMASK,
> > > @@ -561,7 +561,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_AE_PRIORITY_CONTROL,
> > > -             .size           = 1,
> > > +             .data_size      = 1,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > > @@ -570,7 +570,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_EXPOSURE_ABSOLUTE,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
> > > -             .size           = 32,
> > > +             .data_size      = 32,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -581,7 +581,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_AUTO_WHITE_BALANCE,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
> > > -             .size           = 1,
> > > +             .data_size      = 1,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > > @@ -591,7 +591,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -602,7 +602,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_AUTO_WHITE_BALANCE,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
> > > -             .size           = 1,
> > > +             .data_size      = 1,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > > @@ -613,7 +613,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_BLUE_BALANCE,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > > @@ -624,7 +624,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_RED_BALANCE,
> > >               .entity         = UVC_GUID_UVC_PROCESSING,
> > >               .selector       = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 16,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > > @@ -635,7 +635,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_FOCUS_ABSOLUTE,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -646,7 +646,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_FOCUS_AUTO,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_FOCUS_AUTO_CONTROL,
> > > -             .size           = 1,
> > > +             .data_size      = 1,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > > @@ -656,7 +656,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_IRIS_ABSOLUTE,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_IRIS_ABSOLUTE_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -665,7 +665,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_IRIS_RELATIVE,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_IRIS_RELATIVE_CONTROL,
> > > -             .size           = 8,
> > > +             .data_size      = 8,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > > @@ -674,7 +674,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_ZOOM_ABSOLUTE,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
> > > @@ -683,7 +683,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_ZOOM_CONTINUOUS,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_ZOOM_RELATIVE_CONTROL,
> > > -             .size           = 0,
> > > +             .data_size      = 0,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > > @@ -694,7 +694,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_PAN_ABSOLUTE,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
> > > -             .size           = 32,
> > > +             .data_size      = 32,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > > @@ -703,7 +703,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_TILT_ABSOLUTE,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
> > > -             .size           = 32,
> > > +             .data_size      = 32,
> > >               .offset         = 32,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > > @@ -712,7 +712,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_PAN_SPEED,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_PANTILT_RELATIVE_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > > @@ -723,7 +723,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_TILT_SPEED,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_PANTILT_RELATIVE_CONTROL,
> > > -             .size           = 16,
> > > +             .data_size      = 16,
> > >               .offset         = 16,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
> > > @@ -734,7 +734,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_PRIVACY,
> > >               .entity         = UVC_GUID_UVC_CAMERA,
> > >               .selector       = UVC_CT_PRIVACY_CONTROL,
> > > -             .size           = 1,
> > > +             .data_size      = 1,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > > @@ -743,7 +743,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .id             = V4L2_CID_PRIVACY,
> > >               .entity         = UVC_GUID_EXT_GPIO_CONTROLLER,
> > >               .selector       = UVC_CT_PRIVACY_CONTROL,
> > > -             .size           = 1,
> > > +             .data_size      = 1,
> > >               .offset         = 0,
> > >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > > @@ -754,7 +754,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
> > >       .id             = V4L2_CID_POWER_LINE_FREQUENCY,
> > >       .entity         = UVC_GUID_UVC_PROCESSING,
> > >       .selector       = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
> > > -     .size           = 2,
> > > +     .data_size      = 2,
> > >       .offset         = 0,
> > >       .v4l2_type      = V4L2_CTRL_TYPE_MENU,
> > >       .data_type      = UVC_CTRL_DATA_TYPE_ENUM,
> > > @@ -766,7 +766,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
> > >       .id             = V4L2_CID_POWER_LINE_FREQUENCY,
> > >       .entity         = UVC_GUID_UVC_PROCESSING,
> > >       .selector       = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
> > > -     .size           = 2,
> > > +     .data_size      = 2,
> > >       .offset         = 0,
> > >       .v4l2_type      = V4L2_CTRL_TYPE_MENU,
> > >       .data_type      = UVC_CTRL_DATA_TYPE_ENUM,
> > > @@ -783,7 +783,7 @@ static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
> > >       .id             = V4L2_CID_POWER_LINE_FREQUENCY,
> > >       .entity         = UVC_GUID_UVC_PROCESSING,
> > >       .selector       = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
> > > -     .size           = 2,
> > > +     .data_size      = 2,
> > >       .offset         = 0,
> > >       .v4l2_type      = V4L2_CTRL_TYPE_MENU,
> > >       .data_type      = UVC_CTRL_DATA_TYPE_ENUM,
> > > @@ -816,7 +816,7 @@ static inline void uvc_clear_bit(u8 *data, int bit)
> > >  }
> > >
> > >  /*
> > > - * Extract the bit string specified by mapping->offset and mapping->size
> > > + * Extract the bit string specified by mapping->offset and mapping->data_size
> > >   * from the little-endian data stored at 'data' and return the result as
> > >   * a signed 32bit integer. Sign extension will be performed if the mapping
> > >   * references a signed data type.
> > > @@ -824,7 +824,7 @@ static inline void uvc_clear_bit(u8 *data, int bit)
> > >  static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
> > >       u8 query, const u8 *data)
> > >  {
> > > -     int bits = mapping->size;
> > > +     int bits = mapping->data_size;
> > >       int offset = mapping->offset;
> > >       s32 value = 0;
> > >       u8 mask;
> > > @@ -847,19 +847,19 @@ static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
> > >
> > >       /* Sign-extend the value if needed. */
> > >       if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
> > > -             value |= -(value & (1 << (mapping->size - 1)));
> > > +             value |= -(value & (1 << (mapping->data_size - 1)));
> > >
> > >       return value;
> > >  }
> > >
> > >  /*
> > > - * Set the bit string specified by mapping->offset and mapping->size
> > > + * Set the bit string specified by mapping->offset and mapping->data_size
> > >   * in the little-endian data stored at 'data' to the value 'value'.
> > >   */
> > >  static void uvc_set_le_value(struct uvc_control_mapping *mapping,
> > >       s32 value, u8 *data)
> > >  {
> > > -     int bits = mapping->size;
> > > +     int bits = mapping->data_size;
> > >       int offset = mapping->offset;
> > >       u8 mask;
> > >
> > > @@ -2039,7 +2039,7 @@ int uvc_ctrl_set(struct uvc_fh *handle,
> > >        * needs to be loaded from the device to perform the read-modify-write
> > >        * operation.
> > >        */
> > > -     if ((ctrl->info.size * 8) != mapping->size) {
> > > +     if ((ctrl->info.size * 8) != mapping->data_size) {
> > >               ret = __uvc_ctrl_load_cur(chain, ctrl);
> > >               if (ret < 0)
> > >                       return ret;
> > > @@ -2546,8 +2546,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> > >       }
> > >
> > >       /* Validate the user-provided bit-size and offset */
> > > -     if (mapping->size > 32 ||
> > > -         mapping->offset + mapping->size > ctrl->info.size * 8) {
> > > +     if (mapping->data_size > 32 ||
> > > +         mapping->offset + mapping->data_size > ctrl->info.size * 8) {
> > >               ret = -EINVAL;
> > >               goto done;
> > >       }
> > > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> > > index 5a88847bfbfe..ff72caf7bc9e 100644
> > > --- a/drivers/media/usb/uvc/uvc_v4l2.c
> > > +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> > > @@ -122,7 +122,7 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
> > >       }
> > >       memcpy(map->entity, xmap->entity, sizeof(map->entity));
> > >       map->selector = xmap->selector;
> > > -     map->size = xmap->size;
> > > +     map->data_size = xmap->size;
> > >       map->offset = xmap->offset;
> > >       map->v4l2_type = xmap->v4l2_type;
> > >       map->data_type = xmap->data_type;
> > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > index 5091085fcfb0..7bc41270ed94 100644
> > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > @@ -110,7 +110,11 @@ struct uvc_control_mapping {
> > >       u8 entity[16];
> > >       u8 selector;
> > >
> > > -     u8 size;
> > > +     /* Size of the v4l2 control. Required for compound controls. */
> > > +     u8 v4l2_size;
> >
> > Let's introduce this field in the patch that uses it. The commit message
> > needs to be updated to explain that this patch renames the size field to
> > data_size to prepare for adding another size field for compound
> > controls.
> 
> Sounds good.
> 
> > > +     /* UVC data size. Required for all controls. */
> >
> > "UVC data size" is not very clear. Let me attempt to write a more
> > precise description:
> >
> >         /*
> >          * Size of the control data in the payload of the UVC control GET and
> >          * SET requests, expressed in bits.
> >          */
> >
> > Is this correct ?
> 
> Yes, that sounds much better indeed.
> 
> > > +     u8 data_size;
> > > +
> > >       u8 offset;
> > >       enum v4l2_ctrl_type v4l2_type;
> > >       u32 data_type;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 05/11] media: uvcvideo: Add support for compound controls
  2023-12-13  7:38     ` Yunke Cao
@ 2023-12-18  3:27       ` Laurent Pinchart
  2023-12-20  2:28         ` Yunke Cao
  0 siblings, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-18  3:27 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

On Wed, Dec 13, 2023 at 04:38:40PM +0900, Yunke Cao wrote:
> On Sat, Dec 9, 2023 at 12:07 AM Laurent Pinchart wrote:
> > On Fri, Dec 01, 2023 at 04:18:56PM +0900, Yunke Cao wrote:
> > > Supports getting/setting current value.
> > > Supports getting default value.
> > > Handles V4L2_CTRL_FLAG_NEXT_COMPOUND.
> >
> > Please write a better commit message.
> >
> > > Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> > > Signed-off-by: Yunke Cao <yunkec@google.com>
> > > ---
> > > Changelog since v11:
> > > - No change.
> > > Changelog since v10:
> > > - Rewrite some logic in __uvc_find_control().
> > > - Remove a duplicate check in __uvc_ctrl_get_compound_cur().
> > > - Thanks, Daniel!
> > > Changelog since v9:
> > > - Make __uvc_ctrl_set_compound() static.
> > > Changelog since v8:
> > > - No change.
> > > Changelog since v7:
> > > - Fixed comments styles, indentation and a few other style issues.
> > > - Renamed uvc_g/set_array() to uvc_g/set_compound().
> > > - Moved size check to __uvc_ctrl_add_mapping().
> > > - After setting a new value, copy it back to user.
> > > - In __uvc_ctrl_set_compound(), check size before allocating.
> > >
> > >  drivers/media/usb/uvc/uvc_ctrl.c | 179 +++++++++++++++++++++++++++----
> > >  drivers/media/usb/uvc/uvcvideo.h |   4 +
> > >  2 files changed, 164 insertions(+), 19 deletions(-)
> > >
> > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > index 98254b93eb46..aae2480496b7 100644
> > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > @@ -884,6 +884,28 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
> > >       }
> > >  }
> > >
> > > +/*
> > > + * Extract the byte array specified by mapping->offset and mapping->data_size
> > > + * stored at 'data' to the output array 'data_out'.
> > > + */
> > > +static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
> > > +                         u8 *data_out)
> > > +{
> > > +     memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
> > > +     return 0;
> > > +}
> > > +
> > > +/*
> > > + * Copy the byte array 'data_in' to the destination specified by mapping->offset
> > > + * and mapping->data_size stored at 'data'.
> > > + */
> > > +static int uvc_set_compound(struct uvc_control_mapping *mapping,
> > > +                         const u8 *data_in, u8 *data)
> > > +{
> > > +     memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
> > > +     return 0;
> > > +}
> > > +
> >
> > I may be missing something, but isn't this essentially dead code ? This
> > series adds support for a single compound control, with custom get/set
> > handlers, so these two default handlers will never be called.
> >
> > I don't think it makes sense to have default handlers for compound
> > controls, I don't foresee any use case where a simple mempcy() will do
> > the right thing.
> 
> Yes, they are dead code. I thought they would be useful for some
> controls. I will remove them in v15.
> 
> > >  static bool
> > >  uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
> > >  {
> > > @@ -906,7 +928,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
> > >
> > >  static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
> > >       struct uvc_control_mapping **mapping, struct uvc_control **control,
> > > -     int next)
> > > +     int next, int next_compound)
> > >  {
> > >       struct uvc_control *ctrl;
> > >       struct uvc_control_mapping *map;
> > > @@ -921,14 +943,16 @@ static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
> > >                       continue;
> > >
> > >               list_for_each_entry(map, &ctrl->info.mappings, list) {
> > > -                     if ((map->id == v4l2_id) && !next) {
> > > +                     if (map->id == v4l2_id && !next && !next_compound) {
> > >                               *control = ctrl;
> > >                               *mapping = map;
> > >                               return;
> > >                       }
> > >
> > >                       if ((*mapping == NULL || (*mapping)->id > map->id) &&
> > > -                         (map->id > v4l2_id) && next) {
> > > +                         (map->id > v4l2_id) &&
> > > +                         (uvc_ctrl_mapping_is_compound(map) ?
> > > +                          next_compound : next)) {
> > >                               *control = ctrl;
> > >                               *mapping = map;
> > >                       }
> > > @@ -942,6 +966,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
> > >       struct uvc_control *ctrl = NULL;
> > >       struct uvc_entity *entity;
> > >       int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> > > +     int next_compound = v4l2_id & V4L2_CTRL_FLAG_NEXT_COMPOUND;
> > >
> > >       *mapping = NULL;
> > >
> > > @@ -950,12 +975,13 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
> > >
> > >       /* Find the control. */
> > >       list_for_each_entry(entity, &chain->entities, chain) {
> > > -             __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
> > > -             if (ctrl && !next)
> > > +             __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next,
> > > +                                next_compound);
> > > +             if (ctrl && !next && !next_compound)
> > >                       return ctrl;
> > >       }
> > >
> > > -     if (ctrl == NULL && !next)
> > > +     if (!ctrl && !next && !next_compound)
> > >               uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
> > >                       v4l2_id);
> > >
> > > @@ -1101,12 +1127,59 @@ static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
> > >       return 0;
> > >  }
> > >
> > > +static int __uvc_ctrl_get_compound(struct uvc_control_mapping *mapping,
> > > +                                struct uvc_control *ctrl,
> > > +                                int id,
> > > +                                struct v4l2_ext_control *xctrl)
> > > +{
> > > +     u8 size;
> > > +     u8 *data;
> > > +     int ret;
> > > +
> > > +     size = mapping->v4l2_size / 8;
> > > +     if (xctrl->size < size) {
> > > +             xctrl->size = size;
> > > +             return -ENOSPC;
> > > +     }
> > > +
> > > +     data = kmalloc(size, GFP_KERNEL);
> > > +     if (!data)
> > > +             return -ENOMEM;
> > > +
> > > +     ret = mapping->get_compound(mapping, uvc_ctrl_data(ctrl, id), data);
> > > +     if (ret < 0)
> > > +             goto out;
> > > +
> > > +     ret = copy_to_user(xctrl->ptr, data, size) ? -EFAULT : 0;
> > > +
> > > +out:
> > > +     kfree(data);
> > > +     return ret;
> > > +}
> > > +
> > > +static int __uvc_ctrl_get_compound_cur(struct uvc_video_chain *chain,
> > > +                                    struct uvc_control *ctrl,
> > > +                                    struct uvc_control_mapping *mapping,
> > > +                                    struct v4l2_ext_control *xctrl)
> > > +{
> > > +     int ret;
> > > +
> > > +     ret = __uvc_ctrl_load_cur(chain, ctrl);
> > > +     if (ret < 0)
> > > +             return ret;
> > > +
> > > +     return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT,
> > > +                                    xctrl);
> > > +}
> > > +
> > >  static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
> > >                                 u32 found_id)
> > >  {
> > > -     bool find_next = req_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> > >       unsigned int i;
> > >
> > > +     bool find_next = req_id &
> > > +             (V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND);
> > > +
> > >       req_id &= V4L2_CTRL_ID_MASK;
> > >
> > >       for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
> > > @@ -1194,7 +1267,7 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
> > >       }
> > >
> > >       __uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
> > > -                        &master_ctrl, 0);
> > > +                        &master_ctrl, 0, 0);
> > >
> > >       if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
> > >               return 0;
> > > @@ -1262,7 +1335,7 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> > >
> > >       if (mapping->master_id)
> > >               __uvc_find_control(ctrl->entity, mapping->master_id,
> > > -                                &master_map, &master_ctrl, 0);
> > > +                                &master_map, &master_ctrl, 0, 0);
> > >       if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
> > >               s32 val = 0;
> > >               int ret;
> > > @@ -1278,6 +1351,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> > >                               v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
> > >       }
> > >
> > > +     if (v4l2_ctrl->type >= V4L2_CTRL_COMPOUND_TYPES) {
> > > +             v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
> > > +             v4l2_ctrl->default_value = 0;
> > > +             v4l2_ctrl->minimum = 0;
> > > +             v4l2_ctrl->maximum = 0;
> > > +             v4l2_ctrl->step = 0;
> > > +             return 0;
> > > +     }
> > > +
> > >       if (!ctrl->cached) {
> > >               int ret = uvc_ctrl_populate_cache(chain, ctrl);
> > >               if (ret < 0)
> > > @@ -1533,7 +1615,7 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
> > >       u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
> > >       s32 val = 0;
> > >
> > > -     __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
> > > +     __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0, 0);
> > >       if (ctrl == NULL)
> > >               return;
> > >
> > > @@ -1843,7 +1925,7 @@ static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
> > >
> > >       for (i = 0; i < ctrls->count; i++) {
> > >               __uvc_find_control(entity, ctrls->controls[i].id, &mapping,
> > > -                                &ctrl_found, 0);
> > > +                                &ctrl_found, 0, 0);
> > >               if (uvc_control == ctrl_found)
> > >                       return i;
> > >       }
> > > @@ -1891,7 +1973,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
> > >               return -EINVAL;
> > >
> > >       if (uvc_ctrl_mapping_is_compound(mapping))
> > > -             return -EINVAL;
> > > +             return __uvc_ctrl_get_compound_cur(chain, ctrl, mapping, xctrl);
> > >       else
> > >               return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
> > >  }
> > > @@ -1912,6 +1994,22 @@ static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
> > >       return 0;
> > >  }
> > >
> > > +static int __uvc_ctrl_get_boundary_compound(struct uvc_video_chain *chain,
> > > +                                         struct uvc_control *ctrl,
> > > +                                         struct uvc_control_mapping *mapping,
> > > +                                         struct v4l2_ext_control *xctrl)
> > > +{
> > > +     int ret;
> > > +
> > > +     if (!ctrl->cached) {
> > > +             ret = uvc_ctrl_populate_cache(chain, ctrl);
> > > +             if (ret < 0)
> > > +                     return ret;
> > > +     }
> > > +
> > > +     return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_DEF, xctrl);
> > > +}
> > > +
> > >  int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> > >                         struct v4l2_ext_control *xctrl)
> > >  {
> > > @@ -1929,7 +2027,8 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> > >       }
> > >
> > >       if (uvc_ctrl_mapping_is_compound(mapping))
> > > -             ret = -EINVAL;
> > > +             ret = __uvc_ctrl_get_boundary_compound(chain, ctrl, mapping,
> > > +                                                    xctrl);
> > >       else
> > >               ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
> > >
> > > @@ -1938,6 +2037,34 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> > >       return ret;
> > >  }
> > >
> > > +static int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> > > +                                struct v4l2_ext_control *xctrl,
> > > +                                struct uvc_control *ctrl)
> > > +{
> > > +     u8 *data;
> > > +     int ret;
> > > +
> > > +     if (xctrl->size != mapping->v4l2_size / 8)
> > > +             return -EINVAL;
> > > +
> > > +     data = kmalloc(xctrl->size, GFP_KERNEL);
> > > +     if (!data)
> > > +             return -ENOMEM;
> > > +
> > > +     ret = copy_from_user(data, xctrl->ptr, xctrl->size);
> > > +     if (ret < 0)
> > > +             goto out;
> > > +
> > > +     ret = mapping->set_compound(mapping, data,
> > > +                     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> > > +
> > > +     __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT, xctrl);
> >
> > Why do you need to call __uvc_ctrl_get_compound() here ?
> 
> I was trying to copy the clamped control value back to the user.
> I guess we should do it in uvc_ctrl_set() after the refactor.

I think the code would be easier to understand that way, yes.

> > > +
> > > +out:
> > > +     kfree(data);
> > > +     return ret;
> > > +}
> > > +
> > >  int uvc_ctrl_set(struct uvc_fh *handle,
> > >       struct v4l2_ext_control *xctrl)
> > >  {
> > > @@ -2052,13 +2179,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
> > >                      ctrl->info.size);
> > >       }
> > >
> > > -     if (uvc_ctrl_mapping_is_compound(mapping))
> > > -             return -EINVAL;
> > > -     else
> > > +     if (uvc_ctrl_mapping_is_compound(mapping)) {
> > > +             ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
> > > +             if (ret < 0)
> > > +                     return ret;
> > > +     } else
> > >               mapping->set(mapping, value,
> > >                            uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> >
> > I really don't like how handling of compound controls is scattered
> > everywhere :-( That makes the code difficult to read, and thus more
> > error-prone.
> >
> > Could this be refactored to simplify the implementation ? In particular,
> > I'm thinking about
> >
> > - Moving the copy_from_user() and copy_to_user() towards the top of the
> >   call stack, directly in uvc_ctrl_get(), uvc_ctrl_get_boundary() and
> >   uvc_ctrl_set()
> 
> Sorry, I'm not sure I understand it.
> For the uvc_ctrl_get() case, do you mean we should get rid of
> __uvc_ctrl_get_compound_cur()
> and call __uvc_ctrl_load_cur(), mapping->get() and copy_to_user()
> directly in uvc_ctrl_get()?

What I meant is that the copy_from_user() and copy_to_user() calls are
hidden deep in the call stack. Would it be possible to move
copy_to_user() to the end of uvc_ctrl_get() and uvc_ctrl_set(), to avoid
dealing with __user pointers anywhere but at the top level ? I don't
mind keeping __uvc_ctrl_get_compound_cur() if it makes sense.

> > - Merging the .[gs]et() and .[gs]et_compound() functions (see below)
> >
> > - Moving the value clamping from uvc_set_compound_rect() to
> >   uvc_ctrl_set(), with the rest of the clamping code
> >
> > >
> > > -
> > >       if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
> > >               ctrl->handle = handle;
> > >
> > > @@ -2468,10 +2596,23 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> > >                       goto err_nomem;
> > >       }
> > >
> > > -     if (map->get == NULL)
> > > +     if (uvc_ctrl_mapping_is_compound(map)) {
> > > +             if (map->data_size != map->v4l2_size)
> > > +                     return -EINVAL;
> >
> > If the two values have to be the same, why do we have two fields ?
> >
> > > +
> > > +             /* 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))
> > >               map->get = uvc_get_le_value;
> > > -     if (map->set == NULL)
> > > +     if (!map->set && !uvc_ctrl_mapping_is_compound(map))
> > >               map->set = uvc_set_le_value;
> > > +     if (!map->get_compound && uvc_ctrl_mapping_is_compound(map))
> > > +             map->get_compound = uvc_get_compound;
> > > +     if (!map->set_compound && uvc_ctrl_mapping_is_compound(map))
> > > +             map->set_compound = uvc_set_compound;
> > >
> > >       for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
> > >               if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
> > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > index 7bc41270ed94..11805b729c22 100644
> > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > @@ -129,8 +129,12 @@ struct uvc_control_mapping {
> > >
> > >       s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
> > >                  const u8 *data);
> > > +     int (*get_compound)(struct uvc_control_mapping *mapping, const u8 *data,
> > > +                         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);
> >
> > Instead of adding new functions, I think we could modify the existing
> > .get() and .set() handlers to be usable for compound controls.
> >
> >         int (*get)(struct uvc_control_mapping *mapping, u8 query,
> >                    const u8 *data_in, void *data_out);
> >         void (*set)(struct uvc_control_mapping *mapping, const void *data_in,
> >                     u8 *data_out);
> >
> > For additional safety, you could pass the size of the void * buffer to
> > the functions.
> >
> > >  };
> > >
> > >  struct uvc_control {

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 11/11] media: uvcvideo: document UVC v1.5 ROI
  2023-12-12  4:45     ` Yunke Cao
@ 2023-12-18  3:44       ` Laurent Pinchart
  0 siblings, 0 replies; 40+ messages in thread
From: Laurent Pinchart @ 2023-12-18  3:44 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Yunke,

On Tue, Dec 12, 2023 at 01:45:00PM +0900, Yunke Cao wrote:
> On Sat, Dec 9, 2023 at 1:00 AM Laurent Pinchart wrote:
> > On Fri, Dec 01, 2023 at 04:19:02PM +0900, Yunke Cao wrote:
> > > Added documentation of V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
> > > V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.
> >
> > It would be useful to point here to the userspace code that uses those
> > controls. A link to the appropriate Chrome OS git tree would be useful,
> > to showcase a real user.
> 
> Does https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/camera/hal/usb/
> work?

I would pin it to a branch, as it may disappear from the main branch
later:

https://chromium.googlesource.com/chromiumos/platform2/+/refs/heads/release-R121-15699.B/camera/hal/usb/

> Do you think we need the link in both the commit message and this .rst file?

Sounds good to me.

> > > Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> > > Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> > > Signed-off-by: Yunke Cao <yunkec@google.com>
> > > ---
> > > Changelog since v11:
> > > - No change.
> > > Changelog since v10:
> > > - Added Reviewed-by from Sergey.
> > > Changelog since v9:
> > > - No change.
> > > Changelog since v8:
> > > - No change.
> > > Changelog since v7:
> > > - Fix documentation for automatic exposure based on comment in v7.
> > >
> > >  .../userspace-api/media/drivers/uvcvideo.rst  | 62 +++++++++++++++++++
> > >  1 file changed, 62 insertions(+)
> > >
> > > diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > > index aab4304e6bb5..3dc062221f8b 100644
> > > --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > > +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > > @@ -181,6 +181,7 @@ Argument: struct uvc_xu_control_mapping
> > >       UVC_CTRL_DATA_TYPE_BOOLEAN      Boolean
> > >       UVC_CTRL_DATA_TYPE_ENUM         Enumeration
> > >       UVC_CTRL_DATA_TYPE_BITMASK      Bitmask
> > > +     UVC_CTRL_DATA_TYPE_RECT         Rectangular area
> > >
> > >
> > >  UVCIOC_CTRL_QUERY - Query a UVC XU control
> > > @@ -255,3 +256,64 @@ Argument: struct uvc_xu_control_query
> > >       __u8    query           Request code to send to the device
> > >       __u16   size            Control data size (in bytes)
> > >       __u8    *data           Control value
> > > +
> > > +
> > > +Driver-specific V4L2 controls
> > > +-----------------------------
> > > +
> > > +The uvcvideo driver implements the following UVC-specific controls:
> > > +
> > > +``V4L2_CID_UVC_REGION_OF_INTEREST_RECT (struct)``
> > > +     This control determines the region of interest (ROI). ROI is a
> > > +     rectangular area represented by a struct :c:type:`v4l2_rect`. The
> > > +     rectangle is in global sensor coordinates and pixel units. It is
> > > +     independent of the field of view, not impacted by any cropping or
> > > +     scaling.
> > > +
> > > +     Use ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` to query
> > > +     the range of rectangle sizes. The left/top coordinates of a minimum or
> > > +     maximum rectangle are always 0. For example, a device can have a minimum
> > > +     ROI rectangle of 1x1@0x0 and a maximum of 640x480@0x0.
> >
> > Is that actually true ? The UVC 1.5 specification states
> >
> >     GET_MAX shall return the current Window as specified by
> >     CT_DIGITAL_WINDOW_CONTROL.
> >
> > And the window can have non-zero left and right coordinates.
> 
> Ah, you are right. I think I will just remove "The left/top
> coordinates of a minimum or maximum rectangle are always 0."

The rectangle would still be relative to CT_DIGITAL_WINDOW_CONTROL,
which isn't exposed by the driver to userspace. Isn't that a problem ?

> > > +
> > > +     Setting a ROI allows the camera to optimize the capture for the region.
> > > +     The value of ``V4L2_CID_REGION_OF_INTEREST_AUTO`` control determines
> > > +     the detailed behavior.
> > > +
> > > +
> > > +``V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (bitmask)``
> > > +     This determines which, if any, on board features should track to the
> >
> > s/on board/on-board/
> >
> > > +     Region of Interest specified by the current value of
> > > +     ``V4L2_CID_UVD__REGION_OF_INTEREST_RECT``.
> > > +
> > > +     Max value is a mask indicating all supported Auto Controls.
> > > +
> > > +.. flat-table::
> > > +    :header-rows:  0
> > > +    :stub-columns: 0
> > > +
> > > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE``
> > > +      - Setting this to true causes automatic exposure to track the region of
> >
> > Maybe "Setting this bit causes..." as those values are bit flags, not
> > booleans. Same below.
> 
> Sounds good.
> 
> > > +     interest instead of the whole image.
> > > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS``
> > > +      - Setting this to true causes automatic iris to track the region of
> > > +     interest instead of the whole image.
> > > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE``
> > > +      - Setting this to true causes automatic white balance to track the region
> > > +     of interest instead of the whole image.
> > > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS``
> > > +      - Setting this to true causes automatic focus adjustment to track the
> > > +     region of interest instead of the whole image.
> > > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT``
> > > +      - Setting this to true causes automatic face detection to track the
> > > +     region of interest instead of the whole image.
> > > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK``
> > > +      - Setting this to true enables automatic face detection and tracking. The
> > > +     current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> > > +     the driver.
> > > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION``
> > > +      - Setting this to true enables automatic image stabilization. The
> > > +     current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> > > +     the driver.
> > > +    * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY``
> > > +      - Setting this to true enables automatically capture the specified region
> >
> > I wonder what this means, the UVC 1.5 specification doesn't document
> > this bit clearly :-(
> 
> I have no idea :-( . I haven't seen any camera module in Chrome OS
> attempt to implement this bit, either.
> 
> > > +     with higher quality if possible.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 05/11] media: uvcvideo: Add support for compound controls
  2023-12-18  3:27       ` Laurent Pinchart
@ 2023-12-20  2:28         ` Yunke Cao
  0 siblings, 0 replies; 40+ messages in thread
From: Yunke Cao @ 2023-12-20  2:28 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Laurent,

On Mon, Dec 18, 2023 at 12:27 PM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke,
>
> On Wed, Dec 13, 2023 at 04:38:40PM +0900, Yunke Cao wrote:
> > On Sat, Dec 9, 2023 at 12:07 AM Laurent Pinchart wrote:
> > > On Fri, Dec 01, 2023 at 04:18:56PM +0900, Yunke Cao wrote:
> > > > Supports getting/setting current value.
> > > > Supports getting default value.
> > > > Handles V4L2_CTRL_FLAG_NEXT_COMPOUND.
> > >
> > > Please write a better commit message.
> > >
> > > > Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> > > > Signed-off-by: Yunke Cao <yunkec@google.com>
> > > > ---
> > > > Changelog since v11:
> > > > - No change.
> > > > Changelog since v10:
> > > > - Rewrite some logic in __uvc_find_control().
> > > > - Remove a duplicate check in __uvc_ctrl_get_compound_cur().
> > > > - Thanks, Daniel!
> > > > Changelog since v9:
> > > > - Make __uvc_ctrl_set_compound() static.
> > > > Changelog since v8:
> > > > - No change.
> > > > Changelog since v7:
> > > > - Fixed comments styles, indentation and a few other style issues.
> > > > - Renamed uvc_g/set_array() to uvc_g/set_compound().
> > > > - Moved size check to __uvc_ctrl_add_mapping().
> > > > - After setting a new value, copy it back to user.
> > > > - In __uvc_ctrl_set_compound(), check size before allocating.
> > > >
> > > >  drivers/media/usb/uvc/uvc_ctrl.c | 179 +++++++++++++++++++++++++++----
> > > >  drivers/media/usb/uvc/uvcvideo.h |   4 +
> > > >  2 files changed, 164 insertions(+), 19 deletions(-)
> > > >
> > > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > index 98254b93eb46..aae2480496b7 100644
> > > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > > @@ -884,6 +884,28 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
> > > >       }
> > > >  }
> > > >
> > > > +/*
> > > > + * Extract the byte array specified by mapping->offset and mapping->data_size
> > > > + * stored at 'data' to the output array 'data_out'.
> > > > + */
> > > > +static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
> > > > +                         u8 *data_out)
> > > > +{
> > > > +     memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +/*
> > > > + * Copy the byte array 'data_in' to the destination specified by mapping->offset
> > > > + * and mapping->data_size stored at 'data'.
> > > > + */
> > > > +static int uvc_set_compound(struct uvc_control_mapping *mapping,
> > > > +                         const u8 *data_in, u8 *data)
> > > > +{
> > > > +     memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
> > > > +     return 0;
> > > > +}
> > > > +
> > >
> > > I may be missing something, but isn't this essentially dead code ? This
> > > series adds support for a single compound control, with custom get/set
> > > handlers, so these two default handlers will never be called.
> > >
> > > I don't think it makes sense to have default handlers for compound
> > > controls, I don't foresee any use case where a simple mempcy() will do
> > > the right thing.
> >
> > Yes, they are dead code. I thought they would be useful for some
> > controls. I will remove them in v15.
> >
> > > >  static bool
> > > >  uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
> > > >  {
> > > > @@ -906,7 +928,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
> > > >
> > > >  static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
> > > >       struct uvc_control_mapping **mapping, struct uvc_control **control,
> > > > -     int next)
> > > > +     int next, int next_compound)
> > > >  {
> > > >       struct uvc_control *ctrl;
> > > >       struct uvc_control_mapping *map;
> > > > @@ -921,14 +943,16 @@ static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
> > > >                       continue;
> > > >
> > > >               list_for_each_entry(map, &ctrl->info.mappings, list) {
> > > > -                     if ((map->id == v4l2_id) && !next) {
> > > > +                     if (map->id == v4l2_id && !next && !next_compound) {
> > > >                               *control = ctrl;
> > > >                               *mapping = map;
> > > >                               return;
> > > >                       }
> > > >
> > > >                       if ((*mapping == NULL || (*mapping)->id > map->id) &&
> > > > -                         (map->id > v4l2_id) && next) {
> > > > +                         (map->id > v4l2_id) &&
> > > > +                         (uvc_ctrl_mapping_is_compound(map) ?
> > > > +                          next_compound : next)) {
> > > >                               *control = ctrl;
> > > >                               *mapping = map;
> > > >                       }
> > > > @@ -942,6 +966,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
> > > >       struct uvc_control *ctrl = NULL;
> > > >       struct uvc_entity *entity;
> > > >       int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> > > > +     int next_compound = v4l2_id & V4L2_CTRL_FLAG_NEXT_COMPOUND;
> > > >
> > > >       *mapping = NULL;
> > > >
> > > > @@ -950,12 +975,13 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
> > > >
> > > >       /* Find the control. */
> > > >       list_for_each_entry(entity, &chain->entities, chain) {
> > > > -             __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
> > > > -             if (ctrl && !next)
> > > > +             __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next,
> > > > +                                next_compound);
> > > > +             if (ctrl && !next && !next_compound)
> > > >                       return ctrl;
> > > >       }
> > > >
> > > > -     if (ctrl == NULL && !next)
> > > > +     if (!ctrl && !next && !next_compound)
> > > >               uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
> > > >                       v4l2_id);
> > > >
> > > > @@ -1101,12 +1127,59 @@ static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
> > > >       return 0;
> > > >  }
> > > >
> > > > +static int __uvc_ctrl_get_compound(struct uvc_control_mapping *mapping,
> > > > +                                struct uvc_control *ctrl,
> > > > +                                int id,
> > > > +                                struct v4l2_ext_control *xctrl)
> > > > +{
> > > > +     u8 size;
> > > > +     u8 *data;
> > > > +     int ret;
> > > > +
> > > > +     size = mapping->v4l2_size / 8;
> > > > +     if (xctrl->size < size) {
> > > > +             xctrl->size = size;
> > > > +             return -ENOSPC;
> > > > +     }
> > > > +
> > > > +     data = kmalloc(size, GFP_KERNEL);
> > > > +     if (!data)
> > > > +             return -ENOMEM;
> > > > +
> > > > +     ret = mapping->get_compound(mapping, uvc_ctrl_data(ctrl, id), data);
> > > > +     if (ret < 0)
> > > > +             goto out;
> > > > +
> > > > +     ret = copy_to_user(xctrl->ptr, data, size) ? -EFAULT : 0;
> > > > +
> > > > +out:
> > > > +     kfree(data);
> > > > +     return ret;
> > > > +}
> > > > +
> > > > +static int __uvc_ctrl_get_compound_cur(struct uvc_video_chain *chain,
> > > > +                                    struct uvc_control *ctrl,
> > > > +                                    struct uvc_control_mapping *mapping,
> > > > +                                    struct v4l2_ext_control *xctrl)
> > > > +{
> > > > +     int ret;
> > > > +
> > > > +     ret = __uvc_ctrl_load_cur(chain, ctrl);
> > > > +     if (ret < 0)
> > > > +             return ret;
> > > > +
> > > > +     return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT,
> > > > +                                    xctrl);
> > > > +}
> > > > +
> > > >  static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
> > > >                                 u32 found_id)
> > > >  {
> > > > -     bool find_next = req_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> > > >       unsigned int i;
> > > >
> > > > +     bool find_next = req_id &
> > > > +             (V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND);
> > > > +
> > > >       req_id &= V4L2_CTRL_ID_MASK;
> > > >
> > > >       for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
> > > > @@ -1194,7 +1267,7 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
> > > >       }
> > > >
> > > >       __uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
> > > > -                        &master_ctrl, 0);
> > > > +                        &master_ctrl, 0, 0);
> > > >
> > > >       if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
> > > >               return 0;
> > > > @@ -1262,7 +1335,7 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> > > >
> > > >       if (mapping->master_id)
> > > >               __uvc_find_control(ctrl->entity, mapping->master_id,
> > > > -                                &master_map, &master_ctrl, 0);
> > > > +                                &master_map, &master_ctrl, 0, 0);
> > > >       if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
> > > >               s32 val = 0;
> > > >               int ret;
> > > > @@ -1278,6 +1351,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> > > >                               v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
> > > >       }
> > > >
> > > > +     if (v4l2_ctrl->type >= V4L2_CTRL_COMPOUND_TYPES) {
> > > > +             v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
> > > > +             v4l2_ctrl->default_value = 0;
> > > > +             v4l2_ctrl->minimum = 0;
> > > > +             v4l2_ctrl->maximum = 0;
> > > > +             v4l2_ctrl->step = 0;
> > > > +             return 0;
> > > > +     }
> > > > +
> > > >       if (!ctrl->cached) {
> > > >               int ret = uvc_ctrl_populate_cache(chain, ctrl);
> > > >               if (ret < 0)
> > > > @@ -1533,7 +1615,7 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
> > > >       u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
> > > >       s32 val = 0;
> > > >
> > > > -     __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
> > > > +     __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0, 0);
> > > >       if (ctrl == NULL)
> > > >               return;
> > > >
> > > > @@ -1843,7 +1925,7 @@ static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
> > > >
> > > >       for (i = 0; i < ctrls->count; i++) {
> > > >               __uvc_find_control(entity, ctrls->controls[i].id, &mapping,
> > > > -                                &ctrl_found, 0);
> > > > +                                &ctrl_found, 0, 0);
> > > >               if (uvc_control == ctrl_found)
> > > >                       return i;
> > > >       }
> > > > @@ -1891,7 +1973,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
> > > >               return -EINVAL;
> > > >
> > > >       if (uvc_ctrl_mapping_is_compound(mapping))
> > > > -             return -EINVAL;
> > > > +             return __uvc_ctrl_get_compound_cur(chain, ctrl, mapping, xctrl);
> > > >       else
> > > >               return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
> > > >  }
> > > > @@ -1912,6 +1994,22 @@ static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
> > > >       return 0;
> > > >  }
> > > >
> > > > +static int __uvc_ctrl_get_boundary_compound(struct uvc_video_chain *chain,
> > > > +                                         struct uvc_control *ctrl,
> > > > +                                         struct uvc_control_mapping *mapping,
> > > > +                                         struct v4l2_ext_control *xctrl)
> > > > +{
> > > > +     int ret;
> > > > +
> > > > +     if (!ctrl->cached) {
> > > > +             ret = uvc_ctrl_populate_cache(chain, ctrl);
> > > > +             if (ret < 0)
> > > > +                     return ret;
> > > > +     }
> > > > +
> > > > +     return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_DEF, xctrl);
> > > > +}
> > > > +
> > > >  int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> > > >                         struct v4l2_ext_control *xctrl)
> > > >  {
> > > > @@ -1929,7 +2027,8 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> > > >       }
> > > >
> > > >       if (uvc_ctrl_mapping_is_compound(mapping))
> > > > -             ret = -EINVAL;
> > > > +             ret = __uvc_ctrl_get_boundary_compound(chain, ctrl, mapping,
> > > > +                                                    xctrl);
> > > >       else
> > > >               ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
> > > >
> > > > @@ -1938,6 +2037,34 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
> > > >       return ret;
> > > >  }
> > > >
> > > > +static int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> > > > +                                struct v4l2_ext_control *xctrl,
> > > > +                                struct uvc_control *ctrl)
> > > > +{
> > > > +     u8 *data;
> > > > +     int ret;
> > > > +
> > > > +     if (xctrl->size != mapping->v4l2_size / 8)
> > > > +             return -EINVAL;
> > > > +
> > > > +     data = kmalloc(xctrl->size, GFP_KERNEL);
> > > > +     if (!data)
> > > > +             return -ENOMEM;
> > > > +
> > > > +     ret = copy_from_user(data, xctrl->ptr, xctrl->size);
> > > > +     if (ret < 0)
> > > > +             goto out;
> > > > +
> > > > +     ret = mapping->set_compound(mapping, data,
> > > > +                     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> > > > +
> > > > +     __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT, xctrl);
> > >
> > > Why do you need to call __uvc_ctrl_get_compound() here ?
> >
> > I was trying to copy the clamped control value back to the user.
> > I guess we should do it in uvc_ctrl_set() after the refactor.
>
> I think the code would be easier to understand that way, yes.
>
> > > > +
> > > > +out:
> > > > +     kfree(data);
> > > > +     return ret;
> > > > +}
> > > > +
> > > >  int uvc_ctrl_set(struct uvc_fh *handle,
> > > >       struct v4l2_ext_control *xctrl)
> > > >  {
> > > > @@ -2052,13 +2179,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
> > > >                      ctrl->info.size);
> > > >       }
> > > >
> > > > -     if (uvc_ctrl_mapping_is_compound(mapping))
> > > > -             return -EINVAL;
> > > > -     else
> > > > +     if (uvc_ctrl_mapping_is_compound(mapping)) {
> > > > +             ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
> > > > +             if (ret < 0)
> > > > +                     return ret;
> > > > +     } else
> > > >               mapping->set(mapping, value,
> > > >                            uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> > >
> > > I really don't like how handling of compound controls is scattered
> > > everywhere :-( That makes the code difficult to read, and thus more
> > > error-prone.
> > >
> > > Could this be refactored to simplify the implementation ? In particular,
> > > I'm thinking about
> > >
> > > - Moving the copy_from_user() and copy_to_user() towards the top of the
> > >   call stack, directly in uvc_ctrl_get(), uvc_ctrl_get_boundary() and
> > >   uvc_ctrl_set()
> >
> > Sorry, I'm not sure I understand it.
> > For the uvc_ctrl_get() case, do you mean we should get rid of
> > __uvc_ctrl_get_compound_cur()
> > and call __uvc_ctrl_load_cur(), mapping->get() and copy_to_user()
> > directly in uvc_ctrl_get()?
>
> What I meant is that the copy_from_user() and copy_to_user() calls are
> hidden deep in the call stack. Would it be possible to move
> copy_to_user() to the end of uvc_ctrl_get() and uvc_ctrl_set(), to avoid
> dealing with __user pointers anywhere but at the top level ? I don't
> mind keeping __uvc_ctrl_get_compound_cur() if it makes sense.
>
Ah, I see. Let me give it a try.

Thanks!
Yunke

> > > - Merging the .[gs]et() and .[gs]et_compound() functions (see below)
> > >
> > > - Moving the value clamping from uvc_set_compound_rect() to
> > >   uvc_ctrl_set(), with the rest of the clamping code
> > >
> > > >
> > > > -
> > > >       if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
> > > >               ctrl->handle = handle;
> > > >
> > > > @@ -2468,10 +2596,23 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> > > >                       goto err_nomem;
> > > >       }
> > > >
> > > > -     if (map->get == NULL)
> > > > +     if (uvc_ctrl_mapping_is_compound(map)) {
> > > > +             if (map->data_size != map->v4l2_size)
> > > > +                     return -EINVAL;
> > >
> > > If the two values have to be the same, why do we have two fields ?
> > >
> > > > +
> > > > +             /* 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))
> > > >               map->get = uvc_get_le_value;
> > > > -     if (map->set == NULL)
> > > > +     if (!map->set && !uvc_ctrl_mapping_is_compound(map))
> > > >               map->set = uvc_set_le_value;
> > > > +     if (!map->get_compound && uvc_ctrl_mapping_is_compound(map))
> > > > +             map->get_compound = uvc_get_compound;
> > > > +     if (!map->set_compound && uvc_ctrl_mapping_is_compound(map))
> > > > +             map->set_compound = uvc_set_compound;
> > > >
> > > >       for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
> > > >               if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
> > > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > > index 7bc41270ed94..11805b729c22 100644
> > > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > > @@ -129,8 +129,12 @@ struct uvc_control_mapping {
> > > >
> > > >       s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
> > > >                  const u8 *data);
> > > > +     int (*get_compound)(struct uvc_control_mapping *mapping, const u8 *data,
> > > > +                         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);
> > >
> > > Instead of adding new functions, I think we could modify the existing
> > > .get() and .set() handlers to be usable for compound controls.
> > >
> > >         int (*get)(struct uvc_control_mapping *mapping, u8 query,
> > >                    const u8 *data_in, void *data_out);
> > >         void (*set)(struct uvc_control_mapping *mapping, const void *data_in,
> > >                     u8 *data_out);
> > >
> > > For additional safety, you could pass the size of the void * buffer to
> > > the functions.
> > >
> > > >  };
> > > >
> > > >  struct uvc_control {
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v14 09/11] media: uvcvideo: implement UVC v1.5 ROI
  2023-12-01  7:19 ` [PATCH v14 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
  2023-12-08 15:43   ` Laurent Pinchart
@ 2024-03-14 13:24   ` Gergo Koteles
  1 sibling, 0 replies; 40+ messages in thread
From: Gergo Koteles @ 2024-03-14 13:24 UTC (permalink / raw)
  To: Yunke Cao, Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke,

On Fri, 2023-12-01 at 16:19 +0900, Yunke Cao wrote:
> +	{
> +		.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",
> +	},
>  };
>  

I am just wondering would it makes sense to use individual boolean V4L2
controls for these?

It would work with the existing V4L2 applications without knowing what
this bitmask is.

>  
> +/* 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)
> +

Regards,
Gergo


^ permalink raw reply	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2024-03-14 13:24 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-01  7:18 [PATCH v14 00/11] Implement UVC v1.5 ROI Yunke Cao
2023-12-01  7:18 ` [PATCH v14 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
2023-12-01  8:35   ` Hans Verkuil
2023-12-08 13:41     ` Laurent Pinchart
2023-12-12  1:33       ` Yunke Cao
2023-12-12  1:37         ` Laurent Pinchart
2023-12-01  7:18 ` [PATCH v14 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value Yunke Cao
2023-12-08 15:13   ` Laurent Pinchart
2023-12-01  7:18 ` [PATCH v14 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() Yunke Cao
2023-12-08 15:12   ` Laurent Pinchart
2023-12-01  7:18 ` [PATCH v14 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size Yunke Cao
2023-12-08 14:15   ` Laurent Pinchart
2023-12-12  7:59     ` Yunke Cao
2023-12-18  3:17       ` Laurent Pinchart
2023-12-01  7:18 ` [PATCH v14 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
2023-12-06  5:45   ` Dan Carpenter
2023-12-08 15:07   ` Laurent Pinchart
2023-12-13  7:38     ` Yunke Cao
2023-12-18  3:27       ` Laurent Pinchart
2023-12-20  2:28         ` Yunke Cao
2023-12-01  7:18 ` [PATCH v14 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2023-12-01  8:32   ` Hans Verkuil
2023-12-05 22:30   ` kernel test robot
2023-12-06  4:34   ` kernel test robot
2023-12-06  5:59   ` kernel test robot
2023-12-08 15:20   ` Laurent Pinchart
2023-12-13  4:06     ` Yunke Cao
2023-12-14 17:34   ` kernel test robot
2023-12-01  7:18 ` [PATCH v14 07/11] media: vivid: Add an rectangle control Yunke Cao
2023-12-01  7:18 ` [PATCH v14 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2023-12-08 15:26   ` Laurent Pinchart
2023-12-01  7:19 ` [PATCH v14 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
2023-12-08 15:43   ` Laurent Pinchart
2024-03-14 13:24   ` Gergo Koteles
2023-12-01  7:19 ` [PATCH v14 10/11] media: uvcvideo: initilaize ROI control to default value Yunke Cao
2023-12-08 15:50   ` Laurent Pinchart
2023-12-01  7:19 ` [PATCH v14 11/11] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
2023-12-08 16:00   ` Laurent Pinchart
2023-12-12  4:45     ` Yunke Cao
2023-12-18  3:44       ` Laurent Pinchart

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).