]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/media/v4l2-core/v4l2-ctrls.c
[media] v4l2-ctrls/videodev2.h: add u8 and u16 types
[karo-tx-linux.git] / drivers / media / v4l2-core / v4l2-ctrls.c
index 55c68325410205c68b7f54c3353bc143625814de..adf5485e56a869cfda9f1515db02328b2d49d9ae 100644 (file)
@@ -877,7 +877,7 @@ const char *v4l2_ctrl_get_name(u32 id)
 EXPORT_SYMBOL(v4l2_ctrl_get_name);
 
 void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
-                   s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+                   s64 *min, s64 *max, u64 *step, s64 *def, u32 *flags)
 {
        *name = v4l2_ctrl_get_name(id);
        *flags = 0;
@@ -1041,14 +1041,23 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
                *type = V4L2_CTRL_TYPE_INTEGER;
                *flags |= V4L2_CTRL_FLAG_READ_ONLY;
                break;
-       case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
        case V4L2_CID_MPEG_VIDEO_DEC_PTS:
-               *flags |= V4L2_CTRL_FLAG_VOLATILE;
-               /* Fall through */
+               *type = V4L2_CTRL_TYPE_INTEGER64;
+               *flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY;
+               *min = *def = 0;
+               *max = 0x1ffffffffLL;
+               *step = 1;
+               break;
+       case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
+               *type = V4L2_CTRL_TYPE_INTEGER64;
+               *flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY;
+               *min = *def = 0;
+               *max = 0x7fffffffffffffffLL;
+               *step = 1;
+               break;
        case V4L2_CID_PIXEL_RATE:
                *type = V4L2_CTRL_TYPE_INTEGER64;
                *flags |= V4L2_CTRL_FLAG_READ_ONLY;
-               *min = *max = *step = *def = 0;
                break;
        default:
                *type = V4L2_CTRL_TYPE_INTEGER;
@@ -1115,20 +1124,6 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 }
 EXPORT_SYMBOL(v4l2_ctrl_fill);
 
