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

From: Yunke Cao <yunkec@chromium.org>

Hi,

This patch set implements UVC v1.5 region of interest using V4L2
control API. I rebased v10 and resended.

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.

Tested on two different usb cameras using v4l2-compliance, v4l2-ctl
and calling ioctls.

1/11 adds V4L2_CTRL_TYPE_RECT.
2/11, 3/11, 4/11 refactors uvc_ctrl.c.
5/11 adds support for compound controls.
6/11 is a cherry-pick for Hans' implementation of
V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
7/11 documents min/max for the rectangle control.
8/11 supports MIN/MAX in UVC.
9/11 implements ROI in UVC.
10/11 initializes ROI control to default value.
11/11 documents the changes.

Changelog since v9:
- Reordered patches, move MIN/MAX related patches (patch 6, 7, 8) before
  implementing ROI control in UVC. Clamping set current ROI value based on
  MIN/MAX values.
- Fixed some minor issues. Changelog in patches.
Changelog since v9:
- Rebased onto Linus' master branch.
- Fixed some minor issues. Changelog in patches.
Changelog since v9:
- Rebased onto Linus' master branch.
- Fixed some minor issues. Changelog in patches.
Changelog since v8:
- Rebased onto media-stage master (v6.1-rc2).
- Addressed Hans' comments in 8/11 v4l2-ctrls: add support for
  V4L2_CTRL_WHICH_MIN/MAX_VAL. Including adding a new documentation patch
  9/11.
Changelog since v7:
- Rebased onto media-stage master.
- Move the factoring logic from 5/10 to 2/10 and 3/10 (new patch in v8).
- In patch 4/10, split uvc_control_mapping.size to v4l2 and data size.
- Move initializing ROI control from 5/10 into its own patch 7/10.
- Address some comments. Changelogs are in patches.

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: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT
  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          |  15 +-
 .../media/v4l/vidioc-queryctrl.rst            |  11 +
 .../media/videodev2.h.rst.exceptions          |   3 +
 drivers/media/i2c/imx214.c                    |   5 +-
 .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
 drivers/media/usb/uvc/uvc_ctrl.c              | 702 ++++++++++++++----
 drivers/media/usb/uvc/uvc_v4l2.c              |  18 +-
 drivers/media/usb/uvc/uvcvideo.h              |  24 +-
 drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +-
 drivers/media/v4l2-core/v4l2-ctrls-core.c     | 171 ++++-
 drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
 include/media/v4l2-ctrls.h                    |  36 +-
 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                |   4 +
 17 files changed, 942 insertions(+), 204 deletions(-)

-- 
2.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-08-09  6:51   ` Hans Verkuil
  2023-04-26  8:29 ` [PATCH v11 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value Yunke Cao
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 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>
---
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         | 15 +++++++++++++++
 include/media/v4l2-ctrls.h                        |  2 ++
 include/uapi/linux/videodev2.h                    |  2 ++
 6 files changed, 31 insertions(+)

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 892cfeb8b988..927ef397f1ce 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
@@ -189,6 +189,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 a20dfa2a933b..58982cd382e3 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 2a589d34b80e..828cca8e2daa 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 29169170880a..e7f232842376 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -350,6 +350,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
 	case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
 		pr_cont("HEVC_DECODE_PARAMS");
 		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;
@@ -569,6 +574,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;
 
@@ -918,6 +924,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;
 	}
@@ -1605,6 +1617,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 e59d9a234631..1846caf9dd53 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -52,6 +52,7 @@ struct video_device;
  * @p_hdr10_cll:		Pointer to an HDR10 Content Light Level structure.
  * @p_hdr10_mastering:		Pointer to an HDR10 Mastering Display structure.
  * @p_area:			Pointer to an area.
+ * @p_rect:			Pointer to a rectangle.
  * @p:				Pointer to a compound value.
  * @p_const:			Pointer to a constant compound value.
  */
@@ -81,6 +82,7 @@ union v4l2_ctrl_ptr {
 	struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
 	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
 	struct v4l2_area *p_area;
+	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 17a9b975177a..ce2bd9ac4c47 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1797,6 +1797,7 @@ struct v4l2_ext_control {
 		__u32 __user *p_s32;
 		__u32 __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;
@@ -1861,6 +1862,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.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
  2023-04-26  8:29 ` [PATCH v11 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-04-26  8:29 ` [PATCH v11 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() Yunke Cao
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 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>
---
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 5e9d3da862dd..4a4ceb6289f5 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
  */
@@ -1877,6 +1883,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 35453f81c1d9..0ee3932ec33a 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1095,15 +1095,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 9a596c8d894a..87222dd8920e 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.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std()
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
  2023-04-26  8:29 ` [PATCH v11 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
  2023-04-26  8:29 ` [PATCH v11 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-04-26  8:29 ` [PATCH v11 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size Yunke Cao
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 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 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 4a4ceb6289f5..c4a9ea461eb0 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;
 
@@ -1529,7 +1534,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);
@@ -1700,7 +1706,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,
@@ -1880,7 +1887,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,
@@ -2039,8 +2049,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))
+		mapping->set(mapping, value,
+			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+	else
+		return -EINVAL;
+
 
 	if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
 		ctrl->handle = handle;
-- 
2.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (2 preceding siblings ...)
  2023-04-26  8:29 ` [PATCH v11 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-04-26  8:29 ` [PATCH v11 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 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 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 c4a9ea461eb0..4984438d85ca 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;
 
@@ -2036,7 +2036,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;
@@ -2543,8 +2543,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 0ee3932ec33a..77704bd21e5c 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 87222dd8920e..1c6c47ab94f0 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.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 05/11] media: uvcvideo: Add support for compound controls
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (3 preceding siblings ...)
  2023-04-26  8:29 ` [PATCH v11 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-04-26  8:29 ` [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 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 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 | 180 +++++++++++++++++++++++++++----
 drivers/media/usb/uvc/uvcvideo.h |   4 +
 2 files changed, 165 insertions(+), 19 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 4984438d85ca..8f0401230649 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)
@@ -1530,7 +1612,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;
 
@@ -1840,7 +1922,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;
 	}
@@ -1888,7 +1970,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);
 }
@@ -1909,6 +1991,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)
 {
@@ -1926,7 +2024,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);
 
@@ -1935,6 +2034,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)
 {
@@ -2049,12 +2176,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
 		       ctrl->info.size);
 	}
 
-	if (!uvc_ctrl_mapping_is_compound(mapping))
+	if (!uvc_ctrl_mapping_is_compound(mapping)) {
 		mapping->set(mapping, value,
 			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
-	else
-		return -EINVAL;
-
+	} else {
+		ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
+		if (ret < 0)
+			return ret;
+	}
 
 	if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
 		ctrl->handle = handle;
@@ -2465,10 +2594,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 1c6c47ab94f0..8d36a3f8c52e 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.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (4 preceding siblings ...)
  2023-04-26  8:29 ` [PATCH v11 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-08-09  7:05   ` Hans Verkuil
  2023-04-26  8:29 ` [PATCH v11 07/11] media: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT Yunke Cao
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 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 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).

git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
- Fixed some merge conflits.
- Fixed the build error in drivers/media/platform/qcom/venus.

 .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
 .../media/videodev2.h.rst.exceptions          |   2 +
 drivers/media/i2c/imx214.c                    |   5 +-
 .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
 drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +++++--
 drivers/media/v4l2-core/v4l2-ctrls-core.c     | 156 +++++++++++++++---
 drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
 include/media/v4l2-ctrls.h                    |  34 +++-
 include/uapi/linux/videodev2.h                |   2 +
 9 files changed, 236 insertions(+), 44 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 927ef397f1ce..1cc21ee229aa 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
@@ -304,14 +304,21 @@ 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, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
+	value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
 	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
 	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.
+	The definition of minimum/maximum values for compound types are provided by
+	the control documentation. If the control documentation does not
+	document the meaning of minimum/maximum value, then it is not supported.
+	Querying its minmimum/maximum value will result in -ENODATA.
 
 	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/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
index 828cca8e2daa..6295b0fa5716 100644
--- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
@@ -563,6 +563,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 710c9fb515fd..bd11bd8105aa 100644
--- a/drivers/media/i2c/imx214.c
+++ b/drivers/media/i2c/imx214.c
@@ -1037,7 +1037,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 7468e43800a9..28eca8f9d148 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..f8888568c5df 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
@@ -94,6 +94,34 @@ 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)
+{
+	int idx, ret;
+
+	for (idx = 0; idx < ctrl->elems; idx++) {
+		ret = ctrl->type_ops->minimum(ctrl, idx, ctrl->p_new);
+		if (ret)
+			return ret;
+	}
+
+	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)
+{
+	int idx, ret;
+
+	for (idx = 0; idx < ctrl->elems; idx++) {
+		ret = ctrl->type_ops->maximum(ctrl, idx, ctrl->p_new);
+		if (ret)
+			return ret;
+	}
+
+	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 +257,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",
@@ -368,8 +396,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 +417,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 +462,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 +498,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 +599,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 e7f232842376..4f11cd6855e5 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -177,29 +177,72 @@ 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 int 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)
+		return -ENODATA;
+
+	memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
+	return 0;
+}
+
+static int 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)
+		return -ENODATA;
+
+	memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
+	return 0;
+}
+
+static int __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;
+	int ret;
 
 	if (from_idx >= tot_elems)
-		return;
+		return -EINVAL;
+
+	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 -EINVAL;
+	}
 
 	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));
 		}
@@ -209,9 +252,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));
 		}
@@ -221,32 +264,69 @@ 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:
+				ret = std_max_compound(ctrl, i, ptr);
+				if (ret)
+					return ret;
+				break;
+			case V4L2_CTRL_WHICH_MIN_VAL:
+				ret = std_min_compound(ctrl, i, ptr);
+				if (ret)
+					return ret;
+				break;
+			}
+		}
 		break;
 	}
+
+	return 0;
+}
+
+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);
 
+static int v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
+				     union v4l2_ctrl_ptr ptr)
+{
+	return __v4l2_ctrl_type_op_init(ctrl, from_idx,
+					V4L2_CTRL_WHICH_MIN_VAL, ptr);
+}
+
+static int v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
+				     union v4l2_ctrl_ptr ptr)
+{
+	return __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;
@@ -1043,6 +1123,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,
 };
@@ -1514,7 +1596,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;
@@ -1634,6 +1719,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		handler_set_err(hdl, -ERANGE);
 		return NULL;
 	}
+
+	if ((!p_def.p_const && p_min.p_const) ||
+	    (p_min.p_const && !p_max.p_const) ||
+	    (!p_min.p_const && p_max.p_const)) {
+		handler_set_err(hdl, -EINVAL);
+		return NULL;
+	}
+
 	err = check_range(type, min, max, step, def);
 	if (err) {
 		handler_set_err(hdl, err);
@@ -1675,6 +1768,8 @@ 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 * 2;
 
 	ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
 	if (ctrl == NULL) {
@@ -1740,6 +1835,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
 	}
 
+	if (type >= V4L2_CTRL_COMPOUND_TYPES &&
+	    p_min.p_const && p_max.p_const) {
+		ctrl->p_min.p = ctrl->p_def.p + elem_size;
+		memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
+		ctrl->p_max.p = ctrl->p_min.p + elem_size;
+		memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
+	}
+
 	ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
 	cur_to_new(ctrl);
 
@@ -1790,7 +1893,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;
@@ -1815,7 +1919,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);
 
@@ -1848,7 +1953,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);
 
@@ -1880,7 +1986,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);
@@ -1888,7 +1995,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;
@@ -1902,7 +2011,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);
 
@@ -1926,7 +2035,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 87f163a89c80..1463d55fee0a 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -888,7 +888,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 1846caf9dd53..6feeb18411e4 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -125,6 +125,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.
@@ -134,6 +136,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);
+	int (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
+		       union v4l2_ctrl_ptr ptr);
+	int (*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);
 };
@@ -239,6 +245,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.
@@ -298,6 +310,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;
 };
@@ -417,6 +431,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.
@@ -446,6 +462,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;
@@ -715,17 +733,23 @@ 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 to compound controls, thanks
+ * to the @p_def/min/max fields. Use v4l2_ctrl_ptr_create() to create
+ * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
+ * default value of the compound control should be all zeroes. Use
+ * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
+ * is not defined, -ENODATA will be returned in this case.
  *
  */
 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 ce2bd9ac4c47..8870303df713 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1844,6 +1844,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,
