blob: ed31a655b9b7e54ea5668b5d7f5c14c3f0bf18da [file] [log] [blame]
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -03001/*
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
25static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
26{
Guennadi Liakhovetskife05e142013-06-24 05:13:51 -030027#if IS_ENABLED(CONFIG_I2C)
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030028 struct i2c_client *client = i2c_verify_client(dev);
29 return client &&
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030030 asd->match.i2c.adapter_id == client->adapter->nr &&
31 asd->match.i2c.address == client->addr;
Guennadi Liakhovetskife05e142013-06-24 05:13:51 -030032#else
33 return false;
34#endif
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030035}
36
Sylwester Nawrockicfca7642013-07-19 12:14:46 -030037static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd)
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030038{
Sylwester Nawrockicfca7642013-07-19 12:14:46 -030039 return !strcmp(asd->match.device_name.name, dev_name(dev));
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030040}
41
Sylwester Nawrockie7359f82013-07-19 12:21:29 -030042static bool match_of(struct device *dev, struct v4l2_async_subdev *asd)
43{
44 return dev->of_node == asd->match.of.node;
45}
46
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030047static LIST_HEAD(subdev_list);
48static LIST_HEAD(notifier_list);
49static DEFINE_MUTEX(list_lock);
50
51static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
52 struct v4l2_async_subdev_list *asdl)
53{
54 struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
55 struct v4l2_async_subdev *asd;
56 bool (*match)(struct device *,
57 struct v4l2_async_subdev *);
58
59 list_for_each_entry(asd, &notifier->waiting, list) {
60 /* bus_type has been verified valid before */
Sylwester Nawrockicfca7642013-07-19 12:14:46 -030061 switch (asd->match_type) {
62 case V4L2_ASYNC_MATCH_CUSTOM:
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030063 match = asd->match.custom.match;
64 if (!match)
65 /* Match always */
66 return asd;
67 break;
Sylwester Nawrockicfca7642013-07-19 12:14:46 -030068 case V4L2_ASYNC_MATCH_DEVNAME:
69 match = match_devname;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030070 break;
Sylwester Nawrockicfca7642013-07-19 12:14:46 -030071 case V4L2_ASYNC_MATCH_I2C:
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030072 match = match_i2c;
73 break;
Sylwester Nawrockie7359f82013-07-19 12:21:29 -030074 case V4L2_ASYNC_MATCH_OF:
75 match = match_of;
76 break;
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -030077 default:
78 /* Cannot happen, unless someone breaks us */
79 WARN_ON(true);
80 return NULL;
81 }
82
83 /* match cannot be NULL here */
84 if (match(sd->dev, asd))
85 return asd;
86 }
87
88 return NULL;
89}
90
91static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
92 struct v4l2_async_subdev_list *asdl,
93 struct v4l2_async_subdev *asd)
94{
95 struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
96 int ret;
97
98 /* Remove from the waiting list */
99 list_del(&asd->list);
100 asdl->asd = asd;
101 asdl->notifier = notifier;
102
103 if (notifier->bound) {
104 ret = notifier->bound(notifier, sd, asd);
105 if (ret < 0)
106 return ret;
107 }
108 /* Move from the global subdevice list to notifier's done */
109 list_move(&asdl->list, &notifier->done);
110
111 ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
112 if (ret < 0) {
113 if (notifier->unbind)
114 notifier->unbind(notifier, sd, asd);
115 return ret;
116 }
117
118 if (list_empty(&notifier->waiting) && notifier->complete)
119 return notifier->complete(notifier);
120
121 return 0;
122}
123
124static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl)
125{
126 struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
127
128 v4l2_device_unregister_subdev(sd);
129 /* Subdevice driver will reprobe and put asdl back onto the list */
130 list_del_init(&asdl->list);
131 asdl->asd = NULL;
132 sd->dev = NULL;
133}
134
135int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
136 struct v4l2_async_notifier *notifier)
137{
138 struct v4l2_async_subdev_list *asdl, *tmp;
139 struct v4l2_async_subdev *asd;
140 int i;
141
142 if (!notifier->num_subdevs || notifier->num_subdevs > V4L2_MAX_SUBDEVS)
143 return -EINVAL;
144
145 notifier->v4l2_dev = v4l2_dev;
146 INIT_LIST_HEAD(&notifier->waiting);
147 INIT_LIST_HEAD(&notifier->done);
148
149 for (i = 0; i < notifier->num_subdevs; i++) {
Sylwester Nawrockie8419d02013-07-19 12:31:10 -0300150 asd = notifier->subdevs[i];
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300151
Sylwester Nawrockicfca7642013-07-19 12:14:46 -0300152 switch (asd->match_type) {
153 case V4L2_ASYNC_MATCH_CUSTOM:
154 case V4L2_ASYNC_MATCH_DEVNAME:
155 case V4L2_ASYNC_MATCH_I2C:
Sylwester Nawrockie7359f82013-07-19 12:21:29 -0300156 case V4L2_ASYNC_MATCH_OF:
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300157 break;
158 default:
159 dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
Sylwester Nawrockicfca7642013-07-19 12:14:46 -0300160 "Invalid match type %u on %p\n",
161 asd->match_type, asd);
Guennadi Liakhovetskie9e31042013-01-08 07:06:31 -0300162 return -EINVAL;
163 }
164 list_add_tail(&asd->list, &notifier->waiting);
165 }
166
167 mutex_lock(&list_lock);
168
169 /* Keep also completed notifiers on the list */
170 list_add(&notifier->list, &notifier_list);
171
172 list_for_each_entry_safe(asdl, tmp, &subdev_list, list) {
173 int ret;
174
175 asd = v4l2_async_belongs(notifier, asdl);
176 if (!asd)
177 continue;
178
179 ret = v4l2_async_test_notify(notifier, asdl, asd);
180 if (ret < 0) {
181 mutex_unlock(&list_lock);
182 return ret;
183 }
184 }
185
186 mutex_unlock(&list_lock);
187
188 return 0;
189}
190EXPORT_SYMBOL(v4l2_async_notifier_register);
191
192void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
193{
194 struct v4l2_async_subdev_list *asdl, *tmp;
195 unsigned int notif_n_subdev = notifier->num_subdevs;
196 unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
197 struct device *dev[n_subdev];
198 int i = 0;
199
200 mutex_lock(&list_lock);
201
202 list_del(&notifier->list);
203
204 list_for_each_entry_safe(asdl, tmp, &notifier->done, list) {
205 struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
206
207 dev[i] = get_device(sd->dev);
208
209 v4l2_async_cleanup(asdl);
210
211 /* If we handled USB devices, we'd have to lock the parent too */
212 device_release_driver(dev[i++]);
213
214 if (notifier->unbind)
215 notifier->unbind(notifier, sd, sd->asdl.asd);
216 }
217
218 mutex_unlock(&list_lock);
219
220 while (i--) {
221 struct device *d = dev[i];
222
223 if (d && device_attach(d) < 0) {
224 const char *name = "(none)";
225 int lock = device_trylock(d);
226
227 if (lock && d->driver)
228 name = d->driver->name;
229 dev_err(d, "Failed to re-probe to %s\n", name);
230 if (lock)
231 device_unlock(d);
232 }
233 put_device(d);
234 }
235 /*
236 * Don't care about the waiting list, it is initialised and populated
237 * upon notifier registration.
238 */
239}
240EXPORT_SYMBOL(v4l2_async_notifier_unregister);
241
242int v4l2_async_register_subdev(struct v4l2_subdev *sd)
243{
244 struct v4l2_async_subdev_list *asdl = &sd->asdl;
245 struct v4l2_async_notifier *notifier;
246
247 mutex_lock(&list_lock);
248
249 INIT_LIST_HEAD(&asdl->list);
250
251 list_for_each_entry(notifier, &notifier_list, list) {
252 struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl);
253 if (asd) {
254 int ret = v4l2_async_test_notify(notifier, asdl, asd);
255 mutex_unlock(&list_lock);
256 return ret;
257 }
258 }
259
260 /* None matched, wait for hot-plugging */
261 list_add(&asdl->list, &subdev_list);
262
263 mutex_unlock(&list_lock);
264
265 return 0;
266}
267EXPORT_SYMBOL(v4l2_async_register_subdev);
268
269void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
270{
271 struct v4l2_async_subdev_list *asdl = &sd->asdl;
272 struct v4l2_async_notifier *notifier = asdl->notifier;
273
274 if (!asdl->asd) {
275 if (!list_empty(&asdl->list))
276 v4l2_async_cleanup(asdl);
277 return;
278 }
279
280 mutex_lock(&list_lock);
281
282 list_add(&asdl->asd->list, &notifier->waiting);
283
284 v4l2_async_cleanup(asdl);
285
286 if (notifier->unbind)
287 notifier->unbind(notifier, sd, sd->asdl.asd);
288
289 mutex_unlock(&list_lock);
290}
291EXPORT_SYMBOL(v4l2_async_unregister_subdev);