summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/video/v4l2-ctrls.c74
-rw-r--r--include/media/v4l2-ctrls.h3
2 files changed, 69 insertions, 8 deletions
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 166762182f8..fc8666ae408 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -937,9 +937,14 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
break;
}
if (update_inactive) {
- ctrl->flags &= ~V4L2_CTRL_FLAG_INACTIVE;
- if (!is_cur_manual(ctrl->cluster[0]))
+ /* Note: update_inactive can only be true for auto clusters. */
+ ctrl->flags &=
+ ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE);
+ if (!is_cur_manual(ctrl->cluster[0])) {
ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (ctrl->cluster[0]->has_volatiles)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
}
if (changed || update_inactive) {
/* If a control was changed that was not one of the controls
@@ -1489,6 +1494,7 @@ EXPORT_SYMBOL(v4l2_ctrl_add_handler);
/* Cluster controls */
void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
{
+ bool has_volatiles = false;
int i;
/* The first control is the master control and it must not be NULL */
@@ -1498,8 +1504,11 @@ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
if (controls[i]) {
controls[i]->cluster = controls;
controls[i]->ncontrols = ncontrols;
+ if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE)
+ has_volatiles = true;
}
}
+ controls[0]->has_volatiles = has_volatiles;
}
EXPORT_SYMBOL(v4l2_ctrl_cluster);
@@ -1507,18 +1516,21 @@ void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
u8 manual_val, bool set_volatile)
{
struct v4l2_ctrl *master = controls[0];
- u32 flag;
+ u32 flag = 0;
int i;
v4l2_ctrl_cluster(ncontrols, controls);
WARN_ON(ncontrols <= 1);
WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
+ WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl));
master->is_auto = true;
+ master->has_volatiles = set_volatile;
master->manual_mode_value = manual_val;
master->flags |= V4L2_CTRL_FLAG_UPDATE;
- flag = is_cur_manual(master) ? 0 : V4L2_CTRL_FLAG_INACTIVE;
- if (set_volatile)
- flag |= V4L2_CTRL_FLAG_VOLATILE;
+
+ if (!is_cur_manual(master))
+ flag = V4L2_CTRL_FLAG_INACTIVE |
+ (set_volatile ? V4L2_CTRL_FLAG_VOLATILE : 0);
for (i = 1; i < ncontrols; i++)
if (controls[i])
@@ -1957,7 +1969,8 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
v4l2_ctrl_lock(master);
/* g_volatile_ctrl will update the new control values */
- if (has_op(master, g_volatile_ctrl) && !is_cur_manual(master)) {
+ if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
+ (master->has_volatiles && !is_cur_manual(master))) {
for (j = 0; j < master->ncontrols; j++)
cur_to_new(master->cluster[j]);
ret = call_op(master, g_volatile_ctrl);
@@ -2002,7 +2015,7 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
v4l2_ctrl_lock(master);
/* g_volatile_ctrl will update the current control values */
- if ((ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) && !is_cur_manual(master)) {
+ if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
for (i = 0; i < master->ncontrols; i++)
cur_to_new(master->cluster[i]);
ret = call_op(master, g_volatile_ctrl);
@@ -2118,6 +2131,20 @@ static int validate_ctrls(struct v4l2_ext_controls *cs,
return 0;
}
+/* Obtain the current volatile values of an autocluster and mark them
+ as new. */
+static void update_from_auto_cluster(struct v4l2_ctrl *master)
+{
+ int i;
+
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+ if (!call_op(master, g_volatile_ctrl))
+ for (i = 1; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->is_new = 1;
+}
+
/* Try or try-and-set controls */
static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
struct v4l2_ext_controls *cs,
@@ -2163,6 +2190,31 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
if (master->cluster[j])
master->cluster[j]->is_new = 0;
+ /* For volatile autoclusters that are currently in auto mode
+ we need to discover if it will be set to manual mode.
+ If so, then we have to copy the current volatile values
+ first since those will become the new manual values (which
+ may be overwritten by explicit new values from this set
+ of controls). */
+ if (master->is_auto && master->has_volatiles &&
+ !is_cur_manual(master)) {
+ /* Pick an initial non-manual value */
+ s32 new_auto_val = master->manual_mode_value + 1;
+ u32 tmp_idx = idx;
+
+ do {
+ /* Check if the auto control is part of the
+ list, and remember the new value. */
+ if (helpers[tmp_idx].ctrl == master)
+ new_auto_val = cs->controls[tmp_idx].value;
+ tmp_idx = helpers[tmp_idx].next;
+ } while (tmp_idx);
+ /* If the new value == the manual value, then copy
+ the current volatile values. */
+ if (new_auto_val == master->manual_mode_value)
+ update_from_auto_cluster(master);
+ }
+
/* Copy the new caller-supplied control values.
user_to_new() sets 'is_new' to 1. */
do {
@@ -2233,6 +2285,12 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
if (master->cluster[i])
master->cluster[i]->is_new = 0;
+ /* For autoclusters with volatiles that are switched from auto to
+ manual mode we have to update the current volatile values since
+ those will become the initial manual values after such a switch. */
+ if (master->is_auto && master->has_volatiles && ctrl == master &&
+ !is_cur_manual(master) && *val == master->manual_mode_value)
+ update_from_auto_cluster(master);
ctrl->val = *val;
ctrl->is_new = 1;
ret = try_or_set_cluster(fh, master, true);
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index bd6a4a7370d..eeb3df63714 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -69,6 +69,8 @@ struct v4l2_ctrl_ops {
* members are in 'automatic' mode or 'manual' mode. This is
* used for autogain/gain type clusters. Drivers should never
* set this flag directly.
+ * @has_volatiles: If set, then one or more members of the cluster are volatile.
+ * Drivers should never touch this flag.
* @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
@@ -115,6 +117,7 @@ struct v4l2_ctrl {
unsigned int is_new:1;
unsigned int is_private:1;
unsigned int is_auto:1;
+ unsigned int has_volatiles:1;
unsigned int manual_mode_value:8;
const struct v4l2_ctrl_ops *ops;