-/* Helper function to determine whether the control type is compatible with
-   VIDIOC_G/S_CTRL. */
-static bool type_is_int(const struct v4l2_ctrl *ctrl)
-{
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_INTEGER64:
-       case V4L2_CTRL_TYPE_STRING:
-               /* Nope, these need v4l2_ext_control */
-               return false;
-       default:
-               return true;
-       }
-}
-
 static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes)
 {
        memset(ev->reserved, 0, sizeof(ev->reserved));
@@ -1137,10 +1132,10 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change
        ev->u.ctrl.changes = changes;
        ev->u.ctrl.type = ctrl->type;
        ev->u.ctrl.flags = ctrl->flags;
-       if (ctrl->type == V4L2_CTRL_TYPE_STRING)
+       if (ctrl->is_ptr)
                ev->u.ctrl.value64 = 0;
        else
-               ev->u.ctrl.value64 = ctrl->cur.val64;
+               ev->u.ctrl.value64 = *ctrl->p_cur.p_s64;
        ev->u.ctrl.minimum = ctrl->minimum;
        ev->u.ctrl.maximum = ctrl->maximum;
        if (ctrl->type == V4L2_CTRL_TYPE_MENU
@@ -1166,42 +1161,250 @@ static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
                        v4l2_event_queue_fh(sev->fh, &ev);
 }
 
-/* Helper function: copy the current control value back to the caller */
-static int cur_to_user(struct v4l2_ext_control *c,
-                      struct v4l2_ctrl *ctrl)
+static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx,
+                     union v4l2_ctrl_ptr ptr1,
+                     union v4l2_ctrl_ptr ptr2)
+{
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_BUTTON:
+               return false;
+       case V4L2_CTRL_TYPE_STRING:
+               idx *= ctrl->elem_size;
+               /* strings are always 0-terminated */
+               return !strcmp(ptr1.p_char + idx, ptr2.p_char + idx);
+       case V4L2_CTRL_TYPE_INTEGER64:
+               return ptr1.p_s64[idx] == ptr2.p_s64[idx];
+       case V4L2_CTRL_TYPE_U8:
+               return ptr1.p_u8[idx] == ptr2.p_u8[idx];
+       case V4L2_CTRL_TYPE_U16:
+               return ptr1.p_u16[idx] == ptr2.p_u16[idx];
+       default:
+               if (ctrl->is_int)
+                       return ptr1.p_s32[idx] == ptr2.p_s32[idx];
+               idx *= ctrl->elem_size;
+               return !memcmp(ptr1.p + idx, ptr2.p + idx, ctrl->elem_size);
+       }
+}
+
+static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
+                    union v4l2_ctrl_ptr ptr)
+{
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_STRING:
+               idx *= ctrl->elem_size;
+               memset(ptr.p_char + idx, ' ', ctrl->minimum);
+               ptr.p_char[idx + ctrl->minimum] = '\0';
+               break;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               ptr.p_s64[idx] = ctrl->default_value;
+               break;
+       case V4L2_CTRL_TYPE_INTEGER:
+       case V4L2_CTRL_TYPE_INTEGER_MENU:
+       case V4L2_CTRL_TYPE_MENU:
+       case V4L2_CTRL_TYPE_BITMASK:
+       case V4L2_CTRL_TYPE_BOOLEAN:
+               ptr.p_s32[idx] = ctrl->default_value;
+               break;
+       case V4L2_CTRL_TYPE_U8:
+               ptr.p_u8[idx] = ctrl->default_value;
+               break;
+       case V4L2_CTRL_TYPE_U16:
+               ptr.p_u16[idx] = ctrl->default_value;
+               break;
+       default:
+               idx *= ctrl->elem_size;
+               memset(ptr.p + idx, 0, ctrl->elem_size);
+               break;
+       }
+}
+
+static void std_log(const struct v4l2_ctrl *ctrl)
+{
+       union v4l2_ctrl_ptr ptr = ctrl->p_cur;
+
+       if (ctrl->is_array) {
+               unsigned i;
+
+               for (i = 0; i < ctrl->nr_of_dims; i++)
+                       pr_cont("[%u]", ctrl->dims[i]);
+               pr_cont(" ");
+       }
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER:
+               pr_cont("%d", *ptr.p_s32);
+               break;
+       case V4L2_CTRL_TYPE_BOOLEAN:
+               pr_cont("%s", *ptr.p_s32 ? "true" : "false");
+               break;
+       case V4L2_CTRL_TYPE_MENU:
+               pr_cont("%s", ctrl->qmenu[*ptr.p_s32]);
+               break;
+       case V4L2_CTRL_TYPE_INTEGER_MENU:
+               pr_cont("%lld", ctrl->qmenu_int[*ptr.p_s32]);
+               break;
+       case V4L2_CTRL_TYPE_BITMASK:
+               pr_cont("0x%08x", *ptr.p_s32);
+               break;
+       case V4L2_CTRL_TYPE_INTEGER64:
+               pr_cont("%lld", *ptr.p_s64);
+               break;
+       case V4L2_CTRL_TYPE_STRING:
+               pr_cont("%s", ptr.p_char);
+               break;
+       case V4L2_CTRL_TYPE_U8:
+               pr_cont("%u", (unsigned)*ptr.p_u8);
+               break;
+       case V4L2_CTRL_TYPE_U16:
+               pr_cont("%u", (unsigned)*ptr.p_u16);
+               break;
+       default:
+               pr_cont("unknown type %d", ctrl->type);
+               break;
+       }
+}
+
+/* Round towards the closest legal value */
+#define ROUND_TO_RANGE(val, offset_type, ctrl)                 \
+({                                                             \
+       offset_type offset;                                     \
+       val += (ctrl)->step / 2;                                \
+       val = clamp_t(typeof(val), val,                         \
+                     (ctrl)->minimum, (ctrl)->maximum);        \
+       offset = (val) - (ctrl)->minimum;                       \
+       offset = (ctrl)->step * (offset / (ctrl)->step);        \
+       val = (ctrl)->minimum + offset;                         \
+       0;                                                      \
+})
+
+/* Validate a new control */
+static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
+                       union v4l2_ctrl_ptr ptr)
+{
+       size_t len;
+
+       switch (ctrl->type) {
+       case V4L2_CTRL_TYPE_INTEGER:
+               return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl);
+       case V4L2_CTRL_TYPE_INTEGER64:
+               return ROUND_TO_RANGE(ptr.p_s64[idx], u64, ctrl);
+       case V4L2_CTRL_TYPE_U8:
+               return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl);
+       case V4L2_CTRL_TYPE_U16:
+               return ROUND_TO_RANGE(ptr.p_u16[idx], u16, ctrl);
+
+       case V4L2_CTRL_TYPE_BOOLEAN:
+               ptr.p_s32[idx] = !!ptr.p_s32[idx];
+               return 0;
+
+       case V4L2_CTRL_TYPE_MENU:
+       case V4L2_CTRL_TYPE_INTEGER_MENU:
+               if (ptr.p_s32[idx] < ctrl->minimum || ptr.p_s32[idx] > ctrl->maximum)
+                       return -ERANGE;
+               if (ctrl->menu_skip_mask & (1 << ptr.p_s32[idx]))
+                       return -EINVAL;
+               if (ctrl->type == V4L2_CTRL_TYPE_MENU &&
+                   ctrl->qmenu[ptr.p_s32[idx]][0] == '\0')
+                       return -EINVAL;
+               return 0;
+
+       case V4L2_CTRL_TYPE_BITMASK:
+               ptr.p_s32[idx] &= ctrl->maximum;
+               return 0;
+
+       case V4L2_CTRL_TYPE_BUTTON:
+       case V4L2_CTRL_TYPE_CTRL_CLASS:
+               ptr.p_s32[idx] = 0;
+               return 0;
+
+       case V4L2_CTRL_TYPE_STRING:
+               idx *= ctrl->elem_size;
+               len = strlen(ptr.p_char + idx);
+               if (len < ctrl->minimum)
+                       return -ERANGE;
+               if ((len - ctrl->minimum) % ctrl->step)
+                       return -ERANGE;
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct v4l2_ctrl_type_ops std_type_ops = {
+       .equal = std_equal,
+       .init = std_init,
+       .log = std_log,
+       .validate = std_validate,
+};
+
+/* Helper function: copy the given control value back to the caller */
+static int ptr_to_user(struct v4l2_ext_control *c,
+                      struct v4l2_ctrl *ctrl,
+                      union v4l2_ctrl_ptr ptr)
 {
        u32 len;
 
+       if (ctrl->is_ptr && !ctrl->is_string)
+               return copy_to_user(c->ptr, ptr.p, c->size) ?
+                      -EFAULT : 0;
+
        switch (ctrl->type) {
        case V4L2_CTRL_TYPE_STRING:
-               len = strlen(ctrl->cur.string);
+               len = strlen(ptr.p_char);
                if (c->size < len + 1) {
-                       c->size = len + 1;
+                       c->size = ctrl->elem_size;
                        return -ENOSPC;
                }
-               return copy_to_user(c->string, ctrl->cur.string,
-                                               len + 1) ? -EFAULT : 0;
+               return copy_to_user(c->string, ptr.p_char, len + 1) ?
+                      -EFAULT : 0;
        case V4L2_CTRL_TYPE_INTEGER64:
-               c->value64 = ctrl->cur.val64;
+               c->value64 = *ptr.p_s64;
                break;
        default:
-               c->value = ctrl->cur.val;
+               c->value = *ptr.p_s32;
                break;
        }
        return 0;
 }
 
