| From 8b425a877d17fc5168a332edc47f0e6d4c1d9b06 Mon Sep 17 00:00:00 2001 |
| From: Yunke Cao <yunkec@chromium.org> |
| Date: Thu, 13 Jul 2023 14:24:23 +0900 |
| Subject: [PATCH] BACKPORT:FROMLIST:media: uvcvideo: initilaize ROI control to |
| default value |
| |
| 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> |
| (am from https://patchwork.kernel.org/project/linux-media/patch/20230426082923.132909-11-yunkec@google.com/) |
| |
| UPSTREAM-TASK=b:191930245 |
| BUG=b:191930245 |
| TEST=v4l2-compliance |
| |
| Change-Id: Ibf76de110b9148d5be948656509de04e85aec340 |
| Signed-off-by: Yunke Cao <yunkec@chromium.org> |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/4680646 |
| Reviewed-by: Ricardo Ribalda <ribalda@chromium.org> |
| (cherry picked from commit 97219829499eefaf51488abd26e5f121deae4ca9) |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/5531319 |
| --- |
| drivers/media/usb/uvc/uvc_ctrl.c | 274 ++++++++++++++++++------------- |
| drivers/media/usb/uvc/uvcvideo.h | 2 + |
| 2 files changed, 159 insertions(+), 117 deletions(-) |
| |
| diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c |
| index f5a04b9ab9866f8bfc48bd59345302093ce20441..4bd5fe7e6a767a88fa15b2880e844ebd9a4cd4fb 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, |
| }, |
| }; |
| |
| @@ -948,123 +1101,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { |
| }, |
| }; |
| |
| -/* ------------------------------------------------------------------------ |
| - * 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 - max(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 |
| */ |
| @@ -3077,6 +3113,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 edf5b4f9fb25c5f3d7dff83f05f478ff14ffdb69..e7991c7548ee7e4b969eabd0e4e6a09b537616de 100644 |
| --- a/drivers/media/usb/uvc/uvcvideo.h |
| +++ b/drivers/media/usb/uvc/uvcvideo.h |
| @@ -105,6 +105,8 @@ struct uvc_control_info { |
| |
| u16 size; |
| u32 flags; |
| + |
| + int (*init)(struct uvc_device *dev, struct uvc_control *ctrl); |
| }; |
| |
| struct uvc_control_mapping { |
| -- |
| 2.47.0.rc1.288.g06298d1525-goog |
| |