[PATCH v14 10/11] media: uvcvideo: initilaize ROI control to default value

Yunke Cao
2023-12-01T07:20:03Z

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

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

Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v8:
- No change.
Changelog since v7:
- Newly added patch. Split initializing from the previous patch.
- Add an init operation to uvc_control_info and use it for ROI
initialization.

drivers/media/usb/uvc/uvc_ctrl.c | 273 ++++++++++++++++++-------------
drivers/media/usb/uvc/uvcvideo.h | 3 +
2 files changed, 160 insertions(+), 116 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index d405b2a9d477..bda625c392c2 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -32,6 +32,158 @@
#define UVC_CTRL_DATA_DEF 5
#define UVC_CTRL_DATA_LAST 6

+/* ------------------------------------------------------------------------
+ * Utility functions
+ */
+
+static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
+{
+ return ctrl->uvc_data + id * ctrl->info.size;
+}
+
+static inline int uvc_test_bit(const u8 *data, int bit)
+{
+ return (data[bit >> 3] >> (bit & 7)) & 1;
+}
+
+static inline void uvc_clear_bit(u8 *data, int bit)
+{
+ data[bit >> 3] &= ~(1 << (bit & 7));
+}
+
+/*
+ * Extract the bit string specified by mapping->offset and mapping->data_size
+ * from the little-endian data stored at 'data' and return the result as
+ * a signed 32bit integer. Sign extension will be performed if the mapping
+ * references a signed data type.
+ */
+static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
+ u8 query, const u8 *data)
+{
+ int bits = mapping->data_size;
+ int offset = mapping->offset;
+ s32 value = 0;
+ u8 mask;
+
+ data += offset / 8;
+ offset &= 7;
+ mask = ((1LL << bits) - 1) << offset;
+
+ while (1) {
+ u8 byte = *data & mask;
+
+ value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
+ bits -= 8 - (offset > 0 ? offset : 0);
+ if (bits <= 0)
+ break;
+
+ offset -= 8;
+ mask = (1 << bits) - 1;
+ data++;
+ }
+
+ /* Sign-extend the value if needed. */
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
+ value |= -(value & (1 << (mapping->data_size - 1)));
+
+ return value;
+}
+
+/*
+ * Set the bit string specified by mapping->offset and mapping->data_size
+ * in the little-endian data stored at 'data' to the value 'value'.
+ */
+static void uvc_set_le_value(struct uvc_control_mapping *mapping,
+ s32 value, u8 *data)
+{
+ int bits = mapping->data_size;
+ int offset = mapping->offset;
+ u8 mask;
+
+ /*
+ * According to the v4l2 spec, writing any value to a button control
+ * should result in the action belonging to the button control being
+ * triggered. UVC devices however want to see a 1 written -> override
+ * value.
+ */
+ if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
+ value = -1;
+
+ data += offset / 8;
+ offset &= 7;
+
+ for (; bits > 0; data++) {
+ mask = ((1LL << bits) - 1) << offset;
+ *data = (*data & ~mask) | ((value << offset) & mask);
+ value >>= offset ? offset : 8;
+ bits -= 8 - offset;
+ offset = 0;
+ }
+}
+
+/*
+ * Extract the byte array specified by mapping->offset and mapping->data_size
+ * stored at 'data' to the output array 'data_out'.
+ */
+static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
+ u8 *data_out)
+{
+ memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
+ return 0;
+}
+
+/*
+ * Copy the byte array 'data_in' to the destination specified by mapping->offset
+ * and mapping->data_size stored at 'data'.
+ */
+static int uvc_set_compound(struct uvc_control_mapping *mapping,
+ const u8 *data_in, const u8 *data_min,
+ const u8 *data_max, u8 *data)
+{
+ memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
+ return 0;
+}
+
+static bool
+uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
+{
+ return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
+}
+
+static int uvc_ctrl_init_roi(struct uvc_device *dev, struct uvc_control *ctrl)
+{
+ int ret;
+
+ ret = uvc_query_ctrl(dev, UVC_GET_DEF, ctrl->entity->id, dev->intfnum,
+ UVC_CT_REGION_OF_INTEREST_CONTROL,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
+ ctrl->info.size);
+ if (ret)
+ goto out;
+
+ /*
+ * Most firmwares have wrong GET_CUR configuration. E.g. it's
+ * below GET_MIN, or have rectangle coordinates mixed up. This
+ * causes problems sometimes, because we are unable to set
+ * auto-controls value without first setting ROI rectangle to
+ * valid configuration.
+ *
+ * We expect that default configuration is always correct and
+ * is within the GET_MIN / GET_MAX boundaries.
+ *
+ * Set current ROI configuration to GET_DEF, so that we will
+ * always have properly configured ROI.
+ */
+ ret = uvc_query_ctrl(dev, UVC_SET_CUR, 1, dev->intfnum,
+ UVC_CT_REGION_OF_INTEREST_CONTROL,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
+ ctrl->info.size);
+out:
+ if (ret)
+ dev_err(&dev->udev->dev, "Failed to fixup ROI (%d).\n", ret);
+ return ret;
+}
+
/* ------------------------------------------------------------------------
* Controls
*/
@@ -375,6 +527,7 @@ static const struct uvc_control_info uvc_ctrls[] = {
| UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
| UVC_CTRL_FLAG_GET_DEF
| UVC_CTRL_FLAG_AUTO_UPDATE,
+ .init = uvc_ctrl_init_roi,
},
};