-/* Helper function: copy the caller-provider value as the new control value */
-static int user_to_new(struct v4l2_ext_control *c,
+/* Helper function: copy the current control value back to the caller */
+static int cur_to_user(struct v4l2_ext_control *c,
+                      struct v4l2_ctrl *ctrl)
+{
+       return ptr_to_user(c, ctrl, ctrl->p_cur);
+}
+
+/* Helper function: copy the new control value back to the caller */
+static int new_to_user(struct v4l2_ext_control *c,
                       struct v4l2_ctrl *ctrl)
+{
+       return ptr_to_user(c, ctrl, ctrl->p_new);
+}
+
+/* Helper function: copy the caller-provider value to the given control value */
+static int user_to_ptr(struct v4l2_ext_control *c,
+                      struct v4l2_ctrl *ctrl,
+                      union v4l2_ctrl_ptr ptr)
 {
        int ret;
        u32 size;
 
        ctrl->is_new = 1;
+       if (ctrl->is_ptr && !ctrl->is_string) {
+               unsigned idx;
+
+               ret = copy_from_user(ptr.p, c->ptr, c->size) ? -EFAULT : 0;
+               if (ret || !ctrl->is_array)
+                       return ret;
+               for (idx = c->size / ctrl->elem_size; idx < ctrl->elems; idx++)
+                       ctrl->type_ops->init(ctrl, idx, ptr);
+               return 0;
+       }
+
        switch (ctrl->type) {
        case V4L2_CTRL_TYPE_INTEGER64:
-               ctrl->val64 = c->value64;
+               *ptr.p_s64 = c->value64;
                break;
        case V4L2_CTRL_TYPE_STRING:
                size = c->size;
@@ -1209,74 +1412,53 @@ static int user_to_new(struct v4l2_ext_control *c,
                        return -ERANGE;
                if (size > ctrl->maximum + 1)
                        size = ctrl->maximum + 1;
-               ret = copy_from_user(ctrl->string, c->string, size);
+               ret = copy_from_user(ptr.p_char, c->string, size) ? -EFAULT : 0;
                if (!ret) {
-                       char last = ctrl->string[size - 1];
+                       char last = ptr.p_char[size - 1];
 
-                       ctrl->string[size - 1] = 0;
+                       ptr.p_char[size - 1] = 0;
                        /* If the string was longer than ctrl->maximum,
                           then return an error. */
-                       if (strlen(ctrl->string) == ctrl->maximum && last)
+                       if (strlen(ptr.p_char) == ctrl->maximum && last)
                                return -ERANGE;
                }
-               return ret ? -EFAULT : 0;
+               return ret;
        default:
-               ctrl->val = c->value;
+               *ptr.p_s32 = c->value;
                break;
        }
        return 0;
 }
 
-/* Helper function: copy the new control value back to the caller */
-static int new_to_user(struct v4l2_ext_control *c,
+/* 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)
 {
-       u32 len;
+       return user_to_ptr(c, ctrl, ctrl->p_new);
+}
 
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_STRING:
-               len = strlen(ctrl->string);
-               if (c->size < len + 1) {
-                       c->size = ctrl->maximum + 1;
-                       return -ENOSPC;
-               }
-               return copy_to_user(c->string, ctrl->string,
-                                               len + 1) ? -EFAULT : 0;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               c->value64 = ctrl->val64;
-               break;
-       default:
-               c->value = ctrl->val;
-               break;
-       }
-       return 0;
+/* Copy the one value to another. */
+static void ptr_to_ptr(struct v4l2_ctrl *ctrl,
+                      union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to)
+{
+       if (ctrl == NULL)
+               return;
+       memcpy(to.p, from.p, ctrl->elems * ctrl->elem_size);
 }
 
 /* Copy the new value to the current value. */
 static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags)
 {
-       bool changed = false;
+       bool changed;
 
        if (ctrl == NULL)
                return;
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_BUTTON:
-               changed = true;
-               break;
-       case V4L2_CTRL_TYPE_STRING:
-               /* strings are always 0-terminated */
-               changed = strcmp(ctrl->string, ctrl->cur.string);
-               strcpy(ctrl->cur.string, ctrl->string);
-               break;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               changed = ctrl->val64 != ctrl->cur.val64;
-               ctrl->cur.val64 = ctrl->val64;
-               break;
-       default:
-               changed = ctrl->val != ctrl->cur.val;
-               ctrl->cur.val = ctrl->val;
-               break;
-       }
+
+       /* has_changed is set by cluster_changed */
+       changed = ctrl->has_changed;
+       if (changed)
+               ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur);
+
        if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) {
                /* Note: CH_FLAGS is only set for auto clusters. */
                ctrl->flags &=
@@ -1305,62 +1487,46 @@ static void cur_to_new(struct v4l2_ctrl *ctrl)
 {
        if (ctrl == NULL)
                return;
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_STRING:
-               /* strings are always 0-terminated */
-               strcpy(ctrl->string, ctrl->cur.string);
-               break;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               ctrl->val64 = ctrl->cur.val64;
-               break;
-       default:
-               ctrl->val = ctrl->cur.val;
-               break;
-       }
+       ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new);
 }
 
 /* Return non-zero if one or more of the controls in the cluster has a new
    value that differs from the current value. */
 static int cluster_changed(struct v4l2_ctrl *master)
 {
-       int diff = 0;
+       bool changed = false;
+       unsigned idx;
        int i;
 
-       for (i = 0; !diff && i < master->ncontrols; i++) {
+       for (i = 0; i < master->ncontrols; i++) {
                struct v4l2_ctrl *ctrl = master->cluster[i];
+               bool ctrl_changed = false;
 
                if (ctrl == NULL)
                        continue;
-               switch (ctrl->type) {
-               case V4L2_CTRL_TYPE_BUTTON:
-                       /* Button controls are always 'different' */
-                       return 1;
-               case V4L2_CTRL_TYPE_STRING:
-                       /* strings are always 0-terminated */
-                       diff = strcmp(ctrl->string, ctrl->cur.string);
-                       break;
-               case V4L2_CTRL_TYPE_INTEGER64:
-                       diff = ctrl->val64 != ctrl->cur.val64;
-                       break;
-               default:
-                       diff = ctrl->val != ctrl->cur.val;
-                       break;
-               }
+               for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++)
+                       ctrl_changed = !ctrl->type_ops->equal(ctrl, idx,
+                               ctrl->p_cur, ctrl->p_new);
+               ctrl->has_changed = ctrl_changed;
+               changed |= ctrl->has_changed;
        }
-       return diff;
+       return changed;
 }
 
 /* Control range checking */
 static int check_range(enum v4l2_ctrl_type type,
-               s32 min, s32 max, u32 step, s32 def)
+               s64 min, s64 max, u64 step, s64 def)
 {
        switch (type) {
        case V4L2_CTRL_TYPE_BOOLEAN:
                if (step != 1 || max > 1 || min < 0)
                        return -ERANGE;
                /* fall through */
+       case V4L2_CTRL_TYPE_U8:
+       case V4L2_CTRL_TYPE_U16:
        case V4L2_CTRL_TYPE_INTEGER:
-               if (step <= 0 || min > max || def < min || def > max)
+       case V4L2_CTRL_TYPE_INTEGER64:
+               if (step == 0 || min > max || def < min || def > max)
                        return -ERANGE;
                return 0;
        case V4L2_CTRL_TYPE_BITMASK:
@@ -1389,58 +1555,33 @@ static int check_range(enum v4l2_ctrl_type type,
 static int validate_new(const struct v4l2_ctrl *ctrl,
                        struct v4l2_ext_control *c)
 {
-       size_t len;
-       u32 offset;
-       s32 val;
-
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_INTEGER:
-               /* Round towards the closest legal value */
-               val = c->value + ctrl->step / 2;
-               val = clamp(val, ctrl->minimum, ctrl->maximum);
-               offset = val - ctrl->minimum;
-               offset = ctrl->step * (offset / ctrl->step);
-               c->value = ctrl->minimum + offset;
-               return 0;
-
-       case V4L2_CTRL_TYPE_BOOLEAN:
-               c->value = !!c->value;
-               return 0;
-
-       case V4L2_CTRL_TYPE_MENU:
-       case V4L2_CTRL_TYPE_INTEGER_MENU:
-               if (c->value < ctrl->minimum || c->value > ctrl->maximum)
-                       return -ERANGE;
-               if (ctrl->menu_skip_mask & (1 << c->value))
-                       return -EINVAL;
-               if (ctrl->type == V4L2_CTRL_TYPE_MENU &&
-                   ctrl->qmenu[c->value][0] == '\0')
-                       return -EINVAL;
-               return 0;
+       union v4l2_ctrl_ptr ptr;
+       unsigned idx;
+       int err = 0;
 
-       case V4L2_CTRL_TYPE_BITMASK:
-               c->value &= ctrl->maximum;
-               return 0;
-
-       case V4L2_CTRL_TYPE_BUTTON:
-       case V4L2_CTRL_TYPE_CTRL_CLASS:
-               c->value = 0;
-               return 0;
-
-       case V4L2_CTRL_TYPE_INTEGER64:
-               return 0;
-
-       case V4L2_CTRL_TYPE_STRING:
-               len = strlen(c->string);
-               if (len < ctrl->minimum)
-                       return -ERANGE;
-               if ((len - ctrl->minimum) % ctrl->step)
-                       return -ERANGE;
-               return 0;
+       if (!ctrl->is_ptr) {
+               switch (ctrl->type) {
+               case V4L2_CTRL_TYPE_INTEGER:
+               case V4L2_CTRL_TYPE_INTEGER_MENU:
+               case V4L2_CTRL_TYPE_MENU:
+               case V4L2_CTRL_TYPE_BITMASK:
+               case V4L2_CTRL_TYPE_BOOLEAN:
+               case V4L2_CTRL_TYPE_BUTTON:
+               case V4L2_CTRL_TYPE_CTRL_CLASS:
+                       ptr.p_s32 = &c->value;
+                       return ctrl->type_ops->validate(ctrl, 0, ptr);
 
-       default:
-               return -EINVAL;
+               case V4L2_CTRL_TYPE_INTEGER64:
+                       ptr.p_s64 = &c->value64;
+                       return ctrl->type_ops->validate(ctrl, 0, ptr);
+               default:
+                       break;
+               }
        }
+       ptr.p = c->ptr;
+       for (idx = 0; !err && idx < c->size / ctrl->elem_size; idx++)
+               err = ctrl->type_ops->validate(ctrl, idx, ptr);
+       return err;
 }
 
 static inline u32 node2id(struct list_head *node)
@@ -1522,7 +1663,7 @@ static struct v4l2_ctrl_ref *find_private_ref(
                   VIDIOC_G/S_CTRL. */
                if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
                    V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) {
-                       if (!type_is_int(ref->ctrl))
+                       if (!ref->ctrl->is_int)
                                continue;
                        if (id == 0)
                                return ref;
@@ -1592,8 +1733,12 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
        u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
        int bucket = id % hdl->nr_of_buckets;   /* which bucket to use */
 
-       /* Automatically add the control class if it is not yet present. */
-       if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
+       /*
+        * Automatically add the control class if it is not yet present and
+        * the new control is not a compound control.
+        */
+       if (ctrl->type < V4L2_CTRL_COMPOUND_TYPES &&
+           id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
                if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
                        return hdl->error;
 
@@ -1652,20 +1797,58 @@ unlock:
 /* Add a new control */
 static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
                        const struct v4l2_ctrl_ops *ops,
+                       const struct v4l2_ctrl_type_ops *type_ops,
                        u32 id, const char *name, enum v4l2_ctrl_type type,
-                       s32 min, s32 max, u32 step, s32 def,
+                       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, void *priv)
 {
        struct v4l2_ctrl *ctrl;
-       unsigned sz_extra = 0;
+       unsigned sz_extra;
+       unsigned nr_of_dims = 0;
+       unsigned elems = 1;
+       bool is_array;
+       unsigned tot_ctrl_size;
+       unsigned idx;
+       void *data;
        int err;
 
        if (hdl->error)
                return NULL;
 
+       while (dims && dims[nr_of_dims]) {
+               elems *= dims[nr_of_dims];
+               nr_of_dims++;
+               if (nr_of_dims == V4L2_CTRL_MAX_DIMS)
+                       break;
+       }
+       is_array = nr_of_dims > 0;
+
+       /* Prefill elem_size for all types handled by std_type_ops */
+       switch (type) {
+       case V4L2_CTRL_TYPE_INTEGER64:
+               elem_size = sizeof(s64);
+               break;
+       case V4L2_CTRL_TYPE_STRING:
+               elem_size = max + 1;
+               break;
+       case V4L2_CTRL_TYPE_U8:
+               elem_size = sizeof(u8);
+               break;
+       case V4L2_CTRL_TYPE_U16:
+               elem_size = sizeof(u16);
+               break;
+       default:
+               if (type < V4L2_CTRL_COMPOUND_TYPES)
+                       elem_size = sizeof(s32);
+               break;
+       }
+       tot_ctrl_size = elem_size * elems;
+
        /* Sanity checks */
-       if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
+       if (id == 0 || name == NULL || !elem_size ||
+           id >= V4L2_CID_PRIVATE_BASE ||
            (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
            (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) {
                handler_set_err(hdl, -ERANGE);
@@ -1680,13 +1863,23 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
                handler_set_err(hdl, -ERANGE);
                return NULL;
        }
+       if (is_array &&
+           (type == V4L2_CTRL_TYPE_BUTTON ||
+            type == V4L2_CTRL_TYPE_CTRL_CLASS)) {
+               handler_set_err(hdl, -EINVAL);
+               return NULL;
+       }
 
+       sz_extra = 0;
        if (type == V4L2_CTRL_TYPE_BUTTON)
                flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
        else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
                flags |= V4L2_CTRL_FLAG_READ_ONLY;
-       else if (type == V4L2_CTRL_TYPE_STRING)
-               sz_extra += 2 * (max + 1);
+       else if (type == V4L2_CTRL_TYPE_INTEGER64 ||
+                type == V4L2_CTRL_TYPE_STRING ||
+                type >= V4L2_CTRL_COMPOUND_TYPES ||
+                is_array)
+               sz_extra += 2 * tot_ctrl_size;
 
        ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
        if (ctrl == NULL) {
@@ -1698,6 +1891,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
        INIT_LIST_HEAD(&ctrl->ev_subs);
        ctrl->handler = hdl;
        ctrl->ops = ops;
+       ctrl->type_ops = type_ops ? type_ops : &std_type_ops;
        ctrl->id = id;
        ctrl->name = name;
        ctrl->type = type;
@@ -1705,19 +1899,36 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
        ctrl->minimum = min;
        ctrl->maximum = max;
        ctrl->step = step;
+       ctrl->default_value = def;
+       ctrl->is_string = !is_array && type == V4L2_CTRL_TYPE_STRING;
+       ctrl->is_ptr = is_array || type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string;
+       ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64;
+       ctrl->is_array = is_array;
+       ctrl->elems = elems;
+       ctrl->nr_of_dims = nr_of_dims;
+       if (nr_of_dims)
+               memcpy(ctrl->dims, dims, nr_of_dims * sizeof(dims[0]));
+       ctrl->elem_size = elem_size;
        if (type == V4L2_CTRL_TYPE_MENU)
                ctrl->qmenu = qmenu;
        else if (type == V4L2_CTRL_TYPE_INTEGER_MENU)
                ctrl->qmenu_int = qmenu_int;
        ctrl->priv = priv;
-       ctrl->cur.val = ctrl->val = ctrl->default_value = def;
+       ctrl->cur.val = ctrl->val = def;
+       data = &ctrl[1];
 
-       if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
-               ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
-               ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
-               if (ctrl->minimum)
-                       memset(ctrl->cur.string, ' ', ctrl->minimum);
+       if (!ctrl->is_int) {
+               ctrl->p_new.p = data;
+               ctrl->p_cur.p = data + tot_ctrl_size;
+       } else {
+               ctrl->p_new.p = &ctrl->val;
+               ctrl->p_cur.p = &ctrl->cur.val;
+       }
+       for (idx = 0; idx < elems; idx++) {
+               ctrl->type_ops->init(ctrl, idx, ctrl->p_cur);
+               ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
        }
+
        if (handler_new_ref(hdl, ctrl)) {
                kfree(ctrl);
                return NULL;
@@ -1738,10 +1949,10 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
        const s64 *qmenu_int = cfg->qmenu_int;
        enum v4l2_ctrl_type type = cfg->type;
        u32 flags = cfg->flags;
-       s32 min = cfg->min;
-       s32 max = cfg->max;
-       u32 step = cfg->step;
-       s32 def = cfg->def;
+       s64 min = cfg->min;
+       s64 max = cfg->max;
+       u64 step = cfg->step;
+       s64 def = cfg->def;
 
        if (name == NULL)
                v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step,
@@ -1761,10 +1972,11 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
                return NULL;
        }
 
-       ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name,
+       ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->type_ops, cfg->id, name,
                        type, min, max,
-                       is_menu ? cfg->menu_skip_mask : step,
-                       def, flags, qmenu, qmenu_int, priv);
+                       is_menu ? cfg->menu_skip_mask : step, def,
+                       cfg->dims, cfg->elem_size,
+                       flags, qmenu, qmenu_int, priv);
        if (ctrl)
                ctrl->is_private = cfg->is_private;
        return ctrl;
@@ -1774,35 +1986,39 @@ EXPORT_SYMBOL(v4l2_ctrl_new_custom);
 /* Helper function for standard non-menu controls */
 struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
                        const struct v4l2_ctrl_ops *ops,
-                       u32 id, s32 min, s32 max, u32 step, s32 def)
+                       u32 id, s64 min, s64 max, u64 step, s64 def)
 {
        const char *name;
        enum v4l2_ctrl_type type;
        u32 flags;
 
        v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
-       if (type == V4L2_CTRL_TYPE_MENU
-           || type == V4L2_CTRL_TYPE_INTEGER_MENU) {
+       if (type == V4L2_CTRL_TYPE_MENU ||
+           type == V4L2_CTRL_TYPE_INTEGER_MENU ||
+           type >= V4L2_CTRL_COMPOUND_TYPES) {
                handler_set_err(hdl, -EINVAL);
                return NULL;
        }
-       return v4l2_ctrl_new(hdl, ops, id, name, type,
-                            min, max, step, def, flags, NULL, NULL, NULL);
+       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
+                            min, max, step, def, NULL, 0,
+                            flags, NULL, NULL, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_std);
 
 /* Helper function for standard menu controls */
 struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
                        const struct v4l2_ctrl_ops *ops,
-                       u32 id, s32 max, s32 mask, s32 def)
+                       u32 id, u8 _max, u64 mask, u8 _def)
 {
        const char * const *qmenu = NULL;
        const s64 *qmenu_int = NULL;
        unsigned int qmenu_int_len = 0;
        const char *name;
        enum v4l2_ctrl_type type;
-       s32 min;
-       s32 step;
+       s64 min;
+       s64 max = _max;
+       s64 def = _def;
+       u64 step;
        u32 flags;
 
        v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
@@ -1816,21 +2032,24 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
                handler_set_err(hdl, -EINVAL);
                return NULL;
        }
-       return v4l2_ctrl_new(hdl, ops, id, name, type,
-                            0, max, mask, def, flags, qmenu, qmenu_int, NULL);
+       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
+                            0, max, mask, def, NULL, 0,
+                            flags, qmenu, qmenu_int, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
 
 /* Helper function for standard menu controls with driver defined menu */
 struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
-                       const struct v4l2_ctrl_ops *ops, u32 id, s32 max,
-                       s32 mask, s32 def, const char * const *qmenu)
+                       const struct v4l2_ctrl_ops *ops, u32 id, u8 _max,
+                       u64 mask, u8 _def, const char * const *qmenu)
 {
        enum v4l2_ctrl_type type;
        const char *name;
        u32 flags;
-       s32 step;
-       s32 min;
+       u64 step;
+       s64 min;
+       s64 max = _max;
+       s64 def = _def;
 
        /* v4l2_ctrl_new_std_menu_items() should only be called for
         * standard controls without a standard menu.
@@ -1845,7 +2064,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
                handler_set_err(hdl, -EINVAL);
                return NULL;
        }
-       return v4l2_ctrl_new(hdl, ops, id, name, type, 0, max, mask, def,
+       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
+                            0, max, mask, def, NULL, 0,
                             flags, qmenu, NULL, NULL);
 
 }
@@ -1854,12 +2074,14 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
 /* Helper function for standard integer menu controls */
 struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
                        const struct v4l2_ctrl_ops *ops,
-                       u32 id, s32 max, s32 def, const s64 *qmenu_int)
+                       u32 id, u8 _max, u8 _def, const s64 *qmenu_int)
 {
        const char *name;
        enum v4l2_ctrl_type type;
-       s32 min;
-       s32 step;
+       s64 min;
+       u64 step;
+       s64 max = _max;
+       s64 def = _def;
        u32 flags;
 
        v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
@@ -1867,8 +2089,9 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
                handler_set_err(hdl, -EINVAL);
                return NULL;
        }
