mirror of
https://github.com/torvalds/linux.git
synced 2026-06-07 22:14:04 +02:00
CHROMIUM: Add config store support
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> BUG=chrome-os-partner:33728 TEST=build Signed-off-by: Pawel Osciak <posciak@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/232584 Trybot-Ready: Tomasz Figa <tfiga@chromium.org> Tested-by: Tomasz Figa <tfiga@chromium.org> Reviewed-by: Wu-cheng Li <wuchengli@chromium.org> Commit-Queue: Tomasz Figa <tfiga@chromium.org> Conflicts: drivers/media/v4l2-core/v4l2-ctrls.c include/media/v4l2-ctrls.h [rebase44(groeck): Resolved conflicts] Signed-off-by: Guenter Roeck <groeck@chromium.org> Conflicts: drivers/media/v4l2-core/v4l2-ctrls.c Change-Id: I0b2e66f3331cab91d209868f5d5f67f795a0e72c Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
This commit is contained in:
parent
07dc8fde53
commit
3b670167f2
|
|
@ -1484,6 +1484,15 @@ static int cur_to_user(struct v4l2_ext_control *c,
|
|||
return ptr_to_user(c, ctrl, ctrl->p_cur);
|
||||
}
|
||||
|
||||
/* Helper function: copy the store's control value back to the caller */
|
||||
static int store_to_user(struct v4l2_ext_control *c,
|
||||
struct v4l2_ctrl *ctrl, unsigned store)
|
||||
{
|
||||
if (store == 0)
|
||||
return ptr_to_user(c, ctrl, ctrl->p_new);
|
||||
return ptr_to_user(c, ctrl, ctrl->p_stores[store - 1]);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
|
|
@ -1591,6 +1600,14 @@ 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)
|
||||
{
|
||||
/* has_changed is set by cluster_changed */
|
||||
if (ctrl && ctrl->has_changed)
|
||||
ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_stores[ctrl->store - 1]);
|
||||
}
|
||||
|
||||
/* Copy the current value to the new value */
|
||||
static void cur_to_new(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
|
|
@ -1609,6 +1626,7 @@ static int cluster_changed(struct v4l2_ctrl *master)
|
|||
|
||||
for (i = 0; i < master->ncontrols; i++) {
|
||||
struct v4l2_ctrl *ctrl = master->cluster[i];
|
||||
union v4l2_ctrl_ptr ptr;
|
||||
bool ctrl_changed = false;
|
||||
|
||||
if (ctrl == NULL)
|
||||
|
|
@ -1626,9 +1644,14 @@ static int cluster_changed(struct v4l2_ctrl *master)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ctrl->store)
|
||||
ptr = ctrl->p_stores[ctrl->store - 1];
|
||||
else
|
||||
ptr = ctrl->p_cur;
|
||||
|
||||
for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++)
|
||||
ctrl_changed = !ctrl->type_ops->equal(ctrl, idx,
|
||||
ctrl->p_cur, ctrl->p_new);
|
||||
ptr, ctrl->p_new);
|
||||
ctrl->has_changed = ctrl_changed;
|
||||
changed |= ctrl->has_changed;
|
||||
}
|
||||
|
|
@ -1737,6 +1760,13 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
|
|||
list_del(&ctrl->node);
|
||||
list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
|
||||
list_del(&sev->node);
|
||||
if (ctrl->p_stores) {
|
||||
unsigned s;
|
||||
|
||||
for (s = 0; s < ctrl->nr_of_stores; s++)
|
||||
kfree(ctrl->p_stores[s].p);
|
||||
}
|
||||
kfree(ctrl->p_stores);
|
||||
kfree(ctrl);
|
||||
}
|
||||
kfree(hdl->buckets);
|
||||
|
|
@ -1967,7 +1997,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
|
|||
handler_set_err(hdl, -ERANGE);
|
||||
return NULL;
|
||||
}
|
||||
if (is_array &&
|
||||
if ((is_array || (flags & V4L2_CTRL_FLAG_CAN_STORE)) &&
|
||||
(type == V4L2_CTRL_TYPE_BUTTON ||
|
||||
type == V4L2_CTRL_TYPE_CTRL_CLASS)) {
|
||||
handler_set_err(hdl, -EINVAL);
|
||||
|
|
@ -2082,8 +2112,10 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
|
|||
is_menu ? cfg->menu_skip_mask : step, def,
|
||||
cfg->dims, cfg->elem_size,
|
||||
flags, qmenu, qmenu_int, priv);
|
||||
if (ctrl)
|
||||
if (ctrl) {
|
||||
ctrl->is_private = cfg->is_private;
|
||||
v4l2_ctrl_set_max_stores(ctrl, cfg->max_stores);
|
||||
}
|
||||
return ctrl;
|
||||
}
|
||||
EXPORT_SYMBOL(v4l2_ctrl_new_custom);
|
||||
|
|
@ -2450,6 +2482,7 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
|
|||
cur_to_new(master->cluster[i]);
|
||||
master->cluster[i]->is_new = 1;
|
||||
master->cluster[i]->done = true;
|
||||
master->cluster[i]->store = 0;
|
||||
}
|
||||
}
|
||||
ret = call_op(master, s_ctrl);
|
||||
|
|
@ -2537,6 +2570,8 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr
|
|||
qc->id = ctrl->id;
|
||||
strlcpy(qc->name, ctrl->name, sizeof(qc->name));
|
||||
qc->flags = ctrl->flags;
|
||||
if (ctrl->max_stores)
|
||||
qc->flags |= V4L2_CTRL_FLAG_CAN_STORE;
|
||||
qc->type = ctrl->type;
|
||||
if (ctrl->is_ptr)
|
||||
qc->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
|
||||
|
|
@ -2698,6 +2733,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
|
|||
struct v4l2_ctrl_helper *helpers,
|
||||
bool get)
|
||||
{
|
||||
unsigned store = cs->config_store & 0xffff;
|
||||
struct v4l2_ctrl_helper *h;
|
||||
bool have_clusters = false;
|
||||
u32 i;
|
||||
|
|
@ -2723,6 +2759,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
|
|||
ctrl = ref->ctrl;
|
||||
if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
|
||||
return -EINVAL;
|
||||
if (store > ctrl->max_stores)
|
||||
return -EINVAL;
|
||||
|
||||
if (ctrl->cluster[0]->ncontrols > 1)
|
||||
have_clusters = true;
|
||||
|
|
@ -2794,6 +2832,32 @@ static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
|
|||
return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static int extend_store(struct v4l2_ctrl *ctrl, unsigned stores)
|
||||
{
|
||||
unsigned s, idx;
|
||||
union v4l2_ctrl_ptr *p;
|
||||
|
||||
p = kcalloc(stores, sizeof(union v4l2_ctrl_ptr), GFP_KERNEL);
|
||||
if (p == NULL)
|
||||
return -ENOMEM;
|
||||
for (s = ctrl->nr_of_stores; s < stores; s++) {
|
||||
p[s].p = kcalloc(ctrl->elems, ctrl->elem_size, GFP_KERNEL);
|
||||
if (p[s].p == NULL) {
|
||||
while (s > ctrl->nr_of_stores)
|
||||
kfree(p[--s].p);
|
||||
kfree(p);
|
||||
return -ENOMEM;
|
||||
}
|
||||
for (idx = 0; idx < ctrl->elems; idx++)
|
||||
ctrl->type_ops->init(ctrl, idx, p[s]);
|
||||
}
|
||||
if (ctrl->p_stores)
|
||||
memcpy(p, ctrl->p_stores, ctrl->nr_of_stores * sizeof(union v4l2_ctrl_ptr));
|
||||
kfree(ctrl->p_stores);
|
||||
ctrl->p_stores = p;
|
||||
ctrl->nr_of_stores = stores;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Get extended controls. Allocates the helpers array if needed. */
|
||||
|
|
@ -2801,11 +2865,15 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
|
|||
{
|
||||
struct v4l2_ctrl_helper helper[4];
|
||||
struct v4l2_ctrl_helper *helpers = helper;
|
||||
unsigned store = 0;
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
cs->error_idx = cs->count;
|
||||
cs->which = V4L2_CTRL_ID2WHICH(cs->which);
|
||||
if (V4L2_CTRL_ID2WHICH(cs->which))
|
||||
cs->which = V4L2_CTRL_ID2WHICH(cs->which);
|
||||
else
|
||||
store = cs->config_store;
|
||||
|
||||
if (hdl == NULL)
|
||||
return -EINVAL;
|
||||
|
|
@ -2841,13 +2909,19 @@ 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 ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
|
||||
(master->has_volatiles && !is_cur_manual(master))) {
|
||||
if (store == 0 &&
|
||||
((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);
|
||||
ctrl_to_user = new_to_user;
|
||||
}
|
||||
for (j = 0; j < master->ncontrols; j++)
|
||||
if (!ret && master->cluster[j] &&
|
||||
store > master->cluster[j]->nr_of_stores)
|
||||
ret = extend_store(master->cluster[j], store);
|
||||
|
||||
/* If OK, then copy the current (for non-volatile controls)
|
||||
or the new (for volatile controls) control values to the
|
||||
caller */
|
||||
|
|
@ -2855,7 +2929,11 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
|
|||
u32 idx = i;
|
||||
|
||||
do {
|
||||
ret = ctrl_to_user(cs->controls + idx,
|
||||
if (store)
|
||||
ret = store_to_user(cs->controls + idx,
|
||||
helpers[idx].ctrl, store);
|
||||
else
|
||||
ret = ctrl_to_user(cs->controls + idx,
|
||||
helpers[idx].ctrl);
|
||||
idx = helpers[idx].next;
|
||||
} while (!ret && idx);
|
||||
|
|
@ -2950,12 +3028,11 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl)
|
|||
}
|
||||
EXPORT_SYMBOL(v4l2_ctrl_g_ctrl_int64);
|
||||
|
||||
|
||||
/* Core function that calls try/s_ctrl and ensures that the new value is
|
||||
copied to the current value on a set.
|
||||
Must be called with ctrl->handler->lock held. */
|
||||
static int try_or_set_cluster(struct v4l2_fh *fh, struct v4l2_ctrl *master,
|
||||
bool set, u32 ch_flags)
|
||||
u16 store, bool set, u32 ch_flags)
|
||||
{
|
||||
bool update_flag;
|
||||
int ret;
|
||||
|
|
@ -2971,6 +3048,14 @@ static int try_or_set_cluster(struct v4l2_fh *fh, struct v4l2_ctrl *master,
|
|||
if (ctrl == NULL)
|
||||
continue;
|
||||
|
||||
if (store > ctrl->max_stores)
|
||||
return -EINVAL;
|
||||
if (store > ctrl->nr_of_stores) {
|
||||
ret = extend_store(ctrl, store);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ctrl->store = store;
|
||||
if (!ctrl->is_new) {
|
||||
cur_to_new(ctrl);
|
||||
continue;
|
||||
|
|
@ -2992,9 +3077,13 @@ static int try_or_set_cluster(struct v4l2_fh *fh, struct v4l2_ctrl *master,
|
|||
|
||||
/* 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++)
|
||||
new_to_cur(fh, master->cluster[i], ch_flags |
|
||||
((update_flag && i > 0) ? V4L2_EVENT_CTRL_CH_FLAGS : 0));
|
||||
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));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -3045,8 +3134,12 @@ static void update_from_auto_cluster(struct v4l2_ctrl *master)
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < master->ncontrols; i++)
|
||||
for (i = 0; i < master->ncontrols; i++) {
|
||||
if (master->cluster[i] == NULL)
|
||||
continue;
|
||||
cur_to_new(master->cluster[i]);
|
||||
master->cluster[i]->store = 0;
|
||||
}
|
||||
if (!call_op(master, g_volatile_ctrl))
|
||||
for (i = 1; i < master->ncontrols; i++)
|
||||
if (master->cluster[i])
|
||||
|
|
@ -3060,11 +3153,15 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
|
|||
{
|
||||
struct v4l2_ctrl_helper helper[4];
|
||||
struct v4l2_ctrl_helper *helpers = helper;
|
||||
unsigned store = 0;
|
||||
unsigned i, j;
|
||||
int ret;
|
||||
|
||||
cs->error_idx = cs->count;
|
||||
cs->which = V4L2_CTRL_ID2WHICH(cs->which);
|
||||
if (V4L2_CTRL_ID2WHICH(cs->which))
|
||||
cs->which = V4L2_CTRL_ID2WHICH(cs->which);
|
||||
else
|
||||
store = cs->config_store;
|
||||
|
||||
if (hdl == NULL)
|
||||
return -EINVAL;
|
||||
|
|
@ -3105,7 +3202,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
|
|||
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 &&
|
||||
if (!store && 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;
|
||||
|
|
@ -3136,14 +3233,14 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
|
|||
} while (!ret && idx);
|
||||
|
||||
if (!ret)
|
||||
ret = try_or_set_cluster(fh, master, set, 0);
|
||||
ret = try_or_set_cluster(fh, master, store, set, 0);
|
||||
|
||||
/* Copy the new values back to userspace. */
|
||||
if (!ret) {
|
||||
idx = i;
|
||||
do {
|
||||
ret = new_to_user(cs->controls + idx,
|
||||
helpers[idx].ctrl);
|
||||
ret = store_to_user(cs->controls + idx,
|
||||
helpers[idx].ctrl, store);
|
||||
idx = helpers[idx].next;
|
||||
} while (!ret && idx);
|
||||
}
|
||||
|
|
@ -3188,9 +3285,12 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags)
|
|||
int i;
|
||||
|
||||
/* Reset the 'is_new' flags of the cluster */
|
||||
for (i = 0; i < master->ncontrols; i++)
|
||||
if (master->cluster[i])
|
||||
master->cluster[i]->is_new = 0;
|
||||
for (i = 0; i < master->ncontrols; i++) {
|
||||
if (master->cluster[i] == NULL)
|
||||
continue;
|
||||
master->cluster[i]->is_new = 0;
|
||||
master->cluster[i]->store = 0;
|
||||
}
|
||||
|
||||
ret = validate_new(ctrl, ctrl->p_new);
|
||||
if (ret)
|
||||
|
|
@ -3204,7 +3304,7 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags)
|
|||
update_from_auto_cluster(master);
|
||||
|
||||
ctrl->is_new = 1;
|
||||
return try_or_set_cluster(fh, master, true, ch_flags);
|
||||
return try_or_set_cluster(fh, master, 0, true, ch_flags);
|
||||
}
|
||||
|
||||
/* Helper function for VIDIOC_S_CTRL compatibility */
|
||||
|
|
|
|||
|
|
@ -539,8 +539,8 @@ static void v4l_print_query_ext_ctrl(const void *arg, bool write_only)
|
|||
pr_cont("id=0x%x, type=%d, name=%.*s, min/max=%lld/%lld, "
|
||||
"step=%lld, default=%lld, flags=0x%08x, elem_size=%u, elems=%u, "
|
||||
"nr_of_dims=%u, dims=%u,%u,%u,%u\n",
|
||||
p->id, p->type, (int)sizeof(p->name), p->name,
|
||||
p->minimum, p->maximum,
|
||||
p->id, p->type, (int)sizeof(p->name),
|
||||
p->name, p->minimum, p->maximum,
|
||||
p->step, p->default_value, p->flags,
|
||||
p->elem_size, p->elems, p->nr_of_dims,
|
||||
p->dims[0], p->dims[1], p->dims[2], p->dims[3]);
|
||||
|
|
|
|||
|
|
@ -146,6 +146,9 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
|
|||
* @elem_size: The size in bytes of the control.
|
||||
* @dims: The size of each dimension.
|
||||
* @nr_of_dims:The number of dimensions in @dims.
|
||||
* @max_stores:The maximum number of configuration stores of this control.
|
||||
* @nr_of_stores: The number of allocated configuration stores of this control.
|
||||
* @store: The configuration store that the control op operates on.
|
||||
* @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
|
||||
|
|
@ -202,6 +205,9 @@ struct v4l2_ctrl {
|
|||
u32 elem_size;
|
||||
u32 dims[V4L2_CTRL_MAX_DIMS];
|
||||
u32 nr_of_dims;
|
||||
u16 max_stores;
|
||||
u16 nr_of_stores;
|
||||
u16 store;
|
||||
union {
|
||||
u64 step;
|
||||
u64 menu_skip_mask;
|
||||
|
|
@ -219,6 +225,7 @@ struct v4l2_ctrl {
|
|||
|
||||
union v4l2_ctrl_ptr p_new;
|
||||
union v4l2_ctrl_ptr p_cur;
|
||||
union v4l2_ctrl_ptr *p_stores;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -285,6 +292,7 @@ struct v4l2_ctrl_handler {
|
|||
* @def: The control's default value.
|
||||
* @dims: The size of each dimension.
|
||||
* @elem_size: The size in bytes of the control.
|
||||
* @max_stores: The maximum number of stores allowed.
|
||||
* @flags: The control's flags.
|
||||
* @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,
|
||||
|
|
@ -313,6 +321,7 @@ struct v4l2_ctrl_config {
|
|||
s64 def;
|
||||
u32 dims[V4L2_CTRL_MAX_DIMS];
|
||||
u32 elem_size;
|
||||
u16 max_stores;
|
||||
u32 flags;
|
||||
u64 menu_skip_mask;
|
||||
const char * const *qmenu;
|
||||
|
|
@ -886,6 +895,11 @@ static inline int v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s)
|
|||
return rval;
|
||||
}
|
||||
|
||||
static inline void v4l2_ctrl_set_max_stores(struct v4l2_ctrl *ctrl, u16 max_stores)
|
||||
{
|
||||
ctrl->max_stores = max_stores;
|
||||
}
|
||||
|
||||
/* Internal helper functions that deal with control events. */
|
||||
extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops;
|
||||
void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user