-- 
2.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 07/11] media: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (5 preceding siblings ...)
  2023-04-26  8:29 ` [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-08-09  7:13   ` Hans Verkuil
  2023-04-26  8:29 ` [PATCH v11 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Document the default implementation of min/max for rectangle controls.

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

 Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
index 58982cd382e3..efa07428ba7a 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
@@ -447,7 +447,11 @@ 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. Use ``V4L2_CTRL_WHICH_MIN_VAL`` and
+	``V4L2_CTRL_WHICH_MAX_VAL`` to query the range of rectangle sizes. The
+	top-left corner of the minimum and maximum rectangles should be the
+	same. For example, a control can have a minimum rectangle of 1x1@0x0 and
+	a maximum of 640x480@0x0.
     * - ``V4L2_CTRL_TYPE_H264_SPS``
       - n/a
       - n/a
-- 
2.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (6 preceding siblings ...)
  2023-04-26  8:29 ` [PATCH v11 07/11] media: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-04-26  8:29 ` [PATCH v11 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 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 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 | 44 ++++++++++++++++++++++++++++----
 drivers/media/usb/uvc/uvc_v4l2.c |  7 +++--
 drivers/media/usb/uvc/uvcvideo.h |  3 ++-
 3 files changed, 46 insertions(+), 8 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 8f0401230649..109369ec2c42 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1978,6 +1978,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 };
@@ -1987,28 +1988,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 -EACCES;
+
 	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;
@@ -2025,9 +2057,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 77704bd21e5c..2aeb92e8621f 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1093,9 +1093,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 8d36a3f8c52e..69aba8873f65 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.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 09/11] media: uvcvideo: implement UVC v1.5 ROI
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (7 preceding siblings ...)
  2023-04-26  8:29 ` [PATCH v11 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-04-26  8:29 ` [PATCH v11 10/11] media: uvcvideo: initilaize ROI control to default value Yunke Cao
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 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 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 109369ec2c42..df19978746d3 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)
 {
@@ -2087,6 +2192,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);
@@ -2214,6 +2321,13 @@ int uvc_ctrl_set(struct uvc_fh *handle,
 		mapping->set(mapping, value,
 			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
 	} else {
+		/* Populates min/max value cache for clamping. */
+		if (!ctrl->cached) {
+			ret = uvc_ctrl_populate_cache(chain, ctrl);
+			if (ret < 0)
+				return ret;
+		}
+
 		ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
 		if (ret < 0)
 			return ret;
@@ -2629,12 +2743,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 2aeb92e8621f..6e49a9e5d0d1 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1052,7 +1052,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 69aba8873f65..5f1d0ba4b761 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 5e80daa4ffe0..195c57489d6b 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -993,6 +993,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.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 10/11] media: uvcvideo: initilaize ROI control to default value
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (8 preceding siblings ...)
  2023-04-26  8:29 ` [PATCH v11 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-04-26  8:29 ` [PATCH v11 11/11] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 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 v10:
- No change.
Changelog since v9:
- No change.
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 df19978746d3..9a46e0bdec32 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
  */
@@ -2972,6 +3009,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 5f1d0ba4b761..9a47d736ddf3 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.40.1.495.gc816e09b53d-goog


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

* [PATCH v11 11/11] media: uvcvideo: document UVC v1.5 ROI
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (9 preceding siblings ...)
  2023-04-26  8:29 ` [PATCH v11 10/11] media: uvcvideo: initilaize ROI control to default value Yunke Cao
@ 2023-04-26  8:29 ` Yunke Cao
  2023-06-15  7:40 ` [PATCH v11 00/11] media: Implement " Tomasz Figa
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 31+ messages in thread
From: Yunke Cao @ 2023-04-26  8:29 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 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.40.1.495.gc816e09b53d-goog


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

* Re: [PATCH v11 00/11] media: Implement UVC v1.5 ROI
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (10 preceding siblings ...)
  2023-04-26  8:29 ` [PATCH v11 11/11] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
@ 2023-06-15  7:40 ` Tomasz Figa
  2023-08-09  4:04 ` Sergey Senozhatsky
  2023-08-09  7:19 ` Hans Verkuil
  13 siblings, 0 replies; 31+ messages in thread
From: Tomasz Figa @ 2023-06-15  7:40 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart
  Cc: Yunke Cao, Daniel Scally, Sergey Senozhatsky, Ricardo Ribalda,
	linux-media, Yunke Cao

Hi Hans and Laurent,

On Wed, Apr 26, 2023 at 5:29 PM Yunke Cao <yunkec@google.com> wrote:
>
> From: Yunke Cao <yunkec@chromium.org>
>
> Hi,
>
> This patch set implements UVC v1.5 region of interest using V4L2
> control API. I rebased v10 and resended.
>
> 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.
>
> Tested on two different usb cameras using v4l2-compliance, v4l2-ctl
> and calling ioctls.
>
> 1/11 adds V4L2_CTRL_TYPE_RECT.
> 2/11, 3/11, 4/11 refactors uvc_ctrl.c.
> 5/11 adds support for compound controls.
> 6/11 is a cherry-pick for Hans' implementation of
> V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
> 7/11 documents min/max for the rectangle control.
> 8/11 supports MIN/MAX in UVC.
> 9/11 implements ROI in UVC.
> 10/11 initializes ROI control to default value.
> 11/11 documents the changes.
>

Would you have some time to take a look at this series, please? Thanks
in advance!

I think we need Hans's acks for 1, 6 and 7, since they modify the
control framework and then from Laurent for the uvc driver parts.

Best regards,
Tomasz

> Changelog since v9:
> - Reordered patches, move MIN/MAX related patches (patch 6, 7, 8) before
>   implementing ROI control in UVC. Clamping set current ROI value based on
>   MIN/MAX values.
> - Fixed some minor issues. Changelog in patches.
> Changelog since v9:
> - Rebased onto Linus' master branch.
> - Fixed some minor issues. Changelog in patches.
> Changelog since v9:
> - Rebased onto Linus' master branch.
> - Fixed some minor issues. Changelog in patches.
> Changelog since v8:
> - Rebased onto media-stage master (v6.1-rc2).
> - Addressed Hans' comments in 8/11 v4l2-ctrls: add support for
>   V4L2_CTRL_WHICH_MIN/MAX_VAL. Including adding a new documentation patch
>   9/11.
> Changelog since v7:
> - Rebased onto media-stage master.
> - Move the factoring logic from 5/10 to 2/10 and 3/10 (new patch in v8).
> - In patch 4/10, split uvc_control_mapping.size to v4l2 and data size.
> - Move initializing ROI control from 5/10 into its own patch 7/10.
> - Address some comments. Changelogs are in patches.
>
> 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: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT
>   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          |  15 +-
>  .../media/v4l/vidioc-queryctrl.rst            |  11 +
>  .../media/videodev2.h.rst.exceptions          |   3 +
>  drivers/media/i2c/imx214.c                    |   5 +-
>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
>  drivers/media/usb/uvc/uvc_ctrl.c              | 702 ++++++++++++++----
>  drivers/media/usb/uvc/uvc_v4l2.c              |  18 +-
>  drivers/media/usb/uvc/uvcvideo.h              |  24 +-
>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +-
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 171 ++++-
>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>  include/media/v4l2-ctrls.h                    |  36 +-
>  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                |   4 +
>  17 files changed, 942 insertions(+), 204 deletions(-)
>
> --
> 2.40.1.495.gc816e09b53d-goog
>

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

* Re: [PATCH v11 00/11] media: Implement UVC v1.5 ROI
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (11 preceding siblings ...)
  2023-06-15  7:40 ` [PATCH v11 00/11] media: Implement " Tomasz Figa
@ 2023-08-09  4:04 ` Sergey Senozhatsky
  2023-08-09  7:19 ` Hans Verkuil
  13 siblings, 0 replies; 31+ messages in thread
From: Sergey Senozhatsky @ 2023-08-09  4:04 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart
  Cc: Daniel Scally, Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda,
	linux-media, Yunke Cao, Yunke Cao

On (23/04/26 17:29), Yunke Cao wrote:
> Hi,
> 
> This patch set implements UVC v1.5 region of interest using V4L2
> control API. I rebased v10 and resended.
> 
> 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.
> 
> Tested on two different usb cameras using v4l2-compliance, v4l2-ctl
> and calling ioctls.
> 
> 1/11 adds V4L2_CTRL_TYPE_RECT.
> 2/11, 3/11, 4/11 refactors uvc_ctrl.c.
> 5/11 adds support for compound controls.
> 6/11 is a cherry-pick for Hans' implementation of
> V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
> 7/11 documents min/max for the rectangle control.
> 8/11 supports MIN/MAX in UVC.
> 9/11 implements ROI in UVC.
> 10/11 initializes ROI control to default value.
> 11/11 documents the changes.

Hello folks,

Can we please get some reviews/feedback on this?

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

* Re: [PATCH v11 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2023-04-26  8:29 ` [PATCH v11 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
@ 2023-08-09  6:51   ` Hans Verkuil
  2023-08-09  8:35     ` Hans Verkuil
  0 siblings, 1 reply; 31+ messages in thread
From: Hans Verkuil @ 2023-08-09  6:51 UTC (permalink / raw)
  To: Yunke Cao, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

On 26/04/2023 10:29, 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>

Regards,

	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         | 15 +++++++++++++++
>  include/media/v4l2-ctrls.h                        |  2 ++
>  include/uapi/linux/videodev2.h                    |  2 ++
>  6 files changed, 31 insertions(+)
> 
> 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 892cfeb8b988..927ef397f1ce 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> @@ -189,6 +189,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 a20dfa2a933b..58982cd382e3 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 2a589d34b80e..828cca8e2daa 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 29169170880a..e7f232842376 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -350,6 +350,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
>  	case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
>  		pr_cont("HEVC_DECODE_PARAMS");
>  		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;
> @@ -569,6 +574,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;
>  
> @@ -918,6 +924,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;
>  	}
> @@ -1605,6 +1617,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 e59d9a234631..1846caf9dd53 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -52,6 +52,7 @@ struct video_device;
>   * @p_hdr10_cll:		Pointer to an HDR10 Content Light Level structure.
>   * @p_hdr10_mastering:		Pointer to an HDR10 Mastering Display structure.
>   * @p_area:			Pointer to an area.
> + * @p_rect:			Pointer to a rectangle.
>   * @p:				Pointer to a compound value.
>   * @p_const:			Pointer to a constant compound value.
>   */
> @@ -81,6 +82,7 @@ union v4l2_ctrl_ptr {
>  	struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
>  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
>  	struct v4l2_area *p_area;
> +	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 17a9b975177a..ce2bd9ac4c47 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1797,6 +1797,7 @@ struct v4l2_ext_control {
>  		__u32 __user *p_s32;
>  		__u32 __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;
> @@ -1861,6 +1862,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] 31+ messages in thread

* Re: [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-04-26  8:29 ` [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2023-08-09  7:05   ` Hans Verkuil
  2023-08-09  7:34     ` Yunke Cao
  2023-08-09 15:16     ` Hans Verkuil
  0 siblings, 2 replies; 31+ messages in thread
From: Hans Verkuil @ 2023-08-09  7:05 UTC (permalink / raw)
  To: Yunke Cao, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

On 26/04/2023 10:29, 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 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).
> 
> git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
> - Fixed some merge conflits.
> - Fixed the build error in drivers/media/platform/qcom/venus.
> 
>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
>  .../media/videodev2.h.rst.exceptions          |   2 +
>  drivers/media/i2c/imx214.c                    |   5 +-
>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +++++--
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 156 +++++++++++++++---
>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>  include/media/v4l2-ctrls.h                    |  34 +++-
>  include/uapi/linux/videodev2.h                |   2 +
>  9 files changed, 236 insertions(+), 44 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 927ef397f1ce..1cc21ee229aa 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> @@ -304,14 +304,21 @@ 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, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
> +	value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
>  	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
>  	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.
> +	The definition of minimum/maximum values for compound types are provided by
> +	the control documentation. If the control documentation does not
> +	document the meaning of minimum/maximum value, then it is not supported.
> +	Querying its minmimum/maximum value will result in -ENODATA.

typo: minmimum -> minimum

That last line is a bit ambiguous, I suggest this:

If  ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` are not supported,
then querying the minimum or maximum value will result in -ENODATA.

Regards,

	Hans

>  
>  	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/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> index 828cca8e2daa..6295b0fa5716 100644
> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> @@ -563,6 +563,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 710c9fb515fd..bd11bd8105aa 100644
> --- a/drivers/media/i2c/imx214.c
> +++ b/drivers/media/i2c/imx214.c
> @@ -1037,7 +1037,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 7468e43800a9..28eca8f9d148 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..f8888568c5df 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> @@ -94,6 +94,34 @@ 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)
> +{
> +	int idx, ret;
> +
> +	for (idx = 0; idx < ctrl->elems; idx++) {
> +		ret = ctrl->type_ops->minimum(ctrl, idx, ctrl->p_new);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	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)
> +{
> +	int idx, ret;
> +
> +	for (idx = 0; idx < ctrl->elems; idx++) {
> +		ret = ctrl->type_ops->maximum(ctrl, idx, ctrl->p_new);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	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 +257,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",
> @@ -368,8 +396,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 +417,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 +462,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 +498,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 +599,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 e7f232842376..4f11cd6855e5 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -177,29 +177,72 @@ 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 int 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)
> +		return -ENODATA;
> +
> +	memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
> +	return 0;
> +}
> +
> +static int 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)
> +		return -ENODATA;
> +
> +	memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> +	return 0;
> +}
> +
> +static int __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;
> +	int ret;
>  
>  	if (from_idx >= tot_elems)
> -		return;
> +		return -EINVAL;
> +
> +	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 -EINVAL;
> +	}
>  
>  	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));
>  		}
> @@ -209,9 +252,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));
>  		}
> @@ -221,32 +264,69 @@ 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:
> +				ret = std_max_compound(ctrl, i, ptr);
> +				if (ret)
> +					return ret;
> +				break;
> +			case V4L2_CTRL_WHICH_MIN_VAL:
> +				ret = std_min_compound(ctrl, i, ptr);
> +				if (ret)
> +					return ret;
> +				break;
> +			}
> +		}
>  		break;
>  	}
> +
> +	return 0;
> +}
> +
> +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);
>  
> +static int v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +				     union v4l2_ctrl_ptr ptr)
> +{
> +	return __v4l2_ctrl_type_op_init(ctrl, from_idx,
> +					V4L2_CTRL_WHICH_MIN_VAL, ptr);
> +}
> +
> +static int v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> +				     union v4l2_ctrl_ptr ptr)
> +{
> +	return __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;
> @@ -1043,6 +1123,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,
>  };
> @@ -1514,7 +1596,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;
> @@ -1634,6 +1719,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		handler_set_err(hdl, -ERANGE);
>  		return NULL;
>  	}
> +
> +	if ((!p_def.p_const && p_min.p_const) ||
> +	    (p_min.p_const && !p_max.p_const) ||
> +	    (!p_min.p_const && p_max.p_const)) {
> +		handler_set_err(hdl, -EINVAL);
> +		return NULL;
> +	}
> +
>  	err = check_range(type, min, max, step, def);
>  	if (err) {
>  		handler_set_err(hdl, err);
> @@ -1675,6 +1768,8 @@ 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 * 2;
>  
>  	ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
>  	if (ctrl == NULL) {
> @@ -1740,6 +1835,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
>  	}
>  
> +	if (type >= V4L2_CTRL_COMPOUND_TYPES &&
> +	    p_min.p_const && p_max.p_const) {
> +		ctrl->p_min.p = ctrl->p_def.p + elem_size;
> +		memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> +		ctrl->p_max.p = ctrl->p_min.p + elem_size;
> +		memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> +	}
> +
>  	ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
>  	cur_to_new(ctrl);
>  
> @@ -1790,7 +1893,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;
> @@ -1815,7 +1919,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);
>  
> @@ -1848,7 +1953,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);
>  
> @@ -1880,7 +1986,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);
> @@ -1888,7 +1995,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;
> @@ -1902,7 +2011,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);
>  
> @@ -1926,7 +2035,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 87f163a89c80..1463d55fee0a 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -888,7 +888,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 1846caf9dd53..6feeb18411e4 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -125,6 +125,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.
> @@ -134,6 +136,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);
> +	int (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
> +		       union v4l2_ctrl_ptr ptr);
> +	int (*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);
>  };
> @@ -239,6 +245,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.
> @@ -298,6 +310,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;
>  };
> @@ -417,6 +431,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.
> @@ -446,6 +462,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;
> @@ -715,17 +733,23 @@ 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 to compound controls, thanks
> + * to the @p_def/min/max fields. Use v4l2_ctrl_ptr_create() to create
> + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
> + * default value of the compound control should be all zeroes. Use
> + * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
> + * is not defined, -ENODATA will be returned in this case.
>   *
>   */
>  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 ce2bd9ac4c47..8870303df713 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1844,6 +1844,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,


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

* Re: [PATCH v11 07/11] media: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT
  2023-04-26  8:29 ` [PATCH v11 07/11] media: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT Yunke Cao
@ 2023-08-09  7:13   ` Hans Verkuil
  0 siblings, 0 replies; 31+ messages in thread
From: Hans Verkuil @ 2023-08-09  7:13 UTC (permalink / raw)
  To: Yunke Cao, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

On 26/04/2023 10:29, Yunke Cao wrote:
> Document the default implementation of min/max for rectangle controls.
> 
> Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> Changelog since v10:
> - Added Reviewed-by from Daniel.
> Changelog since v9:
> - No change.
> Changelog since v8:
> - New patch.
> 
>  Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> index 58982cd382e3..efa07428ba7a 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> @@ -447,7 +447,11 @@ 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. Use ``V4L2_CTRL_WHICH_MIN_VAL`` and
> +	``V4L2_CTRL_WHICH_MAX_VAL`` to query the range of rectangle sizes. The
> +	top-left corner of the minimum and maximum rectangles should be the
> +	same. For example, a control can have a minimum rectangle of 1x1@0x0 and
> +	a maximum of 640x480@0x0.

I am not sure that it is wise to document here how MIN/MAX_VAL work. This might
depend on the control itself. E.g. patch 11/11 nicely documents this for
V4L2_CID_UVC_REGION_OF_INTEREST_RECT.

I am actually in favor of dropping this patch altogether. Whether min/max is
supported and what it means has to be part of the actual control documentation
and not of the definition of the compound control type itself.

Regards,

	Hans

>      * - ``V4L2_CTRL_TYPE_H264_SPS``
>        - n/a
>        - n/a


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

* Re: [PATCH v11 00/11] media: Implement UVC v1.5 ROI
  2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (12 preceding siblings ...)
  2023-08-09  4:04 ` Sergey Senozhatsky
@ 2023-08-09  7:19 ` Hans Verkuil
  2023-08-09  7:50   ` Yunke Cao
  13 siblings, 1 reply; 31+ messages in thread
From: Hans Verkuil @ 2023-08-09  7:19 UTC (permalink / raw)
  To: Yunke Cao, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

On 26/04/2023 10:29, Yunke Cao wrote:
> From: Yunke Cao <yunkec@chromium.org>
> 
> Hi,
> 
> This patch set implements UVC v1.5 region of interest using V4L2
> control API. I rebased v10 and resended.
> 
> 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.
> 
> Tested on two different usb cameras using v4l2-compliance, v4l2-ctl
> and calling ioctls.
> 
> 1/11 adds V4L2_CTRL_TYPE_RECT.
> 2/11, 3/11, 4/11 refactors uvc_ctrl.c.
> 5/11 adds support for compound controls.
> 6/11 is a cherry-pick for Hans' implementation of
> V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
> 7/11 documents min/max for the rectangle control.
> 8/11 supports MIN/MAX in UVC.
> 9/11 implements ROI in UVC.
> 10/11 initializes ROI control to default value.
> 11/11 documents the changes.

I just looked at the core control code, and except for some minor issues
it looks good.

One thing needs to be added here, though: I want to have a patch adding
a rectangle control + min/max support to the vivid driver, similar to
the existing VIVID_CID_AREA control. This makes it possible to do regression
tests without requiring hardware that supports this.

v4l2-ctl/compliance also need to be adapted for min/max.

Regards,

	Hans

> 
> Changelog since v9:
> - Reordered patches, move MIN/MAX related patches (patch 6, 7, 8) before
>   implementing ROI control in UVC. Clamping set current ROI value based on
>   MIN/MAX values.
> - Fixed some minor issues. Changelog in patches.
> Changelog since v9:
> - Rebased onto Linus' master branch.
> - Fixed some minor issues. Changelog in patches.
> Changelog since v9:
> - Rebased onto Linus' master branch.
> - Fixed some minor issues. Changelog in patches.
> Changelog since v8:
> - Rebased onto media-stage master (v6.1-rc2).
> - Addressed Hans' comments in 8/11 v4l2-ctrls: add support for
>   V4L2_CTRL_WHICH_MIN/MAX_VAL. Including adding a new documentation patch
>   9/11.
> Changelog since v7:
> - Rebased onto media-stage master.
> - Move the factoring logic from 5/10 to 2/10 and 3/10 (new patch in v8).
> - In patch 4/10, split uvc_control_mapping.size to v4l2 and data size.
> - Move initializing ROI control from 5/10 into its own patch 7/10.
> - Address some comments. Changelogs are in patches.
> 
> 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: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT
>   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          |  15 +-
>  .../media/v4l/vidioc-queryctrl.rst            |  11 +
>  .../media/videodev2.h.rst.exceptions          |   3 +
>  drivers/media/i2c/imx214.c                    |   5 +-
>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
>  drivers/media/usb/uvc/uvc_ctrl.c              | 702 ++++++++++++++----
>  drivers/media/usb/uvc/uvc_v4l2.c              |  18 +-
>  drivers/media/usb/uvc/uvcvideo.h              |  24 +-
>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +-
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 171 ++++-
>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>  include/media/v4l2-ctrls.h                    |  36 +-
>  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                |   4 +
>  17 files changed, 942 insertions(+), 204 deletions(-)
> 


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

* Re: [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-08-09  7:05   ` Hans Verkuil
@ 2023-08-09  7:34     ` Yunke Cao
  2023-08-09  8:36       ` Hans Verkuil
  2023-08-09 15:16     ` Hans Verkuil
  1 sibling, 1 reply; 31+ messages in thread
From: Yunke Cao @ 2023-08-09  7:34 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Laurent Pinchart, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hi Hans,

Thanks for the review.

On Wed, Aug 9, 2023 at 4:05 PM Hans Verkuil <hverkuil-cisco@xs4all.nl> wrote:
>
> On 26/04/2023 10:29, 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 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).
> >
> > git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
> > - Fixed some merge conflits.
> > - Fixed the build error in drivers/media/platform/qcom/venus.
> >
> >  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
> >  .../media/videodev2.h.rst.exceptions          |   2 +
> >  drivers/media/i2c/imx214.c                    |   5 +-
> >  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
> >  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +++++--
> >  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 156 +++++++++++++++---
> >  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
> >  include/media/v4l2-ctrls.h                    |  34 +++-
> >  include/uapi/linux/videodev2.h                |   2 +
> >  9 files changed, 236 insertions(+), 44 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 927ef397f1ce..1cc21ee229aa 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > @@ -304,14 +304,21 @@ 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, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
> > +     value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
> >       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
> >       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.
> > +     The definition of minimum/maximum values for compound types are provided by
> > +     the control documentation. If the control documentation does not
> > +     document the meaning of minimum/maximum value, then it is not supported.
> > +     Querying its minmimum/maximum value will result in -ENODATA.
>
> typo: minmimum -> minimum
>
> That last line is a bit ambiguous, I suggest this:
>
> If  ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` are not supported,
> then querying the minimum or maximum value will result in -ENODATA.
>

This sounds clearer indeed! I will change it in the next version.

Best,
Yunke

>
> Regards,
>
>         Hans
>
> >
> >       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/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > index 828cca8e2daa..6295b0fa5716 100644
> > --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > @@ -563,6 +563,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 710c9fb515fd..bd11bd8105aa 100644
> > --- a/drivers/media/i2c/imx214.c
> > +++ b/drivers/media/i2c/imx214.c
> > @@ -1037,7 +1037,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 7468e43800a9..28eca8f9d148 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..f8888568c5df 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > @@ -94,6 +94,34 @@ 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)
> > +{
> > +     int idx, ret;
> > +
> > +     for (idx = 0; idx < ctrl->elems; idx++) {
> > +             ret = ctrl->type_ops->minimum(ctrl, idx, ctrl->p_new);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     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)
> > +{
> > +     int idx, ret;
> > +
> > +     for (idx = 0; idx < ctrl->elems; idx++) {
> > +             ret = ctrl->type_ops->maximum(ctrl, idx, ctrl->p_new);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     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 +257,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",
> > @@ -368,8 +396,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 +417,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 +462,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 +498,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 +599,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 e7f232842376..4f11cd6855e5 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > @@ -177,29 +177,72 @@ 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 int 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)
> > +             return -ENODATA;
> > +
> > +     memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
> > +     return 0;
> > +}
> > +
> > +static int 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)
> > +             return -ENODATA;
> > +
> > +     memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> > +     return 0;
> > +}
> > +
> > +static int __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;
> > +     int ret;
> >
> >       if (from_idx >= tot_elems)
> > -             return;
> > +             return -EINVAL;
> > +
> > +     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 -EINVAL;
> > +     }
> >
> >       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));
> >               }
> > @@ -209,9 +252,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));
> >               }
> > @@ -221,32 +264,69 @@ 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:
> > +                             ret = std_max_compound(ctrl, i, ptr);
> > +                             if (ret)
> > +                                     return ret;
> > +                             break;
> > +                     case V4L2_CTRL_WHICH_MIN_VAL:
> > +                             ret = std_min_compound(ctrl, i, ptr);
> > +                             if (ret)
> > +                                     return ret;
> > +                             break;
> > +                     }
> > +             }
> >               break;
> >       }
> > +
> > +     return 0;
> > +}
> > +
> > +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);
> >
> > +static int v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > +                                  union v4l2_ctrl_ptr ptr)
> > +{
> > +     return __v4l2_ctrl_type_op_init(ctrl, from_idx,
> > +                                     V4L2_CTRL_WHICH_MIN_VAL, ptr);
> > +}
> > +
> > +static int v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > +                                  union v4l2_ctrl_ptr ptr)
> > +{
> > +     return __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;
> > @@ -1043,6 +1123,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,
> >  };
> > @@ -1514,7 +1596,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;
> > @@ -1634,6 +1719,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >               handler_set_err(hdl, -ERANGE);
> >               return NULL;
> >       }
> > +
> > +     if ((!p_def.p_const && p_min.p_const) ||
> > +         (p_min.p_const && !p_max.p_const) ||
> > +         (!p_min.p_const && p_max.p_const)) {
> > +             handler_set_err(hdl, -EINVAL);
> > +             return NULL;
> > +     }
> > +
> >       err = check_range(type, min, max, step, def);
> >       if (err) {
> >               handler_set_err(hdl, err);
> > @@ -1675,6 +1768,8 @@ 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 * 2;
> >
> >       ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
> >       if (ctrl == NULL) {
> > @@ -1740,6 +1835,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >               memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
> >       }
> >
> > +     if (type >= V4L2_CTRL_COMPOUND_TYPES &&
> > +         p_min.p_const && p_max.p_const) {
> > +             ctrl->p_min.p = ctrl->p_def.p + elem_size;
> > +             memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> > +             ctrl->p_max.p = ctrl->p_min.p + elem_size;
> > +             memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> > +     }
> > +
> >       ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
> >       cur_to_new(ctrl);
> >
> > @@ -1790,7 +1893,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;
> > @@ -1815,7 +1919,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);
> >
> > @@ -1848,7 +1953,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);
> >
> > @@ -1880,7 +1986,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);
> > @@ -1888,7 +1995,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;
> > @@ -1902,7 +2011,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);
> >
> > @@ -1926,7 +2035,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 87f163a89c80..1463d55fee0a 100644
> > --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> > +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> > @@ -888,7 +888,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 1846caf9dd53..6feeb18411e4 100644
> > --- a/include/media/v4l2-ctrls.h
> > +++ b/include/media/v4l2-ctrls.h
> > @@ -125,6 +125,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.
> > @@ -134,6 +136,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);
> > +     int (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
> > +                    union v4l2_ctrl_ptr ptr);
> > +     int (*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);
> >  };
> > @@ -239,6 +245,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.
> > @@ -298,6 +310,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;
> >  };
> > @@ -417,6 +431,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.
> > @@ -446,6 +462,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;
> > @@ -715,17 +733,23 @@ 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 to compound controls, thanks
> > + * to the @p_def/min/max fields. Use v4l2_ctrl_ptr_create() to create
> > + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
> > + * default value of the compound control should be all zeroes. Use
> > + * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
> > + * is not defined, -ENODATA will be returned in this case.
> >   *
> >   */
> >  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 ce2bd9ac4c47..8870303df713 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -1844,6 +1844,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,
>

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

* Re: [PATCH v11 00/11] media: Implement UVC v1.5 ROI
  2023-08-09  7:19 ` Hans Verkuil
@ 2023-08-09  7:50   ` Yunke Cao
  0 siblings, 0 replies; 31+ messages in thread
From: Yunke Cao @ 2023-08-09  7:50 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Laurent Pinchart, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media, Yunke Cao

Hi Hans,

Thanks for the review!!

On Wed, Aug 9, 2023 at 4:19 PM Hans Verkuil <hverkuil-cisco@xs4all.nl> wrote:
>
> On 26/04/2023 10:29, Yunke Cao wrote:
> > From: Yunke Cao <yunkec@chromium.org>
> >
> > Hi,
> >
> > This patch set implements UVC v1.5 region of interest using V4L2
> > control API. I rebased v10 and resended.
> >
> > 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.
> >
> > Tested on two different usb cameras using v4l2-compliance, v4l2-ctl
> > and calling ioctls.
> >
> > 1/11 adds V4L2_CTRL_TYPE_RECT.
> > 2/11, 3/11, 4/11 refactors uvc_ctrl.c.
> > 5/11 adds support for compound controls.
> > 6/11 is a cherry-pick for Hans' implementation of
> > V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
> > 7/11 documents min/max for the rectangle control.
> > 8/11 supports MIN/MAX in UVC.
> > 9/11 implements ROI in UVC.
> > 10/11 initializes ROI control to default value.
> > 11/11 documents the changes.
>
> I just looked at the core control code, and except for some minor issues
> it looks good.
>
> One thing needs to be added here, though: I want to have a patch adding
> a rectangle control + min/max support to the vivid driver, similar to
> the existing VIVID_CID_AREA control. This makes it possible to do regression
> tests without requiring hardware that supports this.

Sounds good, I can add a patch with VIVID_CID_RECT control in the next version.

>
> v4l2-ctl/compliance also need to be adapted for min/max.

This patchset doesn't break v4l2-compliance for the cameras I tested with.
I can try to come up with something to verify min/max .

Laurent, Daniel,
Do you have time to look at the UVC patches?

Best,
Yunke

>
> Regards,
>
>         Hans
>
> >
> > Changelog since v9:
> > - Reordered patches, move MIN/MAX related patches (patch 6, 7, 8) before
> >   implementing ROI control in UVC. Clamping set current ROI value based on
> >   MIN/MAX values.
> > - Fixed some minor issues. Changelog in patches.
> > Changelog since v9:
> > - Rebased onto Linus' master branch.
> > - Fixed some minor issues. Changelog in patches.
> > Changelog since v9:
> > - Rebased onto Linus' master branch.
> > - Fixed some minor issues. Changelog in patches.
> > Changelog since v8:
> > - Rebased onto media-stage master (v6.1-rc2).
> > - Addressed Hans' comments in 8/11 v4l2-ctrls: add support for
> >   V4L2_CTRL_WHICH_MIN/MAX_VAL. Including adding a new documentation patch
> >   9/11.
> > Changelog since v7:
> > - Rebased onto media-stage master.
> > - Move the factoring logic from 5/10 to 2/10 and 3/10 (new patch in v8).
> > - In patch 4/10, split uvc_control_mapping.size to v4l2 and data size.
> > - Move initializing ROI control from 5/10 into its own patch 7/10.
> > - Address some comments. Changelogs are in patches.
> >
> > 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: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT
> >   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          |  15 +-
> >  .../media/v4l/vidioc-queryctrl.rst            |  11 +
> >  .../media/videodev2.h.rst.exceptions          |   3 +
> >  drivers/media/i2c/imx214.c                    |   5 +-
> >  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
> >  drivers/media/usb/uvc/uvc_ctrl.c              | 702 ++++++++++++++----
> >  drivers/media/usb/uvc/uvc_v4l2.c              |  18 +-
> >  drivers/media/usb/uvc/uvcvideo.h              |  24 +-
> >  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +-
> >  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 171 ++++-
> >  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
> >  include/media/v4l2-ctrls.h                    |  36 +-
> >  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                |   4 +
> >  17 files changed, 942 insertions(+), 204 deletions(-)
> >
>

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

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

On 8/9/23 08:51, Hans Verkuil wrote:
> On 26/04/2023 10:29, 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>
> 
> Regards,
> 
> 	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         | 15 +++++++++++++++
>>  include/media/v4l2-ctrls.h                        |  2 ++
>>  include/uapi/linux/videodev2.h                    |  2 ++
>>  6 files changed, 31 insertions(+)
>>
>> 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 892cfeb8b988..927ef397f1ce 100644
>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>> @@ -189,6 +189,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 a20dfa2a933b..58982cd382e3 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

Actually, the min/max entries here should be 'optional'.

>> +      - 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.

And here add something along the lines of:

"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."

This change should be made to patch 06/11 which introduces WHICH_MIN/MAX_VAL.

Regards,

	Hans

>>      * - ``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 2a589d34b80e..828cca8e2daa 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 29169170880a..e7f232842376 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
>> @@ -350,6 +350,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
>>  	case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
>>  		pr_cont("HEVC_DECODE_PARAMS");
>>  		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;
>> @@ -569,6 +574,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;
>>  
>> @@ -918,6 +924,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;
>>  	}
>> @@ -1605,6 +1617,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 e59d9a234631..1846caf9dd53 100644
>> --- a/include/media/v4l2-ctrls.h
>> +++ b/include/media/v4l2-ctrls.h
>> @@ -52,6 +52,7 @@ struct video_device;
>>   * @p_hdr10_cll:		Pointer to an HDR10 Content Light Level structure.
>>   * @p_hdr10_mastering:		Pointer to an HDR10 Mastering Display structure.
>>   * @p_area:			Pointer to an area.
>> + * @p_rect:			Pointer to a rectangle.
>>   * @p:				Pointer to a compound value.
>>   * @p_const:			Pointer to a constant compound value.
>>   */
>> @@ -81,6 +82,7 @@ union v4l2_ctrl_ptr {
>>  	struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
>>  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
>>  	struct v4l2_area *p_area;
>> +	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 17a9b975177a..ce2bd9ac4c47 100644
>> --- a/include/uapi/linux/videodev2.h
>> +++ b/include/uapi/linux/videodev2.h
>> @@ -1797,6 +1797,7 @@ struct v4l2_ext_control {
>>  		__u32 __user *p_s32;
>>  		__u32 __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;
>> @@ -1861,6 +1862,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] 31+ messages in thread

* Re: [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-08-09  7:34     ` Yunke Cao
@ 2023-08-09  8:36       ` Hans Verkuil
  2023-08-09 10:15         ` Laurent Pinchart
  0 siblings, 1 reply; 31+ messages in thread
From: Hans Verkuil @ 2023-08-09  8:36 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Laurent Pinchart, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

On 8/9/23 09:34, Yunke Cao wrote:
> Hi Hans,
> 
> Thanks for the review.
> 
> On Wed, Aug 9, 2023 at 4:05 PM Hans Verkuil <hverkuil-cisco@xs4all.nl> wrote:
>>
>> On 26/04/2023 10:29, 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 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).
>>>
>>> git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
>>> - Fixed some merge conflits.
>>> - Fixed the build error in drivers/media/platform/qcom/venus.
>>>
>>>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
>>>  .../media/videodev2.h.rst.exceptions          |   2 +
>>>  drivers/media/i2c/imx214.c                    |   5 +-
>>>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
>>>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +++++--
>>>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 156 +++++++++++++++---
>>>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>>>  include/media/v4l2-ctrls.h                    |  34 +++-
>>>  include/uapi/linux/videodev2.h                |   2 +
>>>  9 files changed, 236 insertions(+), 44 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 927ef397f1ce..1cc21ee229aa 100644
>>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>>> @@ -304,14 +304,21 @@ 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, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
>>> +     value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
>>>       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
>>>       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.
>>> +     The definition of minimum/maximum values for compound types are provided by
>>> +     the control documentation. If the control documentation does not
>>> +     document the meaning of minimum/maximum value, then it is not supported.
>>> +     Querying its minmimum/maximum value will result in -ENODATA.
>>
>> typo: minmimum -> minimum
>>
>> That last line is a bit ambiguous, I suggest this:
>>
>> If  ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` are not supported,
>> then querying the minimum or maximum value will result in -ENODATA.
>>
> 
> This sounds clearer indeed! I will change it in the next version.

Thinking some more about this, I believe it would be better to add a flag
indicating WHICH_MIN/MAX support. I never like relying on an error to
discover a feature. You still need this error, but in addition we need a new
flag:

#define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000

that is set for any control that supports this.

Regards,

	Hans

> 
> Best,
> Yunke
> 
>>
>> Regards,
>>
>>         Hans
>>
>>>
>>>       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/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
>>> index 828cca8e2daa..6295b0fa5716 100644
>>> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
>>> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
>>> @@ -563,6 +563,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 710c9fb515fd..bd11bd8105aa 100644
>>> --- a/drivers/media/i2c/imx214.c
>>> +++ b/drivers/media/i2c/imx214.c
>>> @@ -1037,7 +1037,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 7468e43800a9..28eca8f9d148 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..f8888568c5df 100644
>>> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
>>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
>>> @@ -94,6 +94,34 @@ 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)
>>> +{
>>> +     int idx, ret;
>>> +
>>> +     for (idx = 0; idx < ctrl->elems; idx++) {
>>> +             ret = ctrl->type_ops->minimum(ctrl, idx, ctrl->p_new);
>>> +             if (ret)
>>> +                     return ret;
>>> +     }
>>> +
>>> +     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)
>>> +{
>>> +     int idx, ret;
>>> +
>>> +     for (idx = 0; idx < ctrl->elems; idx++) {
>>> +             ret = ctrl->type_ops->maximum(ctrl, idx, ctrl->p_new);
>>> +             if (ret)
>>> +                     return ret;
>>> +     }
>>> +
>>> +     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 +257,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",
>>> @@ -368,8 +396,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 +417,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 +462,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 +498,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 +599,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 e7f232842376..4f11cd6855e5 100644
>>> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
>>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
>>> @@ -177,29 +177,72 @@ 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 int 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)
>>> +             return -ENODATA;
>>> +
>>> +     memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
>>> +     return 0;
>>> +}
>>> +
>>> +static int 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)
>>> +             return -ENODATA;
>>> +
>>> +     memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
>>> +     return 0;
>>> +}
>>> +
>>> +static int __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;
>>> +     int ret;
>>>
>>>       if (from_idx >= tot_elems)
>>> -             return;
>>> +             return -EINVAL;
>>> +
>>> +     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 -EINVAL;
>>> +     }
>>>
>>>       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));
>>>               }
>>> @@ -209,9 +252,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));
>>>               }
>>> @@ -221,32 +264,69 @@ 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:
>>> +                             ret = std_max_compound(ctrl, i, ptr);
>>> +                             if (ret)
>>> +                                     return ret;
>>> +                             break;
>>> +                     case V4L2_CTRL_WHICH_MIN_VAL:
>>> +                             ret = std_min_compound(ctrl, i, ptr);
>>> +                             if (ret)
>>> +                                     return ret;
>>> +                             break;
>>> +                     }
>>> +             }
>>>               break;
>>>       }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +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);
>>>
>>> +static int v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
>>> +                                  union v4l2_ctrl_ptr ptr)
>>> +{
>>> +     return __v4l2_ctrl_type_op_init(ctrl, from_idx,
>>> +                                     V4L2_CTRL_WHICH_MIN_VAL, ptr);
>>> +}
>>> +
>>> +static int v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
>>> +                                  union v4l2_ctrl_ptr ptr)
>>> +{
>>> +     return __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;
>>> @@ -1043,6 +1123,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,
>>>  };
>>> @@ -1514,7 +1596,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;
>>> @@ -1634,6 +1719,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>>>               handler_set_err(hdl, -ERANGE);
>>>               return NULL;
>>>       }
>>> +
>>> +     if ((!p_def.p_const && p_min.p_const) ||
>>> +         (p_min.p_const && !p_max.p_const) ||
>>> +         (!p_min.p_const && p_max.p_const)) {
>>> +             handler_set_err(hdl, -EINVAL);
>>> +             return NULL;
>>> +     }
>>> +
>>>       err = check_range(type, min, max, step, def);
>>>       if (err) {
>>>               handler_set_err(hdl, err);
>>> @@ -1675,6 +1768,8 @@ 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 * 2;
>>>
>>>       ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
>>>       if (ctrl == NULL) {
>>> @@ -1740,6 +1835,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>>>               memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
>>>       }
>>>
>>> +     if (type >= V4L2_CTRL_COMPOUND_TYPES &&
>>> +         p_min.p_const && p_max.p_const) {
>>> +             ctrl->p_min.p = ctrl->p_def.p + elem_size;
>>> +             memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
>>> +             ctrl->p_max.p = ctrl->p_min.p + elem_size;
>>> +             memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
>>> +     }
>>> +
>>>       ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
>>>       cur_to_new(ctrl);
>>>
>>> @@ -1790,7 +1893,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;
>>> @@ -1815,7 +1919,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);
>>>
>>> @@ -1848,7 +1953,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);
>>>
>>> @@ -1880,7 +1986,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);
>>> @@ -1888,7 +1995,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;
>>> @@ -1902,7 +2011,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);
>>>
>>> @@ -1926,7 +2035,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 87f163a89c80..1463d55fee0a 100644
>>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>>> @@ -888,7 +888,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 1846caf9dd53..6feeb18411e4 100644
>>> --- a/include/media/v4l2-ctrls.h
>>> +++ b/include/media/v4l2-ctrls.h
>>> @@ -125,6 +125,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.
>>> @@ -134,6 +136,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);
>>> +     int (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
>>> +                    union v4l2_ctrl_ptr ptr);
>>> +     int (*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);
>>>  };
>>> @@ -239,6 +245,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.
>>> @@ -298,6 +310,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;
>>>  };
>>> @@ -417,6 +431,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.
>>> @@ -446,6 +462,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;
>>> @@ -715,17 +733,23 @@ 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 to compound controls, thanks
>>> + * to the @p_def/min/max fields. Use v4l2_ctrl_ptr_create() to create
>>> + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
>>> + * default value of the compound control should be all zeroes. Use
>>> + * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
>>> + * is not defined, -ENODATA will be returned in this case.
>>>   *
>>>   */
>>>  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 ce2bd9ac4c47..8870303df713 100644
>>> --- a/include/uapi/linux/videodev2.h
>>> +++ b/include/uapi/linux/videodev2.h
>>> @@ -1844,6 +1844,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,
>>


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

* Re: [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-08-09  8:36       ` Hans Verkuil
@ 2023-08-09 10:15         ` Laurent Pinchart
  2023-08-09 10:59           ` Hans Verkuil
  0 siblings, 1 reply; 31+ messages in thread
From: Laurent Pinchart @ 2023-08-09 10:15 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Yunke Cao, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

Hello,

On Wed, Aug 09, 2023 at 10:36:16AM +0200, Hans Verkuil wrote:
> On 8/9/23 09:34, Yunke Cao wrote:
> > On Wed, Aug 9, 2023 at 4:05 PM Hans Verkuil wrote:
> >> On 26/04/2023 10:29, 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 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).
> >>>
> >>> git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
> >>> - Fixed some merge conflits.
> >>> - Fixed the build error in drivers/media/platform/qcom/venus.
> >>>
> >>>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
> >>>  .../media/videodev2.h.rst.exceptions          |   2 +
> >>>  drivers/media/i2c/imx214.c                    |   5 +-
> >>>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
> >>>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +++++--
> >>>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 156 +++++++++++++++---
> >>>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
> >>>  include/media/v4l2-ctrls.h                    |  34 +++-
> >>>  include/uapi/linux/videodev2.h                |   2 +
> >>>  9 files changed, 236 insertions(+), 44 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 927ef397f1ce..1cc21ee229aa 100644
> >>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> >>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> >>> @@ -304,14 +304,21 @@ 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, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
> >>> +     value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
> >>>       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
> >>>       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.
> >>> +     The definition of minimum/maximum values for compound types are provided by
> >>> +     the control documentation. If the control documentation does not
> >>> +     document the meaning of minimum/maximum value, then it is not supported.
> >>> +     Querying its minmimum/maximum value will result in -ENODATA.
> >>
> >> typo: minmimum -> minimum
> >>
> >> That last line is a bit ambiguous, I suggest this:
> >>
> >> If  ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` are not supported,
> >> then querying the minimum or maximum value will result in -ENODATA.
> > 
> > This sounds clearer indeed! I will change it in the next version.
> 
> Thinking some more about this, I believe it would be better to add a flag
> indicating WHICH_MIN/MAX support. I never like relying on an error to
> discover a feature. You still need this error, but in addition we need a new
> flag:
> 
> #define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
> 
> that is set for any control that supports this.

I think the intent here was to indicate that drivers must return
-ENODATA for V4L2_CTRL_WHICH_MIN_VAL and V4L2_CTRL_WHICH_MAX_VAL if the
control's documentation doesn't specify the meaning of minimum and
maximum for a control. A flag to indicate support for this new API is
likely a good idea, but the documentation here should still clearly
indicate that only controls that have defined minimum and maximum
concepts in the API documentation can implement this API.

> >>>       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/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> >>> index 828cca8e2daa..6295b0fa5716 100644
> >>> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> >>> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> >>> @@ -563,6 +563,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 710c9fb515fd..bd11bd8105aa 100644
> >>> --- a/drivers/media/i2c/imx214.c
> >>> +++ b/drivers/media/i2c/imx214.c
> >>> @@ -1037,7 +1037,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 7468e43800a9..28eca8f9d148 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..f8888568c5df 100644
> >>> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> >>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> >>> @@ -94,6 +94,34 @@ 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)
> >>> +{
> >>> +     int idx, ret;
> >>> +
> >>> +     for (idx = 0; idx < ctrl->elems; idx++) {
> >>> +             ret = ctrl->type_ops->minimum(ctrl, idx, ctrl->p_new);
> >>> +             if (ret)
> >>> +                     return ret;
> >>> +     }
> >>> +
> >>> +     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)
> >>> +{
> >>> +     int idx, ret;
> >>> +
> >>> +     for (idx = 0; idx < ctrl->elems; idx++) {
> >>> +             ret = ctrl->type_ops->maximum(ctrl, idx, ctrl->p_new);
> >>> +             if (ret)
> >>> +                     return ret;
> >>> +     }
> >>> +
> >>> +     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 +257,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",
> >>> @@ -368,8 +396,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 +417,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 +462,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 +498,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 +599,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 e7f232842376..4f11cd6855e5 100644
> >>> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> >>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> >>> @@ -177,29 +177,72 @@ 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 int 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)
> >>> +             return -ENODATA;
> >>> +
> >>> +     memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
> >>> +     return 0;
> >>> +}
> >>> +
> >>> +static int 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)
> >>> +             return -ENODATA;
> >>> +
> >>> +     memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> >>> +     return 0;
> >>> +}
> >>> +
> >>> +static int __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;
> >>> +     int ret;
> >>>
> >>>       if (from_idx >= tot_elems)
> >>> -             return;
> >>> +             return -EINVAL;
> >>> +
> >>> +     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 -EINVAL;
> >>> +     }
> >>>
> >>>       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));
> >>>               }
> >>> @@ -209,9 +252,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));
> >>>               }
> >>> @@ -221,32 +264,69 @@ 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:
> >>> +                             ret = std_max_compound(ctrl, i, ptr);
> >>> +                             if (ret)
> >>> +                                     return ret;
> >>> +                             break;
> >>> +                     case V4L2_CTRL_WHICH_MIN_VAL:
> >>> +                             ret = std_min_compound(ctrl, i, ptr);
> >>> +                             if (ret)
> >>> +                                     return ret;
> >>> +                             break;
> >>> +                     }
> >>> +             }
> >>>               break;
> >>>       }
> >>> +
> >>> +     return 0;
> >>> +}
> >>> +
> >>> +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);
> >>>
> >>> +static int v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> >>> +                                  union v4l2_ctrl_ptr ptr)
> >>> +{
> >>> +     return __v4l2_ctrl_type_op_init(ctrl, from_idx,
> >>> +                                     V4L2_CTRL_WHICH_MIN_VAL, ptr);
> >>> +}
> >>> +
> >>> +static int v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> >>> +                                  union v4l2_ctrl_ptr ptr)
> >>> +{
> >>> +     return __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;
> >>> @@ -1043,6 +1123,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,
> >>>  };
> >>> @@ -1514,7 +1596,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;
> >>> @@ -1634,6 +1719,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >>>               handler_set_err(hdl, -ERANGE);
> >>>               return NULL;
> >>>       }
> >>> +
> >>> +     if ((!p_def.p_const && p_min.p_const) ||
> >>> +         (p_min.p_const && !p_max.p_const) ||
> >>> +         (!p_min.p_const && p_max.p_const)) {
> >>> +             handler_set_err(hdl, -EINVAL);
> >>> +             return NULL;
> >>> +     }
> >>> +
> >>>       err = check_range(type, min, max, step, def);
> >>>       if (err) {
> >>>               handler_set_err(hdl, err);
> >>> @@ -1675,6 +1768,8 @@ 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 * 2;
> >>>
> >>>       ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
> >>>       if (ctrl == NULL) {
> >>> @@ -1740,6 +1835,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >>>               memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
> >>>       }
> >>>
> >>> +     if (type >= V4L2_CTRL_COMPOUND_TYPES &&
> >>> +         p_min.p_const && p_max.p_const) {
> >>> +             ctrl->p_min.p = ctrl->p_def.p + elem_size;
> >>> +             memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> >>> +             ctrl->p_max.p = ctrl->p_min.p + elem_size;
> >>> +             memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> >>> +     }
> >>> +
> >>>       ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
> >>>       cur_to_new(ctrl);
> >>>
> >>> @@ -1790,7 +1893,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;
> >>> @@ -1815,7 +1919,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);
> >>>
> >>> @@ -1848,7 +1953,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);
> >>>
> >>> @@ -1880,7 +1986,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);
> >>> @@ -1888,7 +1995,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;
> >>> @@ -1902,7 +2011,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);
> >>>
> >>> @@ -1926,7 +2035,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 87f163a89c80..1463d55fee0a 100644
> >>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> >>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> >>> @@ -888,7 +888,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 1846caf9dd53..6feeb18411e4 100644
> >>> --- a/include/media/v4l2-ctrls.h
> >>> +++ b/include/media/v4l2-ctrls.h
> >>> @@ -125,6 +125,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.
> >>> @@ -134,6 +136,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);
> >>> +     int (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
> >>> +                    union v4l2_ctrl_ptr ptr);
> >>> +     int (*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);
> >>>  };
> >>> @@ -239,6 +245,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.
> >>> @@ -298,6 +310,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;
> >>>  };
> >>> @@ -417,6 +431,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.
> >>> @@ -446,6 +462,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;
> >>> @@ -715,17 +733,23 @@ 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 to compound controls, thanks
> >>> + * to the @p_def/min/max fields. Use v4l2_ctrl_ptr_create() to create
> >>> + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
> >>> + * default value of the compound control should be all zeroes. Use
> >>> + * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
> >>> + * is not defined, -ENODATA will be returned in this case.
> >>>   *
> >>>   */
> >>>  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 ce2bd9ac4c47..8870303df713 100644
> >>> --- a/include/uapi/linux/videodev2.h
> >>> +++ b/include/uapi/linux/videodev2.h
> >>> @@ -1844,6 +1844,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,

-- 
Regards,

Laurent Pinchart

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

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

On Wed, Aug 09, 2023 at 10:35:46AM +0200, Hans Verkuil wrote:
> On 8/9/23 08:51, Hans Verkuil wrote:
> > On 26/04/2023 10:29, 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>
> > 
> > Regards,
> > 
> > 	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         | 15 +++++++++++++++
> >>  include/media/v4l2-ctrls.h                        |  2 ++
> >>  include/uapi/linux/videodev2.h                    |  2 ++
> >>  6 files changed, 31 insertions(+)
> >>
> >> 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 892cfeb8b988..927ef397f1ce 100644
> >> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> >> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> >> @@ -189,6 +189,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 a20dfa2a933b..58982cd382e3 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
> 
> Actually, the min/max entries here should be 'optional'.
> 
> >> +      - 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.
> 
> And here add something along the lines of:
> 
> "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."

Did you mean optional as in decided for each individual control of this
type (that is, a V4L2_CID_FOO of type RECT would define in its
documentation if the MIN/MAX API is used or not, and all instances of
that control must comply), or each control instance ?

> This change should be made to patch 06/11 which introduces WHICH_MIN/MAX_VAL.
> 
> >>      * - ``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 2a589d34b80e..828cca8e2daa 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 29169170880a..e7f232842376 100644
> >> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> >> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> >> @@ -350,6 +350,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> >>  	case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
> >>  		pr_cont("HEVC_DECODE_PARAMS");
> >>  		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;
> >> @@ -569,6 +574,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;
> >>  
> >> @@ -918,6 +924,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;
> >>  	}
> >> @@ -1605,6 +1617,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 e59d9a234631..1846caf9dd53 100644
> >> --- a/include/media/v4l2-ctrls.h
> >> +++ b/include/media/v4l2-ctrls.h
> >> @@ -52,6 +52,7 @@ struct video_device;
> >>   * @p_hdr10_cll:		Pointer to an HDR10 Content Light Level structure.
> >>   * @p_hdr10_mastering:		Pointer to an HDR10 Mastering Display structure.
> >>   * @p_area:			Pointer to an area.
> >> + * @p_rect:			Pointer to a rectangle.
> >>   * @p:				Pointer to a compound value.
> >>   * @p_const:			Pointer to a constant compound value.
> >>   */
> >> @@ -81,6 +82,7 @@ union v4l2_ctrl_ptr {
> >>  	struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
> >>  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
> >>  	struct v4l2_area *p_area;
> >> +	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 17a9b975177a..ce2bd9ac4c47 100644
> >> --- a/include/uapi/linux/videodev2.h
> >> +++ b/include/uapi/linux/videodev2.h
> >> @@ -1797,6 +1797,7 @@ struct v4l2_ext_control {
> >>  		__u32 __user *p_s32;
> >>  		__u32 __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;
> >> @@ -1861,6 +1862,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] 31+ messages in thread

* Re: [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-08-09 10:15         ` Laurent Pinchart
@ 2023-08-09 10:59           ` Hans Verkuil
  2023-08-09 12:32             ` Yunke Cao
  0 siblings, 1 reply; 31+ messages in thread
From: Hans Verkuil @ 2023-08-09 10:59 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Yunke Cao, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

On 8/9/23 12:15, Laurent Pinchart wrote:
> Hello,
> 
> On Wed, Aug 09, 2023 at 10:36:16AM +0200, Hans Verkuil wrote:
>> On 8/9/23 09:34, Yunke Cao wrote:
>>> On Wed, Aug 9, 2023 at 4:05 PM Hans Verkuil wrote:
>>>> On 26/04/2023 10:29, 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 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).
>>>>>
>>>>> git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
>>>>> - Fixed some merge conflits.
>>>>> - Fixed the build error in drivers/media/platform/qcom/venus.
>>>>>
>>>>>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
>>>>>  .../media/videodev2.h.rst.exceptions          |   2 +
>>>>>  drivers/media/i2c/imx214.c                    |   5 +-
>>>>>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
>>>>>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +++++--
>>>>>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 156 +++++++++++++++---
>>>>>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>>>>>  include/media/v4l2-ctrls.h                    |  34 +++-
>>>>>  include/uapi/linux/videodev2.h                |   2 +
>>>>>  9 files changed, 236 insertions(+), 44 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 927ef397f1ce..1cc21ee229aa 100644
>>>>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>>>>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>>>>> @@ -304,14 +304,21 @@ 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, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
>>>>> +     value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
>>>>>       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
>>>>>       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.
>>>>> +     The definition of minimum/maximum values for compound types are provided by
>>>>> +     the control documentation. If the control documentation does not
>>>>> +     document the meaning of minimum/maximum value, then it is not supported.
>>>>> +     Querying its minmimum/maximum value will result in -ENODATA.
>>>>
>>>> typo: minmimum -> minimum
>>>>
>>>> That last line is a bit ambiguous, I suggest this:
>>>>
>>>> If  ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` are not supported,
>>>> then querying the minimum or maximum value will result in -ENODATA.
>>>
>>> This sounds clearer indeed! I will change it in the next version.
>>
>> Thinking some more about this, I believe it would be better to add a flag
>> indicating WHICH_MIN/MAX support. I never like relying on an error to
>> discover a feature. You still need this error, but in addition we need a new
>> flag:
>>
>> #define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
>>
>> that is set for any control that supports this.
> 
> I think the intent here was to indicate that drivers must return
> -ENODATA for V4L2_CTRL_WHICH_MIN_VAL and V4L2_CTRL_WHICH_MAX_VAL if the
> control's documentation doesn't specify the meaning of minimum and
> maximum for a control. A flag to indicate support for this new API is
> likely a good idea, but the documentation here should still clearly
> indicate that only controls that have defined minimum and maximum
> concepts in the API documentation can implement this API.

This flag is specific to the control ID: so if set, then you can get
the min/max value using V4L2_CTRL_WHICH_MIN/MAX_VAL for that control ID.

This flag must be set for any control that uses the s64 minimum/maximum
fields in struct v4l2_ext_query_ctrl, and for any compound control that
has explicit support for MIN/MAX_VAL (the UVC rectangle control in this
case).

Regards,

	Hans



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

* Re: [PATCH v11 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2023-08-09 10:21       ` Laurent Pinchart
@ 2023-08-09 11:01         ` Hans Verkuil
  2023-08-09 11:05           ` Laurent Pinchart
  0 siblings, 1 reply; 31+ messages in thread