-       return v4l2_ctrl_new(hdl, ops, id, name, type,
-                            0, max, 0, def, flags, NULL, qmenu_int, NULL);
+       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
+                            0, max, 0, def, NULL, 0,
+                            flags, NULL, qmenu_int, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
 
@@ -2048,45 +2271,21 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl,
        if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
                return;
 
-       printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name);
+       pr_info("%s%s%s: ", prefix, colon, ctrl->name);
+
+       ctrl->type_ops->log(ctrl);
 
-       switch (ctrl->type) {
-       case V4L2_CTRL_TYPE_INTEGER:
-               printk(KERN_CONT "%d", ctrl->cur.val);
-               break;
-       case V4L2_CTRL_TYPE_BOOLEAN:
-               printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false");
-               break;
-       case V4L2_CTRL_TYPE_MENU:
-               printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
-               break;
-       case V4L2_CTRL_TYPE_INTEGER_MENU:
-               printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]);
-               break;
-       case V4L2_CTRL_TYPE_BITMASK:
-               printk(KERN_CONT "0x%08x", ctrl->cur.val);
-               break;
-       case V4L2_CTRL_TYPE_INTEGER64:
-               printk(KERN_CONT "%lld", ctrl->cur.val64);
-               break;
-       case V4L2_CTRL_TYPE_STRING:
-               printk(KERN_CONT "%s", ctrl->cur.string);
-               break;
-       default:
-               printk(KERN_CONT "unknown type %d", ctrl->type);
-               break;
-       }
        if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE |
                           V4L2_CTRL_FLAG_GRABBED |
                           V4L2_CTRL_FLAG_VOLATILE)) {
                if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
-                       printk(KERN_CONT " inactive");
+                       pr_cont(" inactive");
                if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)
