]> Pileus Git - ~andy/linux/blob - drivers/media/v4l2-core/v4l2-async.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
[~andy/linux] / drivers / media / v4l2-core / v4l2-async.c
1 /*
2  * V4L2 asynchronous subdevice registration API
3  *
4  * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/device.h>
12 #include <linux/err.h>
13 #include <linux/i2c.h>
14 #include <linux/list.h>
15 #include <linux/module.h>
16 #include <linux/mutex.h>
17 #include <linux/platform_device.h>
18 #include <linux/slab.h>
19 #include <linux/types.h>
20
21 #include <media/v4l2-async.h>
22 #include <media/v4l2-device.h>
23 #include <media/v4l2-subdev.h>
24
25 static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
26 {
27 #if IS_ENABLED(CONFIG_I2C)
28         struct i2c_client *client = i2c_verify_client(dev);
29         return client &&
30                 asd->bus_type == V4L2_ASYNC_BUS_I2C &&
31                 asd->match.i2c.adapter_id == client->adapter->nr &&
32                 asd->match.i2c.address == client->addr;
33 #else
34         return false;
35 #endif
36 }
37
38 static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd)
39 {
40         return asd->bus_type == V4L2_ASYNC_BUS_PLATFORM &&
41                 !strcmp(asd->match.platform.name, dev_name(dev));
42 }
43
44 static LIST_HEAD(subdev_list);
45 static LIST_HEAD(notifier_list);
46 static DEFINE_MUTEX(list_lock);
47
48 static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
49                                                     struct v4l2_async_subdev_list *asdl)
50 {
51         struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
52         struct v4l2_async_subdev *asd;
53         bool (*match)(struct device *,
54                       struct v4l2_async_subdev *);
55
56         list_for_each_entry(asd, &notifier->waiting, list) {
57                 /* bus_type has been verified valid before */
58                 switch (asd->bus_type) {
59                 case V4L2_ASYNC_BUS_CUSTOM:
60                         match = asd->match.custom.match;
61                         if (!match)
62                                 /* Match always */
63                                 return asd;
64                         break;
65                 case V4L2_ASYNC_BUS_PLATFORM:
66                         match = match_platform;
67                         break;
68                 case V4L2_ASYNC_BUS_I2C:
69                         match = match_i2c;
70                         break;
71                 default:
72                         /* Cannot happen, unless someone breaks us */
73                         WARN_ON(true);
74                         return NULL;
75                 }
76
77                 /* match cannot be NULL here */
78                 if (match(sd->dev, asd))
79                         return asd;
80         }
81
82         return NULL;
83 }
84
85 static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
86                                   struct v4l2_async_subdev_list *asdl,
87                                   struct v4l2_async_subdev *asd)
88 {
89         struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
90         int ret;
91
92         /* Remove from the waiting list */
93         list_del(&asd->list);
94         asdl->asd = asd;
95         asdl->notifier = notifier;
96
97         if (notifier->bound) {
98                 ret = notifier->bound(notifier, sd, asd);
99                 if (ret < 0)
100                         return ret;
101         }
102         /* Move from the global subdevice list to notifier's done */
103         list_move(&asdl->list, &notifier->done);
104
105         ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
106         if (ret < 0) {
107                 if (notifier->unbind)
108                         notifier->unbind(notifier, sd, asd);
109                 return ret;
110         }
111
112         if (list_empty(&notifier->waiting) && notifier->complete)
113                 return notifier->complete(notifier);
114
115         return 0;
116 }
117
118 static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl)
119 {
120         struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
121
122         v4l2_device_unregister_subdev(sd);
123         /* Subdevice driver will reprobe and put asdl back onto the list */
124         list_del_init(&asdl->list);
125         asdl->asd = NULL;
126         sd->dev = NULL;
127 }
128
129 int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
130                                  struct v4l2_async_notifier *notifier)
131 {
132         struct v4l2_async_subdev_list *asdl, *tmp;
133         struct v4l2_async_subdev *asd;
134         int i;
135
136         if (!notifier->num_subdevs || notifier->num_subdevs > V4L2_MAX_SUBDEVS)
137                 return -EINVAL;
138
139         notifier->v4l2_dev = v4l2_dev;
140         INIT_LIST_HEAD(&notifier->waiting);
141         INIT_LIST_HEAD(&notifier->done);
142
143         for (i = 0; i < notifier->num_subdevs; i++) {
144                 asd = notifier->subdev[i];
145
146                 switch (asd->bus_type) {
147                 case V4L2_ASYNC_BUS_CUSTOM:
148                 case V4L2_ASYNC_BUS_PLATFORM:
149                 case V4L2_ASYNC_BUS_I2C:
150                         break;
151                 default:
152                         dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
153                                 "Invalid bus-type %u on %p\n",
154                                 asd->bus_type, asd);
155                         return -EINVAL;
156                 }
157                 list_add_tail(&asd->list, &notifier->waiting);
158         }
159
160         mutex_lock(&list_lock);
161
162         /* Keep also completed notifiers on the list */
163         list_add(&notifier->list, &notifier_list);
164
165         list_for_each_entry_safe(asdl, tmp, &subdev_list, list) {
166                 int ret;
167
168                 asd = v4l2_async_belongs(notifier, asdl);
169                 if (!asd)
170                         continue;
171
172                 ret = v4l2_async_test_notify(notifier, asdl, asd);
173                 if (ret < 0) {
174                         mutex_unlock(&list_lock);
175                         return ret;
176                 }
177         }
178
179         mutex_unlock(&list_lock);
180
181         return 0;
182 }
183 EXPORT_SYMBOL(v4l2_async_notifier_register);
184
185 void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
186 {
187         struct v4l2_async_subdev_list *asdl, *tmp;
188         unsigned int notif_n_subdev = notifier->num_subdevs;
189         unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
190         struct device *dev[n_subdev];
191         int i = 0;
192
193         mutex_lock(&list_lock);
194
195         list_del(&notifier->list);
196
197         list_for_each_entry_safe(asdl, tmp, &notifier->done, list) {
198                 struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
199
200                 dev[i] = get_device(sd->dev);
201
202                 v4l2_async_cleanup(asdl);
203
204                 /* If we handled USB devices, we'd have to lock the parent too */
205                 device_release_driver(dev[i++]);
206
207                 if (notifier->unbind)
208                         notifier->unbind(notifier, sd, sd->asdl.asd);
209         }
210
211         mutex_unlock(&list_lock);
212
213         while (i--) {
214                 struct device *d = dev[i];
215
216                 if (d && device_attach(d) < 0) {
217                         const char *name = "(none)";
218                         int lock = device_trylock(d);
219
220                         if (lock && d->driver)
221                                 name = d->driver->name;
222                         dev_err(d, "Failed to re-probe to %s\n", name);
223                         if (lock)
224                                 device_unlock(d);
225                 }
226                 put_device(d);
227         }
228         /*
229          * Don't care about the waiting list, it is initialised and populated
230          * upon notifier registration.
231          */
232 }
233 EXPORT_SYMBOL(v4l2_async_notifier_unregister);
234
235 int v4l2_async_register_subdev(struct v4l2_subdev *sd)
236 {
237         struct v4l2_async_subdev_list *asdl = &sd->asdl;
238         struct v4l2_async_notifier *notifier;
239
240         mutex_lock(&list_lock);
241
242         INIT_LIST_HEAD(&asdl->list);
243
244         list_for_each_entry(notifier, &notifier_list, list) {
245                 struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl);
246                 if (asd) {
247                         int ret = v4l2_async_test_notify(notifier, asdl, asd);
248                         mutex_unlock(&list_lock);
249                         return ret;
250                 }
251         }
252
253         /* None matched, wait for hot-plugging */
254         list_add(&asdl->list, &subdev_list);
255
256         mutex_unlock(&list_lock);
257
258         return 0;
259 }
260 EXPORT_SYMBOL(v4l2_async_register_subdev);
261
262 void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
263 {
264         struct v4l2_async_subdev_list *asdl = &sd->asdl;
265         struct v4l2_async_notifier *notifier = asdl->notifier;
266
267         if (!asdl->asd) {
268                 if (!list_empty(&asdl->list))
269                         v4l2_async_cleanup(asdl);
270                 return;
271         }
272
273         mutex_lock(&list_lock);
274
275         list_add(&asdl->asd->list, &notifier->waiting);
276
277         v4l2_async_cleanup(asdl);
278
279         if (notifier->unbind)
280                 notifier->unbind(notifier, sd, sd->asdl.asd);
281
282         mutex_unlock(&list_lock);
283 }
284 EXPORT_SYMBOL(v4l2_async_unregister_subdev);