| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * V4L2 asynchronous subdevice registration API | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 as | 
					
						
							|  |  |  |  * published by the Free Software Foundation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/device.h>
 | 
					
						
							|  |  |  | #include <linux/err.h>
 | 
					
						
							|  |  |  | #include <linux/i2c.h>
 | 
					
						
							|  |  |  | #include <linux/list.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/mutex.h>
 | 
					
						
							|  |  |  | #include <linux/platform_device.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/types.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <media/v4l2-async.h>
 | 
					
						
							|  |  |  | #include <media/v4l2-device.h>
 | 
					
						
							|  |  |  | #include <media/v4l2-subdev.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-06-24 05:13:51 -03:00
										 |  |  | #if IS_ENABLED(CONFIG_I2C)
 | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 	struct i2c_client *client = i2c_verify_client(dev); | 
					
						
							|  |  |  | 	return client && | 
					
						
							|  |  |  | 		asd->match.i2c.adapter_id == client->adapter->nr && | 
					
						
							|  |  |  | 		asd->match.i2c.address == client->addr; | 
					
						
							| 
									
										
										
										
											2013-06-24 05:13:51 -03:00
										 |  |  | #else
 | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-19 12:14:46 -03:00
										 |  |  | static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd) | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-19 12:14:46 -03:00
										 |  |  | 	return !strcmp(asd->match.device_name.name, dev_name(dev)); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-19 12:21:29 -03:00
										 |  |  | static bool match_of(struct device *dev, struct v4l2_async_subdev *asd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return dev->of_node == asd->match.of.node; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | static LIST_HEAD(subdev_list); | 
					
						
							|  |  |  | static LIST_HEAD(notifier_list); | 
					
						
							|  |  |  | static DEFINE_MUTEX(list_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier, | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 						    struct v4l2_subdev *sd) | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct v4l2_async_subdev *asd; | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	bool (*match)(struct device *, struct v4l2_async_subdev *); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(asd, ¬ifier->waiting, list) { | 
					
						
							|  |  |  | 		/* bus_type has been verified valid before */ | 
					
						
							| 
									
										
										
										
											2013-07-19 12:14:46 -03:00
										 |  |  | 		switch (asd->match_type) { | 
					
						
							|  |  |  | 		case V4L2_ASYNC_MATCH_CUSTOM: | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 			match = asd->match.custom.match; | 
					
						
							|  |  |  | 			if (!match) | 
					
						
							|  |  |  | 				/* Match always */ | 
					
						
							|  |  |  | 				return asd; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2013-07-19 12:14:46 -03:00
										 |  |  | 		case V4L2_ASYNC_MATCH_DEVNAME: | 
					
						
							|  |  |  | 			match = match_devname; | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2013-07-19 12:14:46 -03:00
										 |  |  | 		case V4L2_ASYNC_MATCH_I2C: | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 			match = match_i2c; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2013-07-19 12:21:29 -03:00
										 |  |  | 		case V4L2_ASYNC_MATCH_OF: | 
					
						
							|  |  |  | 			match = match_of; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			/* Cannot happen, unless someone breaks us */ | 
					
						
							|  |  |  | 			WARN_ON(true); | 
					
						
							|  |  |  | 			return NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* match cannot be NULL here */ | 
					
						
							|  |  |  | 		if (match(sd->dev, asd)) | 
					
						
							|  |  |  | 			return asd; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 				  struct v4l2_subdev *sd, | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 				  struct v4l2_async_subdev *asd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Remove from the waiting list */ | 
					
						
							|  |  |  | 	list_del(&asd->list); | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	sd->asd = asd; | 
					
						
							|  |  |  | 	sd->notifier = notifier; | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (notifier->bound) { | 
					
						
							|  |  |  | 		ret = notifier->bound(notifier, sd, asd); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* Move from the global subdevice list to notifier's done */ | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	list_move(&sd->async_list, ¬ifier->done); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		if (notifier->unbind) | 
					
						
							|  |  |  | 			notifier->unbind(notifier, sd, asd); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (list_empty(¬ifier->waiting) && notifier->complete) | 
					
						
							|  |  |  | 		return notifier->complete(notifier); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | static void v4l2_async_cleanup(struct v4l2_subdev *sd) | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	v4l2_device_unregister_subdev(sd); | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	/* Subdevice driver will reprobe and put the subdev back onto the list */ | 
					
						
							|  |  |  | 	list_del_init(&sd->async_list); | 
					
						
							|  |  |  | 	sd->asd = NULL; | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 	sd->dev = NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, | 
					
						
							|  |  |  | 				 struct v4l2_async_notifier *notifier) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	struct v4l2_subdev *sd, *tmp; | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 	struct v4l2_async_subdev *asd; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!notifier->num_subdevs || notifier->num_subdevs > V4L2_MAX_SUBDEVS) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	notifier->v4l2_dev = v4l2_dev; | 
					
						
							|  |  |  | 	INIT_LIST_HEAD(¬ifier->waiting); | 
					
						
							|  |  |  | 	INIT_LIST_HEAD(¬ifier->done); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < notifier->num_subdevs; i++) { | 
					
						
							| 
									
										
										
										
											2013-07-19 12:31:10 -03:00
										 |  |  | 		asd = notifier->subdevs[i]; | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-19 12:14:46 -03:00
										 |  |  | 		switch (asd->match_type) { | 
					
						
							|  |  |  | 		case V4L2_ASYNC_MATCH_CUSTOM: | 
					
						
							|  |  |  | 		case V4L2_ASYNC_MATCH_DEVNAME: | 
					
						
							|  |  |  | 		case V4L2_ASYNC_MATCH_I2C: | 
					
						
							| 
									
										
										
										
											2013-07-19 12:21:29 -03:00
										 |  |  | 		case V4L2_ASYNC_MATCH_OF: | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL, | 
					
						
							| 
									
										
										
										
											2013-07-19 12:14:46 -03:00
										 |  |  | 				"Invalid match type %u on %p\n", | 
					
						
							|  |  |  | 				asd->match_type, asd); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		list_add_tail(&asd->list, ¬ifier->waiting); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&list_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Keep also completed notifiers on the list */ | 
					
						
							|  |  |  | 	list_add(¬ifier->list, ¬ifier_list); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) { | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 		int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 		asd = v4l2_async_belongs(notifier, sd); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 		if (!asd) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 		ret = v4l2_async_test_notify(notifier, sd, asd); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			mutex_unlock(&list_lock); | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(&list_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(v4l2_async_notifier_register); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	struct v4l2_subdev *sd, *tmp; | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 	unsigned int notif_n_subdev = notifier->num_subdevs; | 
					
						
							|  |  |  | 	unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS); | 
					
						
							| 
									
										
										
										
											2013-11-02 06:20:16 -03:00
										 |  |  | 	struct device **dev; | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 	int i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 07:49:06 -03:00
										 |  |  | 	if (!notifier->v4l2_dev) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-02 06:20:16 -03:00
										 |  |  | 	dev = kmalloc(n_subdev * sizeof(*dev), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!dev) { | 
					
						
							|  |  |  | 		dev_err(notifier->v4l2_dev->dev, | 
					
						
							|  |  |  | 			"Failed to allocate device cache!\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 	mutex_lock(&list_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_del(¬ifier->list); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-31 13:10:18 -03:00
										 |  |  | 	list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) { | 
					
						
							| 
									
										
										
										
											2013-11-02 06:20:16 -03:00
										 |  |  | 		struct device *d; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		d = get_device(sd->dev); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 		v4l2_async_cleanup(sd); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* If we handled USB devices, we'd have to lock the parent too */ | 
					
						
							| 
									
										
										
										
											2013-11-02 06:20:16 -03:00
										 |  |  | 		device_release_driver(d); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (notifier->unbind) | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 			notifier->unbind(notifier, sd, sd->asd); | 
					
						
							| 
									
										
										
										
											2013-11-02 06:20:16 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Store device at the device cache, in order to call | 
					
						
							|  |  |  | 		 * put_device() on the final step | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (dev) | 
					
						
							|  |  |  | 			dev[i++] = d; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			put_device(d); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(&list_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-02 06:20:16 -03:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Call device_attach() to reprobe devices | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * NOTE: If dev allocation fails, i is 0, and the whole loop won't be | 
					
						
							|  |  |  | 	 * executed. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 	while (i--) { | 
					
						
							|  |  |  | 		struct device *d = dev[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (d && device_attach(d) < 0) { | 
					
						
							|  |  |  | 			const char *name = "(none)"; | 
					
						
							|  |  |  | 			int lock = device_trylock(d); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (lock && d->driver) | 
					
						
							|  |  |  | 				name = d->driver->name; | 
					
						
							|  |  |  | 			dev_err(d, "Failed to re-probe to %s\n", name); | 
					
						
							|  |  |  | 			if (lock) | 
					
						
							|  |  |  | 				device_unlock(d); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		put_device(d); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-02 06:20:16 -03:00
										 |  |  | 	kfree(dev); | 
					
						
							| 
									
										
										
										
											2013-07-03 07:49:06 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	notifier->v4l2_dev = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Don't care about the waiting list, it is initialised and populated | 
					
						
							|  |  |  | 	 * upon notifier registration. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(v4l2_async_notifier_unregister); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int v4l2_async_register_subdev(struct v4l2_subdev *sd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct v4l2_async_notifier *notifier; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&list_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	INIT_LIST_HEAD(&sd->async_list); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(notifier, ¬ifier_list, list) { | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 		struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 		if (asd) { | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 			int ret = v4l2_async_test_notify(notifier, sd, asd); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 			mutex_unlock(&list_lock); | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* None matched, wait for hot-plugging */ | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	list_add(&sd->async_list, &subdev_list); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(&list_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(v4l2_async_register_subdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	struct v4l2_async_notifier *notifier = sd->notifier; | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	if (!sd->asd) { | 
					
						
							|  |  |  | 		if (!list_empty(&sd->async_list)) | 
					
						
							|  |  |  | 			v4l2_async_cleanup(sd); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&list_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	list_add(&sd->asd->list, ¬ifier->waiting); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 	v4l2_async_cleanup(sd); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (notifier->unbind) | 
					
						
							| 
									
										
										
										
											2013-07-22 08:01:33 -03:00
										 |  |  | 		notifier->unbind(notifier, sd, sd->asd); | 
					
						
							| 
									
										
										
										
											2013-01-08 07:06:31 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(&list_lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(v4l2_async_unregister_subdev); |