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@xxxxxxxxxx> --- 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 | 272 ++++++++++++++++++------------- drivers/media/usb/uvc/uvcvideo.h | 3 + 2 files changed, 159 insertions(+), 116 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 3616505e22b4..33199de7f1be 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -30,6 +30,157 @@ #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, 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 */ @@ -373,6 +524,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, }, }; @@ -841,122 +993,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings_uvc15[] = { }, }; -/* ------------------------------------------------------------------------ - * 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, 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 */ @@ -2758,6 +2794,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 c47304a63a7d..c7d20333ca8a 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -86,6 +86,7 @@ struct gpio_desc; struct sg_table; struct uvc_device; +struct uvc_control; /* * TODO: Put the most frequently accessed fields at the beginning of @@ -100,6 +101,8 @@ struct uvc_control_info { u16 size; u32 flags; + + int (*init)(struct uvc_device *dev, struct uvc_control *ctrl); }; struct uvc_control_mapping { -- 2.38.0.135.g90850a2211-goog