-                       printk(KERN_CONT " grabbed");
+                       pr_cont(" grabbed");
                if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE)
-                       printk(KERN_CONT " volatile");
+                       pr_cont(" volatile");
        }
-       printk(KERN_CONT "\n");
+       pr_cont("\n");
 }
 
 /* Log all controls owned by the handler */
@@ -2157,9 +2356,10 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
 }
 EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
 
-/* Implement VIDIOC_QUERYCTRL */
-int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+/* Implement VIDIOC_QUERY_EXT_CTRL */
+int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctrl *qc)
 {
+       const unsigned next_flags = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
        u32 id = qc->id & V4L2_CTRL_ID_MASK;
        struct v4l2_ctrl_ref *ref;
        struct v4l2_ctrl *ctrl;
@@ -2172,7 +2372,20 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
        /* Try to find it */
        ref = find_ref(hdl, id);
 
-       if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) {
+       if ((qc->id & next_flags) && !list_empty(&hdl->ctrl_refs)) {
+               bool is_compound;
+               /* Match any control that is not hidden */
+               unsigned mask = 1;
+               bool match = false;
+
+               if ((qc->id & next_flags) == V4L2_CTRL_FLAG_NEXT_COMPOUND) {
+                       /* Match any hidden control */
+                       match = true;
+               } else if ((qc->id & next_flags) == next_flags) {
+                       /* Match any control, compound or not */
+                       mask = 0;
+               }
+
                /* Find the next control with ID > qc->id */
 
                /* Did we reach the end of the control list? */
@@ -2180,19 +2393,34 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
                        ref = NULL; /* Yes, so there is no next control */
                } else if (ref) {
                        /* We found a control with the given ID, so just get
-                          the next one in the list. */
-                       ref = list_entry(ref->node.next, typeof(*ref), node);
+                          the next valid one in the list. */
+                       list_for_each_entry_continue(ref, &hdl->ctrl_refs, node) {
+                               is_compound =
+                                       ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES;
+                               if (id < ref->ctrl->id &&
+                                   (is_compound & mask) == match)
+                                       break;
+                       }
+                       if (&ref->node == &hdl->ctrl_refs)
+                               ref = NULL;
                } else {
                        /* No control with the given ID exists, so start
                           searching for the next largest ID. We know there
                           is one, otherwise the first 'if' above would have
                           been true. */
-                       list_for_each_entry(ref, &hdl->ctrl_refs, node)
-                               if (id < ref->ctrl->id)
+                       list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+                               is_compound =
+                                       ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES;
+                               if (id < ref->ctrl->id &&
+                                   (is_compound & mask) == match)
                                        break;
+                       }
+                       if (&ref->node == &hdl->ctrl_refs)
+                               ref = NULL;
                }
        }
        mutex_unlock(hdl->lock);