From: Hans Verkuil @ 2023-08-09 11:01 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Yunke Cao, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

On 8/9/23 12:21, Laurent Pinchart wrote:
> On Wed, Aug 09, 2023 at 10:35:46AM +0200, Hans Verkuil wrote:
>> On 8/9/23 08:51, Hans Verkuil wrote:
>>> On 26/04/2023 10:29, 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>
>>>
>>> Regards,
>>>
>>> 	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         | 15 +++++++++++++++
>>>>  include/media/v4l2-ctrls.h                        |  2 ++
>>>>  include/uapi/linux/videodev2.h                    |  2 ++
>>>>  6 files changed, 31 insertions(+)
>>>>
>>>> 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 892cfeb8b988..927ef397f1ce 100644
>>>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>>>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>>>> @@ -189,6 +189,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 a20dfa2a933b..58982cd382e3 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
>>
>> Actually, the min/max entries here should be 'optional'.
>>
>>>> +      - 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.
>>
>> And here add something along the lines of:
>>
>> "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."
> 
> Did you mean optional as in decided for each individual control of this
> type (that is, a V4L2_CID_FOO of type RECT would define in its
> documentation if the MIN/MAX API is used or not, and all instances of
> that control must comply), or each control instance ?

Each individual control, so this is associated with the control ID. Any driver
implementing a control with that ID must behave the same.