@@ -901,122 +1054,6 @@ static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = {
NULL, /* Sentinel */
};

-/* ------------------------------------------------------------------------
- * Utility functions
- */
-
-static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
-{
- return ctrl->uvc_data + id * ctrl->info.size;
-}
-
-static inline int uvc_test_bit(const u8 *data, int bit)
-{
- return (data[bit >> 3] >> (bit & 7)) & 1;
-}
-
-static inline void uvc_clear_bit(u8 *data, int bit)
-{
- data[bit >> 3] &= ~(1 << (bit & 7));
-}
-
-/*
- * Extract the bit string specified by mapping->offset and mapping->data_size
- * from the little-endian data stored at 'data' and return the result as
- * a signed 32bit integer. Sign extension will be performed if the mapping
- * references a signed data type.
- */
-static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
- u8 query, const u8 *data)
-{
- int bits = mapping->data_size;
- int offset = mapping->offset;
- s32 value = 0;
- u8 mask;
-
- data += offset / 8;
- offset &= 7;
- mask = ((1LL << bits) - 1) << offset;
-
- while (1) {
- u8 byte = *data & mask;
- value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
- bits -= 8 - (offset > 0 ? offset : 0);
- if (bits <= 0)
- break;
-
- offset -= 8;
- mask = (1 << bits) - 1;
- data++;
- }
-
- /* Sign-extend the value if needed. */
- if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
- value |= -(value & (1 << (mapping->data_size - 1)));
-
- return value;
-}
-
-/*
- * Set the bit string specified by mapping->offset and mapping->data_size
- * in the little-endian data stored at 'data' to the value 'value'.
- */
-static void uvc_set_le_value(struct uvc_control_mapping *mapping,
- s32 value, u8 *data)
-{
- int bits = mapping->data_size;
- int offset = mapping->offset;
- u8 mask;
-
- /*
- * According to the v4l2 spec, writing any value to a button control
- * should result in the action belonging to the button control being
- * triggered. UVC devices however want to see a 1 written -> override
- * value.
- */
- if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
- value = -1;
-
- data += offset / 8;
- offset &= 7;
-
- for (; bits > 0; data++) {
- mask = ((1LL << bits) - 1) << offset;
- *data = (*data & ~mask) | ((value << offset) & mask);
- value >>= offset ? offset : 8;
- bits -= 8 - offset;
- offset = 0;
- }
-}
-
-/*
- * Extract the byte array specified by mapping->offset and mapping->data_size
- * stored at 'data' to the output array 'data_out'.
- */
-static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
- u8 *data_out)
-{
- memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
- return 0;
-}
-
-/*
- * Copy the byte array 'data_in' to the destination specified by mapping->offset
- * and mapping->data_size stored at 'data'.
- */
-static int uvc_set_compound(struct uvc_control_mapping *mapping,
- const u8 *data_in, const u8 *data_min,
- const u8 *data_max, u8 *data)
-{
- memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
- return 0;
-}
-static bool
-uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
-{
- return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
-}
-
/* ------------------------------------------------------------------------
* Terminal and unit management
*/
@@ -2984,6 +3021,10 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
* GET_INFO on standard controls.
*/
uvc_ctrl_get_flags(chain->dev, ctrl, &ctrl->info);
+
+ if (info->init)
+ info->init(chain->dev, ctrl);
+
break;
}
}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 18da5e0b8cb2..335b565da6de 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -85,6 +85,7 @@
struct gpio_desc;
struct sg_table;
struct uvc_device;
+struct uvc_control;

/*
* TODO: Put the most frequently accessed fields at the beginning of
@@ -99,6 +100,8 @@ struct uvc_control_info {

u16 size;
u32 flags;
+
+ int (*init)(struct uvc_device *dev, struct uvc_control *ctrl);
};

struct uvc_control_mapping {
--
2.43.0.rc2.451.g8631bc7472-goog