+
        if (!ref)
                return -EINVAL;
 
@@ -2203,6 +2431,14 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
        else
                qc->id = ctrl->id;
        strlcpy(qc->name, ctrl->name, sizeof(qc->name));
+       qc->flags = ctrl->flags;
+       qc->type = ctrl->type;
+       if (ctrl->is_ptr)
+               qc->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
+       qc->elem_size = ctrl->elem_size;
+       qc->elems = ctrl->elems;
+       qc->nr_of_dims = ctrl->nr_of_dims;
+       memcpy(qc->dims, ctrl->dims, qc->nr_of_dims * sizeof(qc->dims[0]));
        qc->minimum = ctrl->minimum;
        qc->maximum = ctrl->maximum;
        qc->default_value = ctrl->default_value;
@@ -2211,15 +2447,50 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
                qc->step = 1;
        else
                qc->step = ctrl->step;
-       qc->flags = ctrl->flags;
-       qc->type = ctrl->type;
+       return 0;
+}
+EXPORT_SYMBOL(v4l2_query_ext_ctrl);
+
+/* Implement VIDIOC_QUERYCTRL */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+{
+       struct v4l2_query_ext_ctrl qec = { qc->id };
+       int rc;
+
+       rc = v4l2_query_ext_ctrl(hdl, &qec);
+       if (rc)
+               return rc;
+
+       qc->id = qec.id;
+       qc->type = qec.type;
+       qc->flags = qec.flags;
+       strlcpy(qc->name, qec.name, sizeof(qc->name));
+       switch (qc->type) {
+       case V4L2_CTRL_TYPE_INTEGER:
+       case V4L2_CTRL_TYPE_BOOLEAN:
+       case V4L2_CTRL_TYPE_MENU:
+       case V4L2_CTRL_TYPE_INTEGER_MENU:
+       case V4L2_CTRL_TYPE_STRING:
+       case V4L2_CTRL_TYPE_BITMASK:
+               qc->minimum = qec.minimum;
+               qc->maximum = qec.maximum;
+               qc->step = qec.step;
+               qc->default_value = qec.default_value;
+               break;
+       default:
+               qc->minimum = 0;
+               qc->maximum = 0;
+               qc->step = 0;
+               qc->default_value = 0;
+               break;
+       }
        return 0;
 }
 EXPORT_SYMBOL(v4l2_queryctrl);
 
 int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
 {
-       if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL)
+       if (qc->id & (V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND))
                return -EINVAL;
        return v4l2_queryctrl(sd->ctrl_handler, qc);
 }