Regards,

	Hans

> 
>> This change should be made to patch 06/11 which introduces WHICH_MIN/MAX_VAL.
>>
>>>>      * - ``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 2a589d34b80e..828cca8e2daa 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 29169170880a..e7f232842376 100644
>>>> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
>>>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
>>>> @@ -350,6 +350,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
>>>>  	case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
>>>>  		pr_cont("HEVC_DECODE_PARAMS");
>>>>  		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;
>>>> @@ -569,6 +574,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;
>>>>  
>>>> @@ -918,6 +924,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;
>>>>  	}
>>>> @@ -1605,6 +1617,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 e59d9a234631..1846caf9dd53 100644
>>>> --- a/include/media/v4l2-ctrls.h
>>>> +++ b/include/media/v4l2-ctrls.h
>>>> @@ -52,6 +52,7 @@ struct video_device;
>>>>   * @p_hdr10_cll:		Pointer to an HDR10 Content Light Level structure.
>>>>   * @p_hdr10_mastering:		Pointer to an HDR10 Mastering Display structure.
>>>>   * @p_area:			Pointer to an area.
>>>> + * @p_rect:			Pointer to a rectangle.
>>>>   * @p:				Pointer to a compound value.
>>>>   * @p_const:			Pointer to a constant compound value.
>>>>   */
>>>> @@ -81,6 +82,7 @@ union v4l2_ctrl_ptr {
>>>>  	struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
>>>>  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
>>>>  	struct v4l2_area *p_area;
>>>> +	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 17a9b975177a..ce2bd9ac4c47 100644
>>>> --- a/include/uapi/linux/videodev2.h
>>>> +++ b/include/uapi/linux/videodev2.h
>>>> @@ -1797,6 +1797,7 @@ struct v4l2_ext_control {
>>>>  		__u32 __user *p_s32;
>>>>  		__u32 __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;
>>>> @@ -1861,6 +1862,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] 31+ messages in thread

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

On Wed, Aug 09, 2023 at 01:01:21PM +0200, Hans Verkuil wrote:
> On 8/9/23 12:21, Laurent Pinchart wrote:
> > On Wed, Aug 09, 2023 at 10:35:46AM +0200, Hans Verkuil wrote:
> >> On 8/9/23 08:51, Hans Verkuil wrote:
> >>> On 26/04/2023 10:29, 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>
> >>>
> >>> Regards,
> >>>
> >>> 	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         | 15 +++++++++++++++
> >>>>  include/media/v4l2-ctrls.h                        |  2 ++
> >>>>  include/uapi/linux/videodev2.h                    |  2 ++
> >>>>  6 files changed, 31 insertions(+)
> >>>>
> >>>> 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 892cfeb8b988..927ef397f1ce 100644
> >>>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> >>>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> >>>> @@ -189,6 +189,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 a20dfa2a933b..58982cd382e3 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
> >>
> >> Actually, the min/max entries here should be 'optional'.
> >>
> >>>> +      - 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.
> >>
> >> And here add something along the lines of:
> >>
> >> "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."
> > 
> > Did you mean optional as in decided for each individual control of this
> > type (that is, a V4L2_CID_FOO of type RECT would define in its
> > documentation if the MIN/MAX API is used or not, and all instances of
> > that control must comply), or each control instance ?
> 
> Each individual control, so this is associated with the control ID. Any driver
> implementing a control with that ID must behave the same.

Sounds good to me.

> >> This change should be made to patch 06/11 which introduces WHICH_MIN/MAX_VAL.
> >>
> >>>>      * - ``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 2a589d34b80e..828cca8e2daa 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 29169170880a..e7f232842376 100644
> >>>> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> >>>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> >>>> @@ -350,6 +350,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> >>>>  	case V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS:
> >>>>  		pr_cont("HEVC_DECODE_PARAMS");
> >>>>  		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;
> >>>> @@ -569,6 +574,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;
> >>>>  
> >>>> @@ -918,6 +924,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;
> >>>>  	}
> >>>> @@ -1605,6 +1617,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 e59d9a234631..1846caf9dd53 100644
> >>>> --- a/include/media/v4l2-ctrls.h
> >>>> +++ b/include/media/v4l2-ctrls.h
> >>>> @@ -52,6 +52,7 @@ struct video_device;
> >>>>   * @p_hdr10_cll:		Pointer to an HDR10 Content Light Level structure.
> >>>>   * @p_hdr10_mastering:		Pointer to an HDR10 Mastering Display structure.
> >>>>   * @p_area:			Pointer to an area.
> >>>> + * @p_rect:			Pointer to a rectangle.
> >>>>   * @p:				Pointer to a compound value.
> >>>>   * @p_const:			Pointer to a constant compound value.
> >>>>   */
> >>>> @@ -81,6 +82,7 @@ union v4l2_ctrl_ptr {
> >>>>  	struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
> >>>>  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
> >>>>  	struct v4l2_area *p_area;
> >>>> +	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 17a9b975177a..ce2bd9ac4c47 100644
> >>>> --- a/include/uapi/linux/videodev2.h
> >>>> +++ b/include/uapi/linux/videodev2.h
> >>>> @@ -1797,6 +1797,7 @@ struct v4l2_ext_control {
> >>>>  		__u32 __user *p_s32;
> >>>>  		__u32 __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;
> >>>> @@ -1861,6 +1862,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] 31+ messages in thread

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

On Wed, Aug 9, 2023 at 7:59 PM Hans Verkuil <hverkuil-cisco@xs4all.nl> wrote:
>
> On 8/9/23 12:15, Laurent Pinchart wrote:
> > Hello,
> >
> > On Wed, Aug 09, 2023 at 10:36:16AM +0200, Hans Verkuil wrote:
> >> On 8/9/23 09:34, Yunke Cao wrote:
> >>> On Wed, Aug 9, 2023 at 4:05 PM Hans Verkuil wrote:
> >>>> On 26/04/2023 10:29, 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 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).
> >>>>>
> >>>>> git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
> >>>>> - Fixed some merge conflits.
> >>>>> - Fixed the build error in drivers/media/platform/qcom/venus.
> >>>>>
> >>>>>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
> >>>>>  .../media/videodev2.h.rst.exceptions          |   2 +
> >>>>>  drivers/media/i2c/imx214.c                    |   5 +-
> >>>>>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
> >>>>>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +++++--
> >>>>>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 156 +++++++++++++++---
> >>>>>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
> >>>>>  include/media/v4l2-ctrls.h                    |  34 +++-
> >>>>>  include/uapi/linux/videodev2.h                |   2 +
> >>>>>  9 files changed, 236 insertions(+), 44 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 927ef397f1ce..1cc21ee229aa 100644
> >>>>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> >>>>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> >>>>> @@ -304,14 +304,21 @@ 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, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
> >>>>> +     value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
> >>>>>       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
> >>>>>       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.
> >>>>> +     The definition of minimum/maximum values for compound types are provided by
> >>>>> +     the control documentation. If the control documentation does not
> >>>>> +     document the meaning of minimum/maximum value, then it is not supported.
> >>>>> +     Querying its minmimum/maximum value will result in -ENODATA.
> >>>>
> >>>> typo: minmimum -> minimum
> >>>>
> >>>> That last line is a bit ambiguous, I suggest this:
> >>>>
> >>>> If  ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` are not supported,
> >>>> then querying the minimum or maximum value will result in -ENODATA.
> >>>
> >>> This sounds clearer indeed! I will change it in the next version.
> >>
> >> Thinking some more about this, I believe it would be better to add a flag
> >> indicating WHICH_MIN/MAX support. I never like relying on an error to
> >> discover a feature. You still need this error, but in addition we need a new
> >> flag:
> >>
> >> #define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
> >>
> >> that is set for any control that supports this.
> >
> > I think the intent here was to indicate that drivers must return
> > -ENODATA for V4L2_CTRL_WHICH_MIN_VAL and V4L2_CTRL_WHICH_MAX_VAL if the
> > control's documentation doesn't specify the meaning of minimum and
> > maximum for a control. A flag to indicate support for this new API is
> > likely a good idea, but the documentation here should still clearly
> > indicate that only controls that have defined minimum and maximum
> > concepts in the API documentation can implement this API.
>
> This flag is specific to the control ID: so if set, then you can get
> the min/max value using V4L2_CTRL_WHICH_MIN/MAX_VAL for that control ID.
>
> This flag must be set for any control that uses the s64 minimum/maximum
> fields in struct v4l2_ext_query_ctrl, and for any compound control that
> has explicit support for MIN/MAX_VAL (the UVC rectangle control in this
> case).

> any control that uses the s64 minimum/maximum fields
Noob question: does this include all the non-compound controls?
Are drivers responsible for setting this flag for these controls?

Best,
Yunke

>
> Regards,
>
>         Hans
>
>

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

* Re: [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-08-09 12:32             ` Yunke Cao
@ 2023-08-09 12:41               ` Hans Verkuil
  2023-08-09 13:55               ` Hans Verkuil
  1 sibling, 0 replies; 31+ messages in thread
