aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch688
1 files changed, 688 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch b/target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch
new file mode 100644
index 0000000000..03074222fe
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch
@@ -0,0 +1,688 @@
+From 613b860fe9c37b96d9706b02fe77933f086f9896 Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Thu, 15 Apr 2021 13:56:53 +0200
+Subject: [PATCH] v4l2-ctrls: add support for dynamically allocated
+ arrays.
+
+Implement support for dynamically allocated arrays.
+
+Most of the changes concern keeping track of the number of elements
+of the array and the number of elements allocated for the array and
+reallocating memory if needed.
+
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+(cherry picked from commit fd5d45e6561f6f8c406b81aeddecaa11f0bd15af)
+---
+ drivers/media/v4l2-core/v4l2-ctrls-api.c | 103 ++++++++---
+ drivers/media/v4l2-core/v4l2-ctrls-core.c | 182 +++++++++++++++----
+ drivers/media/v4l2-core/v4l2-ctrls-priv.h | 3 +-
+ drivers/media/v4l2-core/v4l2-ctrls-request.c | 13 +-
+ include/media/v4l2-ctrls.h | 42 ++++-
+ 5 files changed, 272 insertions(+), 71 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
+@@ -97,29 +97,47 @@ static int def_to_user(struct v4l2_ext_c
+ 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)
++/* 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)
+ {
+ int ret;
+ u32 size;
+
+- ctrl->is_new = 1;
++ ctrl->is_new = 0;
++ if (ctrl->is_dyn_array &&
++ c->size > ctrl->p_dyn_alloc_elems * ctrl->elem_size) {
++ void *old = ctrl->p_dyn;
++ void *tmp = kvzalloc(2 * c->size, GFP_KERNEL);
++
++ if (!tmp)
++ return -ENOMEM;
++ memcpy(tmp, ctrl->p_new.p, ctrl->elems * ctrl->elem_size);
++ memcpy(tmp + c->size, ctrl->p_cur.p, ctrl->elems * ctrl->elem_size);
++ ctrl->p_new.p = tmp;
++ ctrl->p_cur.p = tmp + c->size;
++ ctrl->p_dyn = tmp;
++ ctrl->p_dyn_alloc_elems = c->size / ctrl->elem_size;
++ kvfree(old);
++ }
++
+ if (ctrl->is_ptr && !ctrl->is_string) {
++ unsigned int elems = c->size / ctrl->elem_size;
+ unsigned int 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);
++ if (copy_from_user(ctrl->p_new.p, c->ptr, c->size))
++ return -EFAULT;
++ ctrl->is_new = 1;
++ if (ctrl->is_dyn_array)
++ ctrl->new_elems = elems;
++ else if (ctrl->is_array)
++ for (idx = elems; idx < ctrl->elems; idx++)
++ ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
+ return 0;
+ }
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+- *ptr.p_s64 = c->value64;
++ *ctrl->p_new.p_s64 = c->value64;
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ size = c->size;
+@@ -127,32 +145,27 @@ static int user_to_ptr(struct v4l2_ext_c
+ return -ERANGE;
+ if (size > ctrl->maximum + 1)
+ size = ctrl->maximum + 1;
+- ret = copy_from_user(ptr.p_char, c->string, size) ? -EFAULT : 0;
++ ret = copy_from_user(ctrl->p_new.p_char, c->string, size) ? -EFAULT : 0;
+ if (!ret) {
+- char last = ptr.p_char[size - 1];
++ char last = ctrl->p_new.p_char[size - 1];
+
+- ptr.p_char[size - 1] = 0;
++ ctrl->p_new.p_char[size - 1] = 0;
+ /*
+ * If the string was longer than ctrl->maximum,
+ * then return an error.
+ */
+- if (strlen(ptr.p_char) == ctrl->maximum && last)
++ if (strlen(ctrl->p_new.p_char) == ctrl->maximum && last)
+ return -ERANGE;
+ }
+ return ret;
+ default:
+- *ptr.p_s32 = c->value;
++ *ctrl->p_new.p_s32 = c->value;
+ break;
+ }
++ ctrl->is_new = 1;
+ return 0;
+ }
+
+-/* 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)
+-{
+- return user_to_ptr(c, ctrl, ctrl->p_new);
+-}
+-
+ /*
+ * VIDIOC_G/TRY/S_EXT_CTRLS implementation
+ */
+@@ -254,7 +267,31 @@ static int prepare_ext_ctrls(struct v4l2
+ have_clusters = true;
+ if (ctrl->cluster[0] != ctrl)
+ ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
+- if (ctrl->is_ptr && !ctrl->is_string) {
++ if (ctrl->is_dyn_array) {
++ unsigned int max_size = ctrl->dims[0] * ctrl->elem_size;
++ unsigned int tot_size = ctrl->elem_size;
++
++ if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL)
++ tot_size *= ref->p_req_elems;
++ else
++ tot_size *= ctrl->elems;
++
++ c->size = ctrl->elem_size * (c->size / ctrl->elem_size);
++ if (get) {
++ if (c->size < tot_size) {
++ c->size = tot_size;
++ return -ENOSPC;
++ }
++ c->size = tot_size;
++ } else {
++ if (c->size > max_size) {
++ c->size = max_size;
++ return -ENOSPC;
++ }
++ if (!c->size)
++ return -EFAULT;
++ }
++ } else if (ctrl->is_ptr && !ctrl->is_string) {
+ unsigned int tot_size = ctrl->elems * ctrl->elem_size;
+
+ if (c->size < tot_size) {
+@@ -346,7 +383,7 @@ static int class_check(struct v4l2_ctrl_
+ *
+ * Note that v4l2_g_ext_ctrls_common() with 'which' set to
+ * V4L2_CTRL_WHICH_REQUEST_VAL is only called if the request was
+- * completed, and in that case valid_p_req is true for all controls.
++ * completed, and in that case p_req_valid is true for all controls.
+ */
+ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+@@ -430,7 +467,9 @@ int v4l2_g_ext_ctrls_common(struct v4l2_
+
+ if (is_default)
+ ret = def_to_user(cs->controls + idx, ref->ctrl);
+- else if (is_request && ref->valid_p_req)
++ else if (is_request && ref->p_req_dyn_enomem)
++ ret = -ENOMEM;
++ else if (is_request && ref->p_req_valid)
+ ret = req_to_user(cs->controls + idx, ref);
+ else if (is_volatile)
+ ret = new_to_user(cs->controls + idx, ref->ctrl);
+@@ -457,6 +496,17 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_ha
+ }
+ EXPORT_SYMBOL(v4l2_g_ext_ctrls);
+
++/* Validate a new control */
++static int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new)
++{
++ unsigned int idx;
++ int err = 0;
++
++ for (idx = 0; !err && idx < ctrl->new_elems; idx++)
++ err = ctrl->type_ops->validate(ctrl, idx, p_new);
++ return err;
++}
++
+ /* Validate controls. */
+ static int validate_ctrls(struct v4l2_ext_controls *cs,
+ struct v4l2_ctrl_helper *helpers,
+@@ -872,6 +922,9 @@ int __v4l2_ctrl_s_ctrl_compound(struct v
+ /* It's a driver bug if this happens. */
+ if (WARN_ON(ctrl->type != type))
+ return -EINVAL;
++ /* Setting dynamic arrays is not (yet?) supported. */
++ if (WARN_ON(ctrl->is_dyn_array))
++ return -EINVAL;
+ memcpy(ctrl->p_new.p, p, ctrl->elems * ctrl->elem_size);
+ return set_ctrl(NULL, ctrl, 0);
+ }
+--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
+@@ -809,11 +809,12 @@ EXPORT_SYMBOL(v4l2_ctrl_notify);
+
+ /* 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)
++ union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to,
++ unsigned int elems)
+ {
+ if (ctrl == NULL)
+ return;
+- memcpy(to.p, from.p_const, ctrl->elems * ctrl->elem_size);
++ memcpy(to.p, from.p_const, elems * ctrl->elem_size);
+ }
+
+ /* Copy the new value to the current value. */
+@@ -826,8 +827,11 @@ void new_to_cur(struct v4l2_fh *fh, stru
+
+ /* has_changed is set by cluster_changed */
+ changed = ctrl->has_changed;
+- if (changed)
+- ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur);
++ if (changed) {
++ if (ctrl->is_dyn_array)
++ ctrl->elems = ctrl->new_elems;
++ ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur, ctrl->elems);
++ }
+
+ if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) {
+ /* Note: CH_FLAGS is only set for auto clusters. */
+@@ -857,36 +861,122 @@ void cur_to_new(struct v4l2_ctrl *ctrl)
+ {
+ if (ctrl == NULL)
+ return;
+- ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new);
++ if (ctrl->is_dyn_array)
++ ctrl->new_elems = ctrl->elems;
++ ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems);
++}
++
++static bool req_alloc_dyn_array(struct v4l2_ctrl_ref *ref, u32 elems)
++{
++ void *tmp;
++
++ if (elems < ref->p_req_dyn_alloc_elems)
++ return true;
++
++ tmp = kvmalloc(elems * ref->ctrl->elem_size, GFP_KERNEL);
++
++ if (!tmp) {
++ ref->p_req_dyn_enomem = true;
++ return false;
++ }
++ ref->p_req_dyn_enomem = false;
++ kvfree(ref->p_req.p);
++ ref->p_req.p = tmp;
++ ref->p_req_dyn_alloc_elems = elems;
++ return true;
+ }
+
+ /* Copy the new value to the request value */
+ void new_to_req(struct v4l2_ctrl_ref *ref)
+ {
++ struct v4l2_ctrl *ctrl;
++
+ if (!ref)
+ return;
+- ptr_to_ptr(ref->ctrl, ref->ctrl->p_new, ref->p_req);
+- ref->valid_p_req = true;
++
++ ctrl = ref->ctrl;
++ if (ctrl->is_dyn_array && !req_alloc_dyn_array(ref, ctrl->new_elems))
++ return;
++
++ ref->p_req_elems = ctrl->new_elems;
++ ptr_to_ptr(ctrl, ctrl->p_new, ref->p_req, ref->p_req_elems);
++ ref->p_req_valid = true;
+ }
+
+ /* Copy the current value to the request value */
+ void cur_to_req(struct v4l2_ctrl_ref *ref)
+ {
++ struct v4l2_ctrl *ctrl;
++
+ if (!ref)
+ return;
+- ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->p_req);
+- ref->valid_p_req = true;
++
++ ctrl = ref->ctrl;
++ if (ctrl->is_dyn_array && !req_alloc_dyn_array(ref, ctrl->elems))
++ return;
++
++ ref->p_req_elems = ctrl->elems;
++ ptr_to_ptr(ctrl, ctrl->p_cur, ref->p_req, ctrl->elems);
++ ref->p_req_valid = true;
+ }
+
+ /* Copy the request value to the new value */
+-void req_to_new(struct v4l2_ctrl_ref *ref)
++int req_to_new(struct v4l2_ctrl_ref *ref)
+ {
++ struct v4l2_ctrl *ctrl;
++
+ if (!ref)
+- return;
+- if (ref->valid_p_req)
+- ptr_to_ptr(ref->ctrl, ref->p_req, ref->ctrl->p_new);
+- else
+- ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->ctrl->p_new);
++ return 0;
++
++ ctrl = ref->ctrl;
++
++ /*
++ * This control was never set in the request, so just use the current
++ * value.
++ */
++ if (!ref->p_req_valid) {
++ if (ctrl->is_dyn_array)
++ ctrl->new_elems = ctrl->elems;
++ ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems);
++ return 0;
++ }
++
++ /* Not a dynamic array, so just copy the request value */
++ if (!ctrl->is_dyn_array) {
++ ptr_to_ptr(ctrl, ref->p_req, ctrl->p_new, ctrl->new_elems);
++ return 0;
++ }
++
++ /* Sanity check, should never happen */
++ if (WARN_ON(!ref->p_req_dyn_alloc_elems))
++ return -ENOMEM;
++
++ /*
++ * Check if the number of elements in the request is more than the
++ * elements in ctrl->p_dyn. If so, attempt to realloc ctrl->p_dyn.
++ * Note that p_dyn is allocated with twice the number of elements
++ * in the dynamic array since it has to store both the current and
++ * new value of such a control.
++ */
++ if (ref->p_req_elems > ctrl->p_dyn_alloc_elems) {
++ unsigned int sz = ref->p_req_elems * ctrl->elem_size;
++ void *old = ctrl->p_dyn;
++ void *tmp = kvzalloc(2 * sz, GFP_KERNEL);
++
++ if (!tmp)
++ return -ENOMEM;
++ memcpy(tmp, ctrl->p_new.p, ctrl->elems * ctrl->elem_size);
++ memcpy(tmp + sz, ctrl->p_cur.p, ctrl->elems * ctrl->elem_size);
++ ctrl->p_new.p = tmp;
++ ctrl->p_cur.p = tmp + sz;
++ ctrl->p_dyn = tmp;
++ ctrl->p_dyn_alloc_elems = ref->p_req_elems;
++ kvfree(old);
++ }
++
++ ctrl->new_elems = ref->p_req_elems;
++ ptr_to_ptr(ctrl, ref->p_req, ctrl->p_new, ctrl->new_elems);
++ return 0;
+ }
+
+ /* Control range checking */
+@@ -928,17 +1018,6 @@ int check_range(enum v4l2_ctrl_type type
+ }
+ }
+
+-/* Validate a new control */
+-int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new)
+-{
+- unsigned idx;
+- int err = 0;
+-
+- for (idx = 0; !err && idx < ctrl->elems; idx++)
+- err = ctrl->type_ops->validate(ctrl, idx, p_new);
+- return err;
+-}
+-
+ /* Set the handler's error code if it wasn't set earlier already */
+ static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
+ {
+@@ -983,6 +1062,8 @@ void v4l2_ctrl_handler_free(struct v4l2_
+ /* Free all nodes */
+ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
+ list_del(&ref->node);
++ if (ref->p_req_dyn_alloc_elems)
++ kvfree(ref->p_req.p);
+ kfree(ref);
+ }
+ /* Free all controls owned by the handler */
+@@ -990,6 +1071,7 @@ void v4l2_ctrl_handler_free(struct v4l2_
+ list_del(&ctrl->node);
+ list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
+ list_del(&sev->node);
++ kvfree(ctrl->p_dyn);
+ kvfree(ctrl);
+ }
+ kvfree(hdl->buckets);
+@@ -1105,7 +1187,7 @@ int handler_new_ref(struct v4l2_ctrl_han
+ if (hdl->error)
+ return hdl->error;
+
+- if (allocate_req)
++ if (allocate_req && !ctrl->is_dyn_array)
+ size_extra_req = ctrl->elems * ctrl->elem_size;
+ new_ref = kzalloc(sizeof(*new_ref) + size_extra_req, GFP_KERNEL);
+ if (!new_ref)
+@@ -1273,7 +1355,6 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+ elem_size = sizeof(s32);
+ break;
+ }
+- tot_ctrl_size = elem_size * elems;
+
+ /* Sanity checks */
+ if (id == 0 || name == NULL || !elem_size ||
+@@ -1294,17 +1375,33 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
++ if (flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) {
++ /*
++ * For now only support this for one-dimensional arrays only.
++ *
++ * This can be relaxed in the future, but this will
++ * require more effort.
++ */
++ if (nr_of_dims != 1) {
++ handler_set_err(hdl, -EINVAL);
++ return NULL;
++ }
++ /* Start with just 1 element */
++ elems = 1;
++ }
+
++ tot_ctrl_size = elem_size * elems;
+ sz_extra = 0;
+ if (type == V4L2_CTRL_TYPE_BUTTON)
+ flags |= V4L2_CTRL_FLAG_WRITE_ONLY |
+ V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+ else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ flags |= V4L2_CTRL_FLAG_READ_ONLY;
+- else if (type == V4L2_CTRL_TYPE_INTEGER64 ||
+- type == V4L2_CTRL_TYPE_STRING ||
+- type >= V4L2_CTRL_COMPOUND_TYPES ||
+- is_array)
++ else if (!(flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) &&
++ (type == V4L2_CTRL_TYPE_INTEGER64 ||
++ type == V4L2_CTRL_TYPE_STRING ||
++ type >= V4L2_CTRL_COMPOUND_TYPES ||
++ is_array))
+ sz_extra += 2 * tot_ctrl_size;
+
+ if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
+@@ -1333,7 +1430,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+ 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->is_dyn_array = !!(flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY);
+ ctrl->elems = elems;
++ ctrl->new_elems = elems;
+ ctrl->nr_of_dims = nr_of_dims;
+ if (nr_of_dims)
+ memcpy(ctrl->dims, dims, nr_of_dims * sizeof(dims[0]));
+@@ -1346,6 +1445,16 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+ ctrl->cur.val = ctrl->val = def;
+ data = &ctrl[1];
+
++ if (ctrl->is_dyn_array) {
++ ctrl->p_dyn_alloc_elems = elems;
++ ctrl->p_dyn = kvzalloc(2 * elems * elem_size, GFP_KERNEL);
++ if (!ctrl->p_dyn) {
++ kvfree(ctrl);
++ return NULL;
++ }
++ data = ctrl->p_dyn;
++ }
++
+ if (!ctrl->is_int) {
+ ctrl->p_new.p = data;
+ ctrl->p_cur.p = data + tot_ctrl_size;
+@@ -1355,7 +1464,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+ }
+
+ if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const) {
+- ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size;
++ if (ctrl->is_dyn_array)
++ ctrl->p_def.p = &ctrl[1];
++ else
++ ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size;
+ memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
+ }
+
+@@ -1365,6 +1477,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+ }
+
+ if (handler_new_ref(hdl, ctrl, NULL, false, false)) {
++ kvfree(ctrl->p_dyn);
+ kvfree(ctrl);
+ return NULL;
+ }
+@@ -1702,6 +1815,9 @@ static int cluster_changed(struct v4l2_c
+ continue;
+ }
+
++ if (ctrl->elems != ctrl->new_elems)
++ ctrl_changed = true;
++
+ for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++)
+ ctrl_changed = !ctrl->type_ops->equal(ctrl, idx,
+ ctrl->p_cur, ctrl->p_new);
+--- a/drivers/media/v4l2-core/v4l2-ctrls-priv.h
++++ b/drivers/media/v4l2-core/v4l2-ctrls-priv.h
+@@ -57,10 +57,9 @@ void cur_to_new(struct v4l2_ctrl *ctrl);
+ void cur_to_req(struct v4l2_ctrl_ref *ref);
+ void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags);
+ void new_to_req(struct v4l2_ctrl_ref *ref);
+-void req_to_new(struct v4l2_ctrl_ref *ref);
++int req_to_new(struct v4l2_ctrl_ref *ref);
+ void send_initial_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl);
+ void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes);
+-int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new);
+ int handler_new_ref(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl,
+ struct v4l2_ctrl_ref **ctrl_ref,
+--- a/drivers/media/v4l2-core/v4l2-ctrls-request.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls-request.c
+@@ -143,7 +143,7 @@ v4l2_ctrl_request_hdl_ctrl_find(struct v
+ {
+ struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
+
+- return (ref && ref->valid_p_req) ? ref->ctrl : NULL;
++ return (ref && ref->p_req_valid) ? ref->ctrl : NULL;
+ }
+ EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find);
+
+@@ -373,7 +373,7 @@ void v4l2_ctrl_request_complete(struct m
+ v4l2_ctrl_unlock(master);
+ continue;
+ }
+- if (ref->valid_p_req)
++ if (ref->p_req_valid)
+ continue;
+
+ /* Copy the current control value into the request */
+@@ -442,7 +442,7 @@ int v4l2_ctrl_request_setup(struct media
+ struct v4l2_ctrl_ref *r =
+ find_ref(hdl, master->cluster[i]->id);
+
+- if (r->valid_p_req) {
++ if (r->p_req_valid) {
+ have_new_data = true;
+ break;
+ }
+@@ -458,7 +458,11 @@ int v4l2_ctrl_request_setup(struct media
+ struct v4l2_ctrl_ref *r =
+ find_ref(hdl, master->cluster[i]->id);
+
+- req_to_new(r);
++ ret = req_to_new(r);
++ if (ret) {
++ v4l2_ctrl_unlock(master);
++ goto error;
++ }
+ master->cluster[i]->is_new = 1;
+ r->req_done = true;
+ }
+@@ -490,6 +494,7 @@ int v4l2_ctrl_request_setup(struct media
+ break;
+ }
+
++error:
+ media_request_object_put(obj);
+ return ret;
+ }
+--- a/include/media/v4l2-ctrls.h
++++ b/include/media/v4l2-ctrls.h
+@@ -181,6 +181,8 @@ typedef void (*v4l2_ctrl_notify_fnc)(str
+ * and/or has type %V4L2_CTRL_TYPE_STRING. In other words, &struct
+ * v4l2_ext_control uses field p to point to the data.
+ * @is_array: If set, then this control contains an N-dimensional array.
++ * @is_dyn_array: If set, then this control contains a dynamically sized 1-dimensional array.
++ * If this is set, then @is_array is also set.
+ * @has_volatiles: If set, then one or more members of the cluster are volatile.
+ * Drivers should never touch this flag.
+ * @call_notify: If set, then call the handler's notify function whenever the
+@@ -201,6 +203,9 @@ typedef void (*v4l2_ctrl_notify_fnc)(str
+ * @step: The control's step value for non-menu controls.
+ * @elems: The number of elements in the N-dimensional array.
+ * @elem_size: The size in bytes of the control.
++ * @new_elems: The number of elements in p_new. This is the same as @elems,
++ * except for dynamic arrays. In that case it is in the range of
++ * 1 to @p_dyn_alloc_elems.
+ * @dims: The size of each dimension.
+ * @nr_of_dims:The number of dimensions in @dims.
+ * @menu_skip_mask: The control's skip mask for menu controls. This makes it
+@@ -219,15 +224,21 @@ typedef void (*v4l2_ctrl_notify_fnc)(str
+ * :math:`ceil(\frac{maximum - minimum}{step}) + 1`.
+ * Used only if the @type is %V4L2_CTRL_TYPE_INTEGER_MENU.
+ * @flags: The control's flags.
+- * @cur: Structure to store the current value.
+- * @cur.val: The control's current value, if the @type is represented via
+- * a u32 integer (see &enum v4l2_ctrl_type).
+- * @val: The control's new s32 value.
+ * @priv: The control's private pointer. For use by the driver. It is
+ * untouched by the control framework. Note that this pointer is
+ * not freed when the control is deleted. Should this be needed
+ * then a new internal bitfield can be added to tell the framework
+ * to free this pointer.
++ * @p_dyn: Pointer to the dynamically allocated array. Only valid if
++ * @is_dyn_array is true.
++ * @p_dyn_alloc_elems: The number of elements in the dynamically allocated
++ * array for both the cur and new values. So @p_dyn is actually
++ * sized for 2 * @p_dyn_alloc_elems * @elem_size. Only valid if
++ * @is_dyn_array is true.
++ * @cur: Structure to store the current value.
++ * @cur.val: The control's current value, if the @type is represented via
++ * a u32 integer (see &enum v4l2_ctrl_type).
++ * @val: The control's new s32 value.
+ * @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).
+@@ -256,6 +267,7 @@ struct v4l2_ctrl {
+ unsigned int is_string:1;
+ unsigned int is_ptr:1;
+ unsigned int is_array:1;
++ unsigned int is_dyn_array:1;
+ unsigned int has_volatiles:1;
+ unsigned int call_notify:1;
+ unsigned int manual_mode_value:8;
+@@ -268,6 +280,7 @@ struct v4l2_ctrl {
+ s64 minimum, maximum, default_value;
+ u32 elems;
+ u32 elem_size;
++ u32 new_elems;
+ u32 dims[V4L2_CTRL_MAX_DIMS];
+ u32 nr_of_dims;
+ union {
+@@ -280,6 +293,8 @@ struct v4l2_ctrl {
+ };
+ unsigned long flags;
+ void *priv;
++ void *p_dyn;
++ u32 p_dyn_alloc_elems;
+ s32 val;
+ struct {
+ s32 val;
+@@ -305,12 +320,22 @@ struct v4l2_ctrl {
+ * the control has been applied. This prevents applying controls
+ * from a cluster with multiple controls twice (when the first
+ * control of a cluster is applied, they all are).
+- * @valid_p_req: If set, then p_req contains the control value for the request.
++ * @p_req_valid: If set, then p_req contains the control value for the request.
++ * @p_req_dyn_enomem: If set, then p_req is invalid since allocating space for
++ * a dynamic array failed. Attempting to read this value shall
++ * result in ENOMEM. Only valid if ctrl->is_dyn_array is true.
++ * @p_req_dyn_alloc_elems: The number of elements allocated for the dynamic
++ * array. Only valid if @p_req_valid and ctrl->is_dyn_array are
++ * true.
++ * @p_req_elems: The number of elements in @p_req. This is the same as
++ * ctrl->elems, except for dynamic arrays. In that case it is in
++ * the range of 1 to @p_req_dyn_alloc_elems. Only valid if
++ * @p_req_valid is true.
+ * @p_req: If the control handler containing this control reference
+ * is bound to a media request, then this points to the
+ * value of the control that must be applied when the request
+ * is executed, or to the value of the control at the time
+- * that the request was completed. If @valid_p_req is false,
++ * that the request was completed. If @p_req_valid is false,
+ * then this control was never set for this request and the
+ * control will not be updated when this request is applied.
+ *
+@@ -325,7 +350,10 @@ struct v4l2_ctrl_ref {
+ struct v4l2_ctrl_helper *helper;
+ bool from_other_dev;
+ bool req_done;
+- bool valid_p_req;
++ bool p_req_valid;
++ bool p_req_dyn_enomem;
++ u32 p_req_dyn_alloc_elems;
++ u32 p_req_elems;
+ union v4l2_ctrl_ptr p_req;
+ };
+