diff options
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-async.c')
-rw-r--r-- | drivers/media/v4l2-core/v4l2-async.c | 180 |
1 files changed, 101 insertions, 79 deletions
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index e3ab003a6c85..e638aa8aecb7 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -5,6 +5,7 @@ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de> */ +#include <linux/debugfs.h> #include <linux/device.h> #include <linux/err.h> #include <linux/i2c.h> @@ -14,6 +15,7 @@ #include <linux/mutex.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/seq_file.h> #include <linux/slab.h> #include <linux/types.h> @@ -64,12 +66,6 @@ static bool match_i2c(struct v4l2_async_notifier *notifier, #endif } -static bool match_devname(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) -{ - return !strcmp(asd->match.device_name, dev_name(sd->dev)); -} - static bool match_fwnode(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { @@ -88,6 +84,14 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, return true; /* + * Check the same situation for any possible secondary assigned to the + * subdev's fwnode + */ + if (!IS_ERR_OR_NULL(sd->fwnode->secondary) && + sd->fwnode->secondary == asd->match.fwnode) + return true; + + /* * Otherwise, check if the sd fwnode and the asd fwnode refer to an * endpoint or a device. If they're of the same type, there's no match. * Technically speaking this checks if the nodes refer to a connected @@ -139,16 +143,6 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, return true; } -static bool match_custom(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) -{ - if (!asd->match.custom.match) - /* Match always */ - return true; - - return asd->match.custom.match(sd->dev, asd); -} - static LIST_HEAD(subdev_list); static LIST_HEAD(notifier_list); static DEFINE_MUTEX(list_lock); @@ -164,12 +158,6 @@ v4l2_async_find_match(struct v4l2_async_notifier *notifier, list_for_each_entry(asd, ¬ifier->waiting, list) { /* bus_type has been verified valid before */ switch (asd->match_type) { - case V4L2_ASYNC_MATCH_CUSTOM: - match = match_custom; - break; - case V4L2_ASYNC_MATCH_DEVNAME: - match = match_devname; - break; case V4L2_ASYNC_MATCH_I2C: match = match_i2c; break; @@ -198,9 +186,6 @@ static bool asd_equal(struct v4l2_async_subdev *asd_x, return false; switch (asd_x->match_type) { - case V4L2_ASYNC_MATCH_DEVNAME: - return strcmp(asd_x->match.device_name, - asd_y->match.device_name) == 0; case V4L2_ASYNC_MATCH_I2C: return asd_x->match.i2c.adapter_id == asd_y->match.i2c.adapter_id && @@ -467,8 +452,6 @@ static int v4l2_async_notifier_asd_valid(struct v4l2_async_notifier *notifier, return -EINVAL; switch (asd->match_type) { - case V4L2_ASYNC_MATCH_CUSTOM: - case V4L2_ASYNC_MATCH_DEVNAME: case V4L2_ASYNC_MATCH_I2C: case V4L2_ASYNC_MATCH_FWNODE: if (v4l2_async_notifier_has_async_subdev(notifier, asd, @@ -628,7 +611,7 @@ void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) } EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup); -int v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, +int __v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { int ret; @@ -645,12 +628,12 @@ unlock: mutex_unlock(&list_lock); return ret; } -EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_subdev); +EXPORT_SYMBOL_GPL(__v4l2_async_notifier_add_subdev); struct v4l2_async_subdev * -v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, - struct fwnode_handle *fwnode, - unsigned int asd_struct_size) +__v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, + struct fwnode_handle *fwnode, + unsigned int asd_struct_size) { struct v4l2_async_subdev *asd; int ret; @@ -662,7 +645,7 @@ v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, asd->match_type = V4L2_ASYNC_MATCH_FWNODE; asd->match.fwnode = fwnode_handle_get(fwnode); - ret = v4l2_async_notifier_add_subdev(notifier, asd); + ret = __v4l2_async_notifier_add_subdev(notifier, asd); if (ret) { fwnode_handle_put(fwnode); kfree(asd); @@ -671,35 +654,35 @@ v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, return asd; } -EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_fwnode_subdev); +EXPORT_SYMBOL_GPL(__v4l2_async_notifier_add_fwnode_subdev); -int -v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, - struct fwnode_handle *endpoint, - struct v4l2_async_subdev *asd) +struct v4l2_async_subdev * +__v4l2_async_notifier_add_fwnode_remote_subdev(struct v4l2_async_notifier *notif, + struct fwnode_handle *endpoint, + unsigned int asd_struct_size) { + struct v4l2_async_subdev *asd; struct fwnode_handle *remote; - int ret; remote = fwnode_graph_get_remote_port_parent(endpoint); if (!remote) - return -ENOTCONN; - - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode = remote; + return ERR_PTR(-ENOTCONN); - ret = v4l2_async_notifier_add_subdev(notif, asd); - if (ret) - fwnode_handle_put(remote); - - return ret; + asd = __v4l2_async_notifier_add_fwnode_subdev(notif, remote, + asd_struct_size); + /* + * Calling __v4l2_async_notifier_add_fwnode_subdev grabs a refcount, + * so drop the one we got in fwnode_graph_get_remote_port_parent. + */ + fwnode_handle_put(remote); + return asd; } -EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_fwnode_remote_subdev); +EXPORT_SYMBOL_GPL(__v4l2_async_notifier_add_fwnode_remote_subdev); struct v4l2_async_subdev * -v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, - int adapter_id, unsigned short address, - unsigned int asd_struct_size) +__v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, + int adapter_id, unsigned short address, + unsigned int asd_struct_size) { struct v4l2_async_subdev *asd; int ret; @@ -712,7 +695,7 @@ v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, asd->match.i2c.adapter_id = adapter_id; asd->match.i2c.address = address; - ret = v4l2_async_notifier_add_subdev(notifier, asd); + ret = __v4l2_async_notifier_add_subdev(notifier, asd); if (ret) { kfree(asd); return ERR_PTR(ret); @@ -720,32 +703,7 @@ v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, return asd; } -EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_i2c_subdev); - -struct v4l2_async_subdev * -v4l2_async_notifier_add_devname_subdev(struct v4l2_async_notifier *notifier, - const char *device_name, - unsigned int asd_struct_size) -{ - struct v4l2_async_subdev *asd; - int ret; - - asd = kzalloc(asd_struct_size, GFP_KERNEL); - if (!asd) - return ERR_PTR(-ENOMEM); - - asd->match_type = V4L2_ASYNC_MATCH_DEVNAME; - asd->match.device_name = device_name; - - ret = v4l2_async_notifier_add_subdev(notifier, asd); - if (ret) { - kfree(asd); - return ERR_PTR(ret); - } - - return asd; -} -EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_devname_subdev); +EXPORT_SYMBOL_GPL(__v4l2_async_notifier_add_i2c_subdev); int v4l2_async_register_subdev(struct v4l2_subdev *sd) { @@ -817,6 +775,9 @@ EXPORT_SYMBOL(v4l2_async_register_subdev); void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) { + if (!sd->async_list.next) + return; + mutex_lock(&list_lock); __v4l2_async_notifier_unregister(sd->subdev_notifier); @@ -837,3 +798,64 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) mutex_unlock(&list_lock); } EXPORT_SYMBOL(v4l2_async_unregister_subdev); + +static void print_waiting_subdev(struct seq_file *s, + struct v4l2_async_subdev *asd) +{ + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_I2C: + seq_printf(s, " [i2c] dev=%d-%04x\n", asd->match.i2c.adapter_id, + asd->match.i2c.address); + break; + case V4L2_ASYNC_MATCH_FWNODE: { + struct fwnode_handle *devnode, *fwnode = asd->match.fwnode; + + devnode = fwnode_graph_is_endpoint(fwnode) ? + fwnode_graph_get_port_parent(fwnode) : + fwnode_handle_get(fwnode); + + seq_printf(s, " [fwnode] dev=%s, node=%pfw\n", + devnode->dev ? dev_name(devnode->dev) : "nil", + fwnode); + + fwnode_handle_put(devnode); + break; + } + } +} + +static const char * +v4l2_async_notifier_name(struct v4l2_async_notifier *notifier) +{ + if (notifier->v4l2_dev) + return notifier->v4l2_dev->name; + else if (notifier->sd) + return notifier->sd->name; + else + return "nil"; +} + +static int pending_subdevs_show(struct seq_file *s, void *data) +{ + struct v4l2_async_notifier *notif; + struct v4l2_async_subdev *asd; + + mutex_lock(&list_lock); + + list_for_each_entry(notif, ¬ifier_list, list) { + seq_printf(s, "%s:\n", v4l2_async_notifier_name(notif)); + list_for_each_entry(asd, ¬if->waiting, list) + print_waiting_subdev(s, asd); + } + + mutex_unlock(&list_lock); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(pending_subdevs); + +void v4l2_async_debug_init(struct dentry *debugfs_dir) +{ + debugfs_create_file("pending_async_subdevices", 0444, debugfs_dir, NULL, + &pending_subdevs_fops); +} |