From: Hans Verkuil @ 2023-08-09 12:41 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Laurent Pinchart, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

On 8/9/23 14:32, Yunke Cao wrote:
> On Wed, Aug 9, 2023 at 7:59 PM Hans Verkuil <hverkuil-cisco@xs4all.nl> wrote:
>>
>> On 8/9/23 12:15, Laurent Pinchart wrote:
>>> Hello,
>>>
>>> On Wed, Aug 09, 2023 at 10:36:16AM +0200, Hans Verkuil wrote:
>>>> On 8/9/23 09:34, Yunke Cao wrote:
>>>>> On Wed, Aug 9, 2023 at 4:05 PM Hans Verkuil wrote:
>>>>>> On 26/04/2023 10:29, 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 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).
>>>>>>>
>>>>>>> git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
>>>>>>> - Fixed some merge conflits.
>>>>>>> - Fixed the build error in drivers/media/platform/qcom/venus.
>>>>>>>
>>>>>>>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
>>>>>>>  .../media/videodev2.h.rst.exceptions          |   2 +
>>>>>>>  drivers/media/i2c/imx214.c                    |   5 +-
>>>>>>>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
>>>>>>>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +++++--
>>>>>>>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 156 +++++++++++++++---
>>>>>>>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>>>>>>>  include/media/v4l2-ctrls.h                    |  34 +++-
>>>>>>>  include/uapi/linux/videodev2.h                |   2 +
>>>>>>>  9 files changed, 236 insertions(+), 44 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 927ef397f1ce..1cc21ee229aa 100644
>>>>>>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>>>>>>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>>>>>>> @@ -304,14 +304,21 @@ 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, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
>>>>>>> +     value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
>>>>>>>       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
>>>>>>>       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.
>>>>>>> +     The definition of minimum/maximum values for compound types are provided by
>>>>>>> +     the control documentation. If the control documentation does not
>>>>>>> +     document the meaning of minimum/maximum value, then it is not supported.
>>>>>>> +     Querying its minmimum/maximum value will result in -ENODATA.
>>>>>>
>>>>>> typo: minmimum -> minimum
>>>>>>
>>>>>> That last line is a bit ambiguous, I suggest this:
>>>>>>
>>>>>> If  ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` are not supported,
>>>>>> then querying the minimum or maximum value will result in -ENODATA.
>>>>>
>>>>> This sounds clearer indeed! I will change it in the next version.
>>>>
>>>> Thinking some more about this, I believe it would be better to add a flag
>>>> indicating WHICH_MIN/MAX support. I never like relying on an error to
>>>> discover a feature. You still need this error, but in addition we need a new
>>>> flag:
>>>>
>>>> #define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
>>>>
>>>> that is set for any control that supports this.
>>>
>>> I think the intent here was to indicate that drivers must return
>>> -ENODATA for V4L2_CTRL_WHICH_MIN_VAL and V4L2_CTRL_WHICH_MAX_VAL if the
>>> control's documentation doesn't specify the meaning of minimum and
>>> maximum for a control. A flag to indicate support for this new API is
>>> likely a good idea, but the documentation here should still clearly
>>> indicate that only controls that have defined minimum and maximum
>>> concepts in the API documentation can implement this API.
>>
>> This flag is specific to the control ID: so if set, then you can get
>> the min/max value using V4L2_CTRL_WHICH_MIN/MAX_VAL for that control ID.
>>
>> This flag must be set for any control that uses the s64 minimum/maximum
>> fields in struct v4l2_ext_query_ctrl, and for any compound control that
>> has explicit support for MIN/MAX_VAL (the UVC rectangle control in this
>> case).
> 
>> any control that uses the s64 minimum/maximum fields
> Noob question: does this include all the non-compound controls?

Yes. Almost all of those have valid and always present min/max ranges.

> Are drivers responsible for setting this flag for these controls?

No, the v4l2 control framework provides that automatically. It just needs
to set the flag. Note that there are some standard types that do not
support this (BUTTON, CTRL_CLASS are the only ones I think), so those should
not set this flag. I think so, at least. Might have to sleep on it for a bit.

Regards,

	Hans

> 
> Best,
> Yunke
> 
>>
>> Regards,
>>
>>         Hans
>>
>>


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

* Re: [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-08-09 12:32             ` Yunke Cao
  2023-08-09 12:41               ` Hans Verkuil
@ 2023-08-09 13:55               ` Hans Verkuil
  1 sibling, 0 replies; 31+ messages in thread
