From: Hans Verkuil <hans.verk...@cisco.com>

Sometimes you want to apply a value every time v4l2_ctrl_apply_store
is called, and sometimes you want to apply that value only once.

This adds support for that feature.

Signed-off-by: Hans Verkuil <hans.verk...@cisco.com>
---
 drivers/media/v4l2-core/v4l2-ctrls.c | 75 ++++++++++++++++++++++++++++--------
 drivers/media/v4l2-core/v4l2-ioctl.c | 14 +++----
 include/media/v4l2-ctrls.h           | 12 ++++++
 3 files changed, 79 insertions(+), 22 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c 
b/drivers/media/v4l2-core/v4l2-ctrls.c
index e5dccf2..21560b0 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -1475,6 +1475,7 @@ static int ptr_to_user(struct v4l2_ext_control *c,
 static int cur_to_user(struct v4l2_ext_control *c,
                       struct v4l2_ctrl *ctrl)
 {
+       c->flags = 0;
        return ptr_to_user(c, ctrl, ctrl->p_cur);
 }
 
@@ -1482,8 +1483,13 @@ static int cur_to_user(struct v4l2_ext_control *c,
 static int store_to_user(struct v4l2_ext_control *c,
                       struct v4l2_ctrl *ctrl, unsigned store)
 {
+       c->flags = 0;
        if (store == 0)
                return ptr_to_user(c, ctrl, ctrl->p_new);
+       if (test_bit(store - 1, ctrl->cluster[0]->ignore_store_after_use))
+               c->flags |= V4L2_EXT_CTRL_FL_IGN_STORE_AFTER_USE;
+       if (test_bit(store - 1, ctrl->cluster[0]->ignore_store))
+               c->flags |= V4L2_EXT_CTRL_FL_IGN_STORE;
        return ptr_to_user(c, ctrl, ctrl->p_stores[store - 1]);
 }
 
@@ -1491,6 +1497,7 @@ static int store_to_user(struct v4l2_ext_control *c,
 static int new_to_user(struct v4l2_ext_control *c,
                       struct v4l2_ctrl *ctrl)
 {
+       c->flags = 0;
        return ptr_to_user(c, ctrl, ctrl->p_new);
 }
 
@@ -1546,6 +1553,8 @@ static int user_to_ptr(struct v4l2_ext_control *c,
 static int user_to_new(struct v4l2_ext_control *c,
                       struct v4l2_ctrl *ctrl)
 {
+       ctrl->cluster[0]->new_ignore_store_after_use =
+               c->flags & V4L2_EXT_CTRL_FL_IGN_STORE_AFTER_USE;
        return user_to_ptr(c, ctrl, ctrl->p_new);
 }
 
@@ -1597,8 +1606,11 @@ static void new_to_cur(struct v4l2_fh *fh, struct 
v4l2_ctrl *ctrl, u32 ch_flags)
 /* Helper function: copy the new control value to the store */
 static void new_to_store(struct v4l2_ctrl *ctrl)
 {
+       if (ctrl == NULL)
+               return;
+
        /* has_changed is set by cluster_changed */
-       if (ctrl && ctrl->has_changed)
+       if (ctrl->has_changed)
                ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_stores[ctrl->store - 1]);
 }
 
@@ -2328,6 +2340,12 @@ void v4l2_ctrl_cluster(unsigned ncontrols, struct 
v4l2_ctrl **controls)
 
        for (i = 0; i < ncontrols; i++) {
                if (controls[i]) {
+                       /*
+                        * All controls in a cluster must have the same
+                        * V4L2_CTRL_FLAG_CAN_STORE flag value.
+                        */
+                       WARN_ON((controls[0]->flags & controls[i]->flags) &
+                                       V4L2_CTRL_FLAG_CAN_STORE);
                        controls[i]->cluster = controls;
                        controls[i]->ncontrols = ncontrols;
                        if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE)
@@ -2850,6 +2868,10 @@ static int extend_store(struct v4l2_ctrl *ctrl, unsigned 
stores)
        unsigned s, idx;
        union v4l2_ctrl_ptr *p;
 
+       /* round up to the next multiple of 4 */
+       stores = (stores + 3) & ~3;
+       if (stores > V4L2_CTRLS_MAX_STORES)
+               return -EINVAL;
        p = kcalloc(stores, sizeof(union v4l2_ctrl_ptr), GFP_KERNEL);
        if (p == NULL)
                return -ENOMEM;
@@ -2868,6 +2890,7 @@ static int extend_store(struct v4l2_ctrl *ctrl, unsigned 
stores)
                memcpy(p, ctrl->p_stores, ctrl->nr_of_stores * sizeof(union 
v4l2_ctrl_ptr));
        kfree(ctrl->p_stores);
        ctrl->p_stores = p;
+       bitmap_set(ctrl->ignore_store, ctrl->nr_of_stores, stores - 
ctrl->nr_of_stores);
        ctrl->nr_of_stores = stores;
        return 0;
 }
@@ -3081,21 +3104,33 @@ static int try_or_set_cluster(struct v4l2_fh *fh, 
struct v4l2_ctrl *master,
 
        ret = call_op(master, try_ctrl);
 
-       /* Don't set if there is no change */
-       if (ret || !set || !cluster_changed(master))
-               return ret;
-       ret = call_op(master, s_ctrl);
-       if (ret)
+       if (ret || !set)
                return ret;
 
-       /* If OK, then make the new values permanent. */
-       update_flag = is_cur_manual(master) != is_new_manual(master);
-       for (i = 0; i < master->ncontrols; i++) {
-               if (store)
-                       new_to_store(master->cluster[i]);
+       /* Don't set if there is no change */
+       if (cluster_changed(master)) {
+               ret = call_op(master, s_ctrl);
+               if (ret)
+                       return ret;
+
+               /* If OK, then make the new values permanent. */
+               update_flag = is_cur_manual(master) != is_new_manual(master);
+               for (i = 0; i < master->ncontrols; i++) {
+                       if (store)
+                               new_to_store(master->cluster[i]);
+                       else
+                               new_to_cur(fh, master->cluster[i], ch_flags |
+                                               ((update_flag && i > 0) ?
+                                                V4L2_EVENT_CTRL_CH_FLAGS : 0));
+               }
+       }
+
+       if (store) {
+               if (master->new_ignore_store_after_use)
+                       set_bit(store - 1, master->ignore_store_after_use);
                else
-                       new_to_cur(fh, master->cluster[i], ch_flags |
-                               ((update_flag && i > 0) ? 
V4L2_EVENT_CTRL_CH_FLAGS : 0));
+                       clear_bit(store - 1, master->ignore_store_after_use);
+               clear_bit(store - 1, master->ignore_store);
        }
        return 0;
 }
@@ -3401,8 +3436,18 @@ int v4l2_ctrl_apply_store(struct v4l2_ctrl_handler *hdl, 
unsigned store)
                        continue;
                if (master->handler != hdl)
                        v4l2_ctrl_lock(master);
-               for (i = 0; i < master->ncontrols; i++)
-                       store_to_new(master->cluster[i], store);
+               for (i = 0; i < master->ncontrols; i++) {
+                       struct v4l2_ctrl *ctrl = master->cluster[i];
+
+                       if (!ctrl || (store && test_bit(store - 1, 
master->ignore_store)))
+                               continue;
+                       store_to_new(ctrl, store);
+               }
+
+               if (store && !test_bit(store - 1, master->ignore_store)) {
+                       if (test_bit(store - 1, master->ignore_store_after_use))
+                               set_bit(store - 1, master->ignore_store);
+               }
 
                /* For volatile autoclusters that are currently in auto mode
                   we need to discover if it will be set to manual mode.
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c 
b/drivers/media/v4l2-core/v4l2-ioctl.c
index 628852c..9d3b4f2 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -562,12 +562,14 @@ static void v4l_print_ext_controls(const void *arg, bool 
write_only)
        pr_cont("class=0x%x, count=%d, error_idx=%d",
                        p->ctrl_class, p->count, p->error_idx);
        for (i = 0; i < p->count; i++) {
-               if (p->controls[i].size)
-                       pr_cont(", id/val=0x%x/0x%x",
-                               p->controls[i].id, p->controls[i].value);
+               if (!p->controls[i].size)
+                       pr_cont(", id/flags/val=0x%x/0x%x/0x%x",
+                               p->controls[i].id, p->controls[i].flags,
+                               p->controls[i].value);
                else
-                       pr_cont(", id/size=0x%x/%u",
-                               p->controls[i].id, p->controls[i].size);
+                       pr_cont(", id/flags/size=0x%x/0x%x/%u",
+                               p->controls[i].id, p->controls[i].flags,
+                               p->controls[i].size);
        }
        pr_cont("\n");
 }
@@ -888,8 +890,6 @@ static int check_ext_ctrls(struct v4l2_ext_controls *c, int 
allow_priv)
 
        /* zero the reserved fields */
        c->reserved[0] = c->reserved[1] = 0;
-       for (i = 0; i < c->count; i++)
-               c->controls[i].reserved2[0] = 0;
 
        /* V4L2_CID_PRIVATE_BASE cannot be used as control class
           when using extended controls.
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 713980a..3005d88 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -36,6 +36,9 @@ struct v4l2_subscribed_event;
 struct v4l2_fh;
 struct poll_table_struct;
 
+/* Must be a multiple of 4 */
+#define V4L2_CTRLS_MAX_STORES VIDEO_MAX_FRAME
+
 /** union v4l2_ctrl_ptr - A pointer to a control value.
  * @p_s32:     Pointer to a 32-bit signed value.
  * @p_s64:     Pointer to a 64-bit signed value.
@@ -123,6 +126,8 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl 
*ctrl, void *priv);
   * @call_notify: If set, then call the handler's notify function whenever the
   *            control's value changes.
   * @can_store: If set, then this control supports configuration stores.
+  * @new_ignore_store_after_use: If set, then the new control had the
+  *            V4L2_EXT_CTRL_FL_IGN_STORE_AFTER_USE flag set.
   * @manual_mode_value: If the is_auto flag is set, then this is the value
   *            of the auto control that determines if that control is in
   *            manual mode. So if the value of the auto control equals this
@@ -143,6 +148,10 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl 
*ctrl, void *priv);
   * @nr_of_dims:The number of dimensions in @dims.
   * @nr_of_stores: The number of allocated configuration stores of this 
control.
   * @store:    The configuration store that the control op operates on.
+  * @ignore_store: If the bit for the corresponding store is 1, then don't 
apply that
+  *            store's value.
+  * @ignore_store_after_use: If the bit for the corresponding store is 1, then 
set the
+  *            bit in @ignore_store after the store's value has been applied.
   * @menu_skip_mask: The control's skip mask for menu controls. This makes it
   *            easy to skip menu items that are not valid. If bit X is set,
   *            then menu item X is skipped. Of course, this only works for
@@ -183,6 +192,7 @@ struct v4l2_ctrl {
        unsigned int has_volatiles:1;
        unsigned int call_notify:1;
        unsigned int can_store:1;
+       unsigned int new_ignore_store_after_use:1;
        unsigned int manual_mode_value:8;
 
        const struct v4l2_ctrl_ops *ops;
@@ -197,6 +207,8 @@ struct v4l2_ctrl {
        u32 nr_of_dims;
        u16 nr_of_stores;
        u16 store;
+       DECLARE_BITMAP(ignore_store, V4L2_CTRLS_MAX_STORES);
+       DECLARE_BITMAP(ignore_store_after_use, V4L2_CTRLS_MAX_STORES);
        union {
                u64 step;
                u64 menu_skip_mask;
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to