@@ -2319,7 +2590,8 @@ EXPORT_SYMBOL(v4l2_subdev_querymenu);
    Find the controls in the control array and do some basic checks. */
 static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
                             struct v4l2_ext_controls *cs,
-                            struct v4l2_ctrl_helper *helpers)
+                            struct v4l2_ctrl_helper *helpers,
+                            bool get)
 {
        struct v4l2_ctrl_helper *h;
        bool have_clusters = false;
@@ -2351,6 +2623,18 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
                        have_clusters = true;
                if (ctrl->cluster[0] != ctrl)
                        ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
+               if (ctrl->is_ptr && !ctrl->is_string) {
+                       unsigned tot_size = ctrl->elems * ctrl->elem_size;
+
+                       if (c->size < tot_size) {
+                               if (get) {
+                                       c->size = tot_size;
+                                       return -ENOSPC;
+                               }
+                               return -EFAULT;
+                       }
+                       c->size = tot_size;
+               }
                /* Store the ref to the master control of the cluster */
                h->mref = ref;
                h->ctrl = ctrl;
@@ -2431,7 +2715,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
                        return -ENOMEM;
        }
 
-       ret = prepare_ext_ctrls(hdl, cs, helpers);
+       ret = prepare_ext_ctrls(hdl, cs, helpers, true);
        cs->error_idx = cs->count;
 
        for (i = 0; !ret && i < cs->count; i++)