From: Hans Verkuil @ 2023-08-09 13:55 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Laurent Pinchart, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
	Ricardo Ribalda, linux-media

On 09/08/2023 14:32, Yunke Cao wrote:
> On Wed, Aug 9, 2023 at 7:59 PM Hans Verkuil <hverkuil-cisco@xs4all.nl> wrote:
>>
>> On 8/9/23 12:15, Laurent Pinchart wrote:
>>> Hello,
>>>
>>> On Wed, Aug 09, 2023 at 10:36:16AM +0200, Hans Verkuil wrote:
>>>> On 8/9/23 09:34, Yunke Cao wrote:
>>>>> On Wed, Aug 9, 2023 at 4:05 PM Hans Verkuil wrote:
>>>>>> On 26/04/2023 10:29, 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 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).
>>>>>>>
>>>>>>> git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
>>>>>>> - Fixed some merge conflits.
>>>>>>> - Fixed the build error in drivers/media/platform/qcom/venus.
>>>>>>>
>>>>>>>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
>>>>>>>  .../media/videodev2.h.rst.exceptions          |   2 +
>>>>>>>  drivers/media/i2c/imx214.c                    |   5 +-
>>>>>>>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
>>>>>>>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +++++--
>>>>>>>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 156 +++++++++++++++---
>>>>>>>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>>>>>>>  include/media/v4l2-ctrls.h                    |  34 +++-
>>>>>>>  include/uapi/linux/videodev2.h                |   2 +
>>>>>>>  9 files changed, 236 insertions(+), 44 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 927ef397f1ce..1cc21ee229aa 100644
>>>>>>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>>>>>>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>>>>>>> @@ -304,14 +304,21 @@ 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, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
>>>>>>> +     value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
>>>>>>>       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
>>>>>>>       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.
>>>>>>> +     The definition of minimum/maximum values for compound types are provided by
>>>>>>> +     the control documentation. If the control documentation does not
>>>>>>> +     document the meaning of minimum/maximum value, then it is not supported.
>>>>>>> +     Querying its minmimum/maximum value will result in -ENODATA.
>>>>>>
>>>>>> typo: minmimum -> minimum
>>>>>>
>>>>>> That last line is a bit ambiguous, I suggest this:
>>>>>>
>>>>>> If  ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` are not supported,
>>>>>> then querying the minimum or maximum value will result in -ENODATA.

I realized that ENODATA is wrong: ENODATA implies that while there is no data
now, there might be in the future. That's not the case here. I think the correct
error code is EINVAL: the value of the 'which' field is invalid for this control.

Regards,

	Hans

>>>>>
>>>>> This sounds clearer indeed! I will change it in the next version.
>>>>
>>>> Thinking some more about this, I believe it would be better to add a flag
>>>> indicating WHICH_MIN/MAX support. I never like relying on an error to
>>>> discover a feature. You still need this error, but in addition we need a new
>>>> flag:
>>>>
>>>> #define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
>>>>
>>>> that is set for any control that supports this.
>>>
>>> I think the intent here was to indicate that drivers must return
>>> -ENODATA for V4L2_CTRL_WHICH_MIN_VAL and V4L2_CTRL_WHICH_MAX_VAL if the
>>> control's documentation doesn't specify the meaning of minimum and
>>> maximum for a control. A flag to indicate support for this new API is
>>> likely a good idea, but the documentation here should still clearly
>>> indicate that only controls that have defined minimum and maximum
>>> concepts in the API documentation can implement this API.
>>
>> This flag is specific to the control ID: so if set, then you can get
>> the min/max value using V4L2_CTRL_WHICH_MIN/MAX_VAL for that control ID.
>>
>> This flag must be set for any control that uses the s64 minimum/maximum
>> fields in struct v4l2_ext_query_ctrl, and for any compound control that
>> has explicit support for MIN/MAX_VAL (the UVC rectangle control in this
>> case).
> 
>> any control that uses the s64 minimum/maximum fields
> Noob question: does this include all the non-compound controls?
> Are drivers responsible for setting this flag for these controls?
> 
> Best,
> Yunke
> 
>>
>> Regards,
>>
>>         Hans
>>
>>


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

* Re: [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2023-08-09  7:05   ` Hans Verkuil
  2023-08-09  7:34     ` Yunke Cao
@ 2023-08-09 15:16     ` Hans Verkuil
  1 sibling, 0 replies; 31+ messages in thread
From: Hans Verkuil @ 2023-08-09 15:16 UTC (permalink / raw)
  To: Yunke Cao, Laurent Pinchart, Daniel Scally
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

On 09/08/2023 09:05, Hans Verkuil wrote:
> On 26/04/2023 10:29, 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 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).
>>
>> git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
>> - Fixed some merge conflits.
>> - Fixed the build error in drivers/media/platform/qcom/venus.
>>
>>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
>>  .../media/videodev2.h.rst.exceptions          |   2 +
>>  drivers/media/i2c/imx214.c                    |   5 +-
>>  .../media/platform/qcom/venus/venc_ctrls.c    |   9 +-
>>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  57 +++++--
>>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 156 +++++++++++++++---
>>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>>  include/media/v4l2-ctrls.h                    |  34 +++-
>>  include/uapi/linux/videodev2.h                |   2 +
>>  9 files changed, 236 insertions(+), 44 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 927ef397f1ce..1cc21ee229aa 100644
>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>> @@ -304,14 +304,21 @@ 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, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
>> +	value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
>>  	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
>>  	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.
>> +	The definition of minimum/maximum values for compound types are provided by
>> +	the control documentation. If the control documentation does not
>> +	document the meaning of minimum/maximum value, then it is not supported.
>> +	Querying its minmimum/maximum value will result in -ENODATA.
> 
> typo: minmimum -> minimum
> 
> That last line is a bit ambiguous, I suggest this:
> 
> If  ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` are not supported,
> then querying the minimum or maximum value will result in -ENODATA.
> 
> Regards,
> 
> 	Hans
> 
>>  
>>  	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/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
>> index 828cca8e2daa..6295b0fa5716 100644
>> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
>> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
>> @@ -563,6 +563,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 710c9fb515fd..bd11bd8105aa 100644
>> --- a/drivers/media/i2c/imx214.c
>> +++ b/drivers/media/i2c/imx214.c
>> @@ -1037,7 +1037,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 7468e43800a9..28eca8f9d148 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..f8888568c5df 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
>> @@ -94,6 +94,34 @@ 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)
>> +{
>> +	int idx, ret;
>> +
>> +	for (idx = 0; idx < ctrl->elems; idx++) {
>> +		ret = ctrl->type_ops->minimum(ctrl, idx, ctrl->p_new);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	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)
>> +{
>> +	int idx, ret;
>> +
>> +	for (idx = 0; idx < ctrl->elems; idx++) {
>> +		ret = ctrl->type_ops->maximum(ctrl, idx, ctrl->p_new);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	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 +257,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",
>> @@ -368,8 +396,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 +417,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 +462,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 +498,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 +599,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 e7f232842376..4f11cd6855e5 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
>> @@ -177,29 +177,72 @@ 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 int 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)
>> +		return -ENODATA;

I don't really like having an error return in such a low-level function.

Why not make these functions return void, and do the same as in std_init_compound:

        if (ctrl->p_min.p_const)
                memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
        else
                memset(p, 0, ctrl->elem_size);

This has the added advantage that if the minimum is all zero, then you don't
need to supply a p_min value.

The actual check if WHICH_MIN_VAL is allowed should take place at a much
higher level, and it can just check for the presence of the new control flag.

Regards,

	Hans

>> +
>> +	memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
>> +	return 0;
>> +}
>> +
>> +static int 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)
>> +		return -ENODATA;
>> +
>> +	memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
>> +	return 0;
>> +}
>> +
>> +static int __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;
>> +	int ret;
>>  
>>  	if (from_idx >= tot_elems)
>> -		return;
>> +		return -EINVAL;
>> +
>> +	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 -EINVAL;
>> +	}
>>  
>>  	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));
>>  		}
>> @@ -209,9 +252,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));
>>  		}
>> @@ -221,32 +264,69 @@ 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:
>> +				ret = std_max_compound(ctrl, i, ptr);
>> +				if (ret)
>> +					return ret;
>> +				break;
>> +			case V4L2_CTRL_WHICH_MIN_VAL:
>> +				ret = std_min_compound(ctrl, i, ptr);
>> +				if (ret)
>> +					return ret;
>> +				break;
>> +			}
>> +		}
>>  		break;
>>  	}
>> +
>> +	return 0;
>> +}
>> +
>> +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);
>>  
>> +static int v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
>> +				     union v4l2_ctrl_ptr ptr)
>> +{
>> +	return __v4l2_ctrl_type_op_init(ctrl, from_idx,
>> +					V4L2_CTRL_WHICH_MIN_VAL, ptr);
>> +}
>> +
>> +static int v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
>> +				     union v4l2_ctrl_ptr ptr)
>> +{
>> +	return __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;
>> @@ -1043,6 +1123,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,
>>  };
>> @@ -1514,7 +1596,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;
>> @@ -1634,6 +1719,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>>  		handler_set_err(hdl, -ERANGE);
>>  		return NULL;
>>  	}
>> +
>> +	if ((!p_def.p_const && p_min.p_const) ||
>> +	    (p_min.p_const && !p_max.p_const) ||
>> +	    (!p_min.p_const && p_max.p_const)) {
>> +		handler_set_err(hdl, -EINVAL);
>> +		return NULL;
>> +	}
>> +
>>  	err = check_range(type, min, max, step, def);
>>  	if (err) {
>>  		handler_set_err(hdl, err);
>> @@ -1675,6 +1768,8 @@ 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 * 2;
>>  
>>  	ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
>>  	if (ctrl == NULL) {
>> @@ -1740,6 +1835,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>>  		memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
>>  	}
>>  
>> +	if (type >= V4L2_CTRL_COMPOUND_TYPES &&
>> +	    p_min.p_const && p_max.p_const) {
>> +		ctrl->p_min.p = ctrl->p_def.p + elem_size;
>> +		memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
>> +		ctrl->p_max.p = ctrl->p_min.p + elem_size;
>> +		memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
>> +	}
>> +
>>  	ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
>>  	cur_to_new(ctrl);
>>  
>> @@ -1790,7 +1893,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;
>> @@ -1815,7 +1919,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);
>>  
>> @@ -1848,7 +1953,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);
>>  
>> @@ -1880,7 +1986,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);
>> @@ -1888,7 +1995,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;
>> @@ -1902,7 +2011,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);
>>  
>> @@ -1926,7 +2035,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 87f163a89c80..1463d55fee0a 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -888,7 +888,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 1846caf9dd53..6feeb18411e4 100644
>> --- a/include/media/v4l2-ctrls.h
>> +++ b/include/media/v4l2-ctrls.h
>> @@ -125,6 +125,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.
>> @@ -134,6 +136,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);
>> +	int (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
>> +		       union v4l2_ctrl_ptr ptr);
>> +	int (*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);
>>  };
>> @@ -239,6 +245,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.
>> @@ -298,6 +310,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;
>>  };
>> @@ -417,6 +431,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.
>> @@ -446,6 +462,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;
>> @@ -715,17 +733,23 @@ 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 to compound controls, thanks
>> + * to the @p_def/min/max fields. Use v4l2_ctrl_ptr_create() to create
>> + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
>> + * default value of the compound control should be all zeroes. Use
>> + * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
>> + * is not defined, -ENODATA will be returned in this case.
>>   *
>>   */
>>  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 ce2bd9ac4c47..8870303df713 100644
>> --- a/include/uapi/linux/videodev2.h
>> +++ b/include/uapi/linux/videodev2.h
>> @@ -1844,6 +1844,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,
> 


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

end of thread, other threads:[~2023-08-09 15:16 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-26  8:29 [PATCH v11 00/11] media: Implement UVC v1.5 ROI Yunke Cao
2023-04-26  8:29 ` [PATCH v11 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
2023-08-09  6:51   ` Hans Verkuil
2023-08-09  8:35     ` Hans Verkuil
2023-08-09 10:21       ` Laurent Pinchart
2023-08-09 11:01         ` Hans Verkuil
2023-08-09 11:05           ` Laurent Pinchart
2023-04-26  8:29 ` [PATCH v11 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value Yunke Cao
2023-04-26  8:29 ` [PATCH v11 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() Yunke Cao
2023-04-26  8:29 ` [PATCH v11 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size Yunke Cao
2023-04-26  8:29 ` [PATCH v11 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
2023-04-26  8:29 ` [PATCH v11 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2023-08-09  7:05   ` Hans Verkuil
2023-08-09  7:34     ` Yunke Cao
2023-08-09  8:36       ` Hans Verkuil
2023-08-09 10:15         ` Laurent Pinchart
2023-08-09 10:59           ` Hans Verkuil
2023-08-09 12:32             ` Yunke Cao
2023-08-09 12:41               ` Hans Verkuil
2023-08-09 13:55               ` Hans Verkuil
2023-08-09 15:16     ` Hans Verkuil
2023-04-26  8:29 ` [PATCH v11 07/11] media: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT Yunke Cao
2023-08-09  7:13   ` Hans Verkuil
2023-04-26  8:29 ` [PATCH v11 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2023-04-26  8:29 ` [PATCH v11 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
2023-04-26  8:29 ` [PATCH v11 10/11] media: uvcvideo: initilaize ROI control to default value Yunke Cao
2023-04-26  8:29 ` [PATCH v11 11/11] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
2023-06-15  7:40 ` [PATCH v11 00/11] media: Implement " Tomasz Figa
2023-08-09  4:04 ` Sergey Senozhatsky
2023-08-09  7:19 ` Hans Verkuil
2023-08-09  7:50   ` Yunke Cao

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).