@@ -2493,11 +2777,11 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)
        int ret = 0;
        int i;
 
-       /* String controls are not supported. The new_to_user() and
+       /* Compound controls are not supported. The new_to_user() and
         * cur_to_user() calls below would need to be modified not to access
         * userspace memory when called from get_ctrl().
         */
-       if (ctrl->type == V4L2_CTRL_TYPE_STRING)
+       if (!ctrl->is_int)
                return -EINVAL;
 
        if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
@@ -2523,7 +2807,7 @@ int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
        struct v4l2_ext_control c;
        int ret;
 
-       if (ctrl == NULL || !type_is_int(ctrl))
+       if (ctrl == NULL || !ctrl->is_int)
                return -EINVAL;
        ret = get_ctrl(ctrl, &c);
        control->value = c.value;
@@ -2542,7 +2826,7 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
        struct v4l2_ext_control c;
 
        /* It's a driver bug if this happens. */
-       WARN_ON(!type_is_int(ctrl));
+       WARN_ON(!ctrl->is_int);
        c.value = 0;
        get_ctrl(ctrl, &c);
        return c.value;
@@ -2554,7 +2838,7 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl)
        struct v4l2_ext_control c;
 
        /* It's a driver bug if this happens. */
-       WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64);
+       WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64);
        c.value = 0;
        get_ctrl(ctrl, &c);
        return c.value;
@@ -2678,7 +2962,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
                if (!helpers)
                        return -ENOMEM;
        }
-       ret = prepare_ext_ctrls(hdl, cs, helpers);
+       ret = prepare_ext_ctrls(hdl, cs, helpers, false);
        if (!ret)
                ret = validate_ctrls(cs, helpers, set);
        if (ret && set)
@@ -2783,11 +3067,11 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
        struct v4l2_ctrl *master = ctrl->cluster[0];
        int i;
 
-       /* String controls are not supported. The user_to_new() and
+       /* Compound controls are not supported. The user_to_new() and
         * cur_to_user() calls below would need to be modified not to access
         * userspace memory when called from set_ctrl().
         */
-       if (ctrl->type == V4L2_CTRL_TYPE_STRING)
+       if (ctrl->is_ptr)
                return -EINVAL;
 
        /* Reset the 'is_new' flags of the cluster */
@@ -2829,7 +3113,7 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
        struct v4l2_ext_control c;
        int ret;
 
-       if (ctrl == NULL || !type_is_int(ctrl))
+       if (ctrl == NULL || !ctrl->is_int)
                return -EINVAL;
 
        if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
@@ -2853,7 +3137,7 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
        struct v4l2_ext_control c;
 
        /* It's a driver bug if this happens. */
-       WARN_ON(!type_is_int(ctrl));
+       WARN_ON(!ctrl->is_int);
        c.value = val;
        return set_ctrl_lock(NULL, ctrl, &c);
 }
@@ -2864,7 +3148,7 @@ int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val)
        struct v4l2_ext_control c;
 
        /* It's a driver bug if this happens. */
-       WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64);
+       WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64);
        c.value64 = val;
        return set_ctrl_lock(NULL, ctrl, &c);
 }
@@ -2887,17 +3171,23 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void
 EXPORT_SYMBOL(v4l2_ctrl_notify);
 
 int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
-                       s32 min, s32 max, u32 step, s32 def)
+                       s64 min, s64 max, u64 step, s64 def)
 {
-       int ret = check_range(ctrl->type, min, max, step, def);
+       int ret;
        struct v4l2_ext_control c;
 
        switch (ctrl->type) {
        case V4L2_CTRL_TYPE_INTEGER:
+       case V4L2_CTRL_TYPE_INTEGER64:
        case V4L2_CTRL_TYPE_BOOLEAN:
        case V4L2_CTRL_TYPE_MENU:
        case V4L2_CTRL_TYPE_INTEGER_MENU:
        case V4L2_CTRL_TYPE_BITMASK:
+       case V4L2_CTRL_TYPE_U8:
+       case V4L2_CTRL_TYPE_U16:
+               if (ctrl->is_array)
+                       return -EINVAL;
+               ret = check_range(ctrl->type, min, max, step, def);
                if (ret)
                        return ret;
                break;
@@ -2909,10 +3199,10 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
        ctrl->maximum = max;
        ctrl->step = step;
        ctrl->default_value = def;
-       c.value = ctrl->cur.val;
+       c.value = *ctrl->p_cur.p_s32;
        if (validate_new(ctrl, &c))
                c.value = def;
-       if (c.value != ctrl->cur.val)
+       if (c.value != *ctrl->p_cur.p_s32)
                ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE);
        else
                send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE);