]>
Commit | Line | Data |
---|---|---|
e9e31049 GL |
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> | |
758d90e1 | 15 | #include <linux/mm.h> |
e9e31049 GL |
16 | #include <linux/module.h> |
17 | #include <linux/mutex.h> | |
ecdf0cfe | 18 | #include <linux/of.h> |
e9e31049 GL |
19 | #include <linux/platform_device.h> |
20 | #include <linux/slab.h> | |
21 | #include <linux/types.h> | |
22 | ||
23 | #include <media/v4l2-async.h> | |
24 | #include <media/v4l2-device.h> | |
9ca46531 | 25 | #include <media/v4l2-fwnode.h> |
e9e31049 GL |
26 | #include <media/v4l2-subdev.h> |
27 | ||
ddddc18b SA |
28 | static int v4l2_async_notifier_call_bound(struct v4l2_async_notifier *n, |
29 | struct v4l2_subdev *subdev, | |
30 | struct v4l2_async_subdev *asd) | |
31 | { | |
32 | if (!n->ops || !n->ops->bound) | |
33 | return 0; | |
34 | ||
35 | return n->ops->bound(n, subdev, asd); | |
36 | } | |
37 | ||
38 | static void v4l2_async_notifier_call_unbind(struct v4l2_async_notifier *n, | |
39 | struct v4l2_subdev *subdev, | |
40 | struct v4l2_async_subdev *asd) | |
41 | { | |
42 | if (!n->ops || !n->ops->unbind) | |
43 | return; | |
44 | ||
45 | n->ops->unbind(n, subdev, asd); | |
46 | } | |
47 | ||
48 | static int v4l2_async_notifier_call_complete(struct v4l2_async_notifier *n) | |
49 | { | |
50 | if (!n->ops || !n->ops->complete) | |
51 | return 0; | |
52 | ||
53 | return n->ops->complete(n); | |
54 | } | |
55 | ||
86217651 | 56 | static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) |
e9e31049 | 57 | { |
fe05e141 | 58 | #if IS_ENABLED(CONFIG_I2C) |
86217651 | 59 | struct i2c_client *client = i2c_verify_client(sd->dev); |
e9e31049 | 60 | return client && |
e9e31049 GL |
61 | asd->match.i2c.adapter_id == client->adapter->nr && |
62 | asd->match.i2c.address == client->addr; | |
fe05e141 GL |
63 | #else |
64 | return false; | |
65 | #endif | |
e9e31049 GL |
66 | } |
67 | ||
86217651 SA |
68 | static bool match_devname(struct v4l2_subdev *sd, |
69 | struct v4l2_async_subdev *asd) | |
e9e31049 | 70 | { |
86217651 | 71 | return !strcmp(asd->match.device_name.name, dev_name(sd->dev)); |
e9e31049 GL |
72 | } |
73 | ||
ecdf0cfe SA |
74 | static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) |
75 | { | |
12f92866 | 76 | return sd->fwnode == asd->match.fwnode.fwnode; |
ecdf0cfe SA |
77 | } |
78 | ||
86217651 SA |
79 | static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) |
80 | { | |
81 | if (!asd->match.custom.match) | |
82 | /* Match always */ | |
83 | return true; | |
84 | ||
85 | return asd->match.custom.match(sd->dev, asd); | |
e7359f8e SN |
86 | } |
87 | ||
e9e31049 GL |
88 | static LIST_HEAD(subdev_list); |
89 | static LIST_HEAD(notifier_list); | |
90 | static DEFINE_MUTEX(list_lock); | |
91 | ||
c8114d90 SA |
92 | static struct v4l2_async_subdev *v4l2_async_find_match( |
93 | struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd) | |
e9e31049 | 94 | { |
86217651 | 95 | bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *); |
e9e31049 | 96 | struct v4l2_async_subdev *asd; |
e9e31049 GL |
97 | |
98 | list_for_each_entry(asd, ¬ifier->waiting, list) { | |
99 | /* bus_type has been verified valid before */ | |
cfca7644 SN |
100 | switch (asd->match_type) { |
101 | case V4L2_ASYNC_MATCH_CUSTOM: | |
86217651 | 102 | match = match_custom; |
e9e31049 | 103 | break; |
cfca7644 SN |
104 | case V4L2_ASYNC_MATCH_DEVNAME: |
105 | match = match_devname; | |
e9e31049 | 106 | break; |
cfca7644 | 107 | case V4L2_ASYNC_MATCH_I2C: |
e9e31049 GL |
108 | match = match_i2c; |
109 | break; | |
ecdf0cfe SA |
110 | case V4L2_ASYNC_MATCH_FWNODE: |
111 | match = match_fwnode; | |
112 | break; | |
e9e31049 GL |
113 | default: |
114 | /* Cannot happen, unless someone breaks us */ | |
115 | WARN_ON(true); | |
116 | return NULL; | |
117 | } | |
118 | ||
119 | /* match cannot be NULL here */ | |
86217651 | 120 | if (match(sd, asd)) |
e9e31049 GL |
121 | return asd; |
122 | } | |
123 | ||
124 | return NULL; | |
125 | } | |
126 | ||
2cab00bb SA |
127 | /* Find the sub-device notifier registered by a sub-device driver. */ |
128 | static struct v4l2_async_notifier *v4l2_async_find_subdev_notifier( | |
129 | struct v4l2_subdev *sd) | |
130 | { | |
131 | struct v4l2_async_notifier *n; | |
132 | ||
133 | list_for_each_entry(n, ¬ifier_list, list) | |
134 | if (n->sd == sd) | |
135 | return n; | |
136 | ||
137 | return NULL; | |
138 | } | |
139 | ||
140 | /* Get v4l2_device related to the notifier if one can be found. */ | |
141 | static struct v4l2_device *v4l2_async_notifier_find_v4l2_dev( | |
142 | struct v4l2_async_notifier *notifier) | |
143 | { | |
144 | while (notifier->parent) | |
145 | notifier = notifier->parent; | |
146 | ||
147 | return notifier->v4l2_dev; | |
148 | } | |
149 | ||
150 | /* | |
151 | * Return true if all child sub-device notifiers are complete, false otherwise. | |
152 | */ | |
153 | static bool v4l2_async_notifier_can_complete( | |
154 | struct v4l2_async_notifier *notifier) | |
155 | { | |
156 | struct v4l2_subdev *sd; | |
157 | ||
158 | if (!list_empty(¬ifier->waiting)) | |
159 | return false; | |
160 | ||
161 | list_for_each_entry(sd, ¬ifier->done, async_list) { | |
162 | struct v4l2_async_notifier *subdev_notifier = | |
163 | v4l2_async_find_subdev_notifier(sd); | |
164 | ||
165 | if (subdev_notifier && | |
166 | !v4l2_async_notifier_can_complete(subdev_notifier)) | |
167 | return false; | |
168 | } | |
169 | ||
170 | return true; | |
171 | } | |
172 | ||
173 | /* | |
174 | * Complete the master notifier if possible. This is done when all async | |
175 | * sub-devices have been bound; v4l2_device is also available then. | |
176 | */ | |
177 | static int v4l2_async_notifier_try_complete( | |
178 | struct v4l2_async_notifier *notifier) | |
179 | { | |
180 | /* Quick check whether there are still more sub-devices here. */ | |
181 | if (!list_empty(¬ifier->waiting)) | |
182 | return 0; | |
183 | ||
184 | /* Check the entire notifier tree; find the root notifier first. */ | |
185 | while (notifier->parent) | |
186 | notifier = notifier->parent; | |
187 | ||
188 | /* This is root if it has v4l2_dev. */ | |
189 | if (!notifier->v4l2_dev) | |
190 | return 0; | |
191 | ||
192 | /* Is everything ready? */ | |
193 | if (!v4l2_async_notifier_can_complete(notifier)) | |
194 | return 0; | |
195 | ||
196 | return v4l2_async_notifier_call_complete(notifier); | |
197 | } | |
198 | ||
199 | static int v4l2_async_notifier_try_all_subdevs( | |
200 | struct v4l2_async_notifier *notifier); | |
201 | ||
c8114d90 | 202 | static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, |
a3620cb4 | 203 | struct v4l2_device *v4l2_dev, |
c8114d90 SA |
204 | struct v4l2_subdev *sd, |
205 | struct v4l2_async_subdev *asd) | |
e9e31049 | 206 | { |
2cab00bb | 207 | struct v4l2_async_notifier *subdev_notifier; |
e9e31049 GL |
208 | int ret; |
209 | ||
a3620cb4 | 210 | ret = v4l2_device_register_subdev(v4l2_dev, sd); |
ddddc18b SA |
211 | if (ret < 0) |
212 | return ret; | |
e9e31049 | 213 | |
24def9b5 | 214 | ret = v4l2_async_notifier_call_bound(notifier, sd, asd); |
e9e31049 | 215 | if (ret < 0) { |
24def9b5 | 216 | v4l2_device_unregister_subdev(sd); |
e9e31049 GL |
217 | return ret; |
218 | } | |
219 | ||
47b037a0 TT |
220 | /* Remove from the waiting list */ |
221 | list_del(&asd->list); | |
222 | sd->asd = asd; | |
223 | sd->notifier = notifier; | |
224 | ||
225 | /* Move from the global subdevice list to notifier's done */ | |
226 | list_move(&sd->async_list, ¬ifier->done); | |
227 | ||
2cab00bb SA |
228 | /* |
229 | * See if the sub-device has a notifier. If not, return here. | |
230 | */ | |
231 | subdev_notifier = v4l2_async_find_subdev_notifier(sd); | |
232 | if (!subdev_notifier || subdev_notifier->parent) | |
233 | return 0; | |
234 | ||
235 | /* | |
236 | * Proceed with checking for the sub-device notifier's async | |
237 | * sub-devices, and return the result. The error will be handled by the | |
238 | * caller. | |
239 | */ | |
240 | subdev_notifier->parent = notifier; | |
241 | ||
242 | return v4l2_async_notifier_try_all_subdevs(subdev_notifier); | |
e9e31049 GL |
243 | } |
244 | ||
a3620cb4 SA |
245 | /* Test all async sub-devices in a notifier for a match. */ |
246 | static int v4l2_async_notifier_try_all_subdevs( | |
247 | struct v4l2_async_notifier *notifier) | |
248 | { | |
2cab00bb SA |
249 | struct v4l2_device *v4l2_dev = |
250 | v4l2_async_notifier_find_v4l2_dev(notifier); | |
251 | struct v4l2_subdev *sd; | |
252 | ||
253 | if (!v4l2_dev) | |
254 | return 0; | |
a3620cb4 | 255 | |
2cab00bb SA |
256 | again: |
257 | list_for_each_entry(sd, &subdev_list, async_list) { | |
a3620cb4 SA |
258 | struct v4l2_async_subdev *asd; |
259 | int ret; | |
260 | ||
261 | asd = v4l2_async_find_match(notifier, sd); | |
262 | if (!asd) | |
263 | continue; | |
264 | ||
265 | ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd); | |
266 | if (ret < 0) | |
267 | return ret; | |
2cab00bb SA |
268 | |
269 | /* | |
270 | * v4l2_async_match_notify() may lead to registering a | |
271 | * new notifier and thus changing the async subdevs | |
272 | * list. In order to proceed safely from here, restart | |
273 | * parsing the list from the beginning. | |
274 | */ | |
275 | goto again; | |
a3620cb4 SA |
276 | } |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
b426b3a6 | 281 | static void v4l2_async_cleanup(struct v4l2_subdev *sd) |
e9e31049 | 282 | { |
e9e31049 | 283 | v4l2_device_unregister_subdev(sd); |
b426b3a6 SN |
284 | /* Subdevice driver will reprobe and put the subdev back onto the list */ |
285 | list_del_init(&sd->async_list); | |
286 | sd->asd = NULL; | |
e9e31049 GL |
287 | } |
288 | ||
2cab00bb | 289 | /* Unbind all sub-devices in the notifier tree. */ |
fb45f436 SA |
290 | static void v4l2_async_notifier_unbind_all_subdevs( |
291 | struct v4l2_async_notifier *notifier) | |
292 | { | |
293 | struct v4l2_subdev *sd, *tmp; | |
294 | ||
295 | list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) { | |
2cab00bb SA |
296 | struct v4l2_async_notifier *subdev_notifier = |
297 | v4l2_async_find_subdev_notifier(sd); | |
298 | ||
299 | if (subdev_notifier) | |
300 | v4l2_async_notifier_unbind_all_subdevs(subdev_notifier); | |
301 | ||
ddddc18b | 302 | v4l2_async_notifier_call_unbind(notifier, sd, sd->asd); |
fb45f436 SA |
303 | v4l2_async_cleanup(sd); |
304 | ||
305 | list_move(&sd->async_list, &subdev_list); | |
306 | } | |
2cab00bb SA |
307 | |
308 | notifier->parent = NULL; | |
fb45f436 SA |
309 | } |
310 | ||
466cae66 SA |
311 | /* See if an fwnode can be found in a notifier's lists. */ |
312 | static bool __v4l2_async_notifier_fwnode_has_async_subdev( | |
313 | struct v4l2_async_notifier *notifier, struct fwnode_handle *fwnode) | |
314 | { | |
315 | struct v4l2_async_subdev *asd; | |
316 | struct v4l2_subdev *sd; | |
317 | ||
318 | list_for_each_entry(asd, ¬ifier->waiting, list) { | |
319 | if (asd->match_type != V4L2_ASYNC_MATCH_FWNODE) | |
320 | continue; | |
321 | ||
322 | if (asd->match.fwnode.fwnode == fwnode) | |
323 | return true; | |
324 | } | |
325 | ||
326 | list_for_each_entry(sd, ¬ifier->done, async_list) { | |
327 | if (WARN_ON(!sd->asd)) | |
328 | continue; | |
329 | ||
330 | if (sd->asd->match_type != V4L2_ASYNC_MATCH_FWNODE) | |
331 | continue; | |
332 | ||
333 | if (sd->asd->match.fwnode.fwnode == fwnode) | |
334 | return true; | |
335 | } | |
336 | ||
337 | return false; | |
338 | } | |
339 | ||
340 | /* | |
341 | * Find out whether an async sub-device was set up for an fwnode already or | |
342 | * whether it exists in a given notifier before @this_index. | |
343 | */ | |
344 | static bool v4l2_async_notifier_fwnode_has_async_subdev( | |
345 | struct v4l2_async_notifier *notifier, struct fwnode_handle *fwnode, | |
346 | unsigned int this_index) | |
347 | { | |
348 | unsigned int j; | |
349 | ||
350 | lockdep_assert_held(&list_lock); | |
351 | ||
352 | /* Check that an fwnode is not being added more than once. */ | |
353 | for (j = 0; j < this_index; j++) { | |
354 | struct v4l2_async_subdev *asd = notifier->subdevs[this_index]; | |
355 | struct v4l2_async_subdev *other_asd = notifier->subdevs[j]; | |
356 | ||
357 | if (other_asd->match_type == V4L2_ASYNC_MATCH_FWNODE && | |
358 | asd->match.fwnode.fwnode == | |
359 | other_asd->match.fwnode.fwnode) | |
360 | return true; | |
361 | } | |
362 | ||
363 | /* Check than an fwnode did not exist in other notifiers. */ | |
364 | list_for_each_entry(notifier, ¬ifier_list, list) | |
365 | if (__v4l2_async_notifier_fwnode_has_async_subdev( | |
366 | notifier, fwnode)) | |
367 | return true; | |
368 | ||
369 | return false; | |
370 | } | |
371 | ||
a3620cb4 | 372 | static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) |
e9e31049 | 373 | { |
466cae66 SA |
374 | struct device *dev = |
375 | notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL; | |
e9e31049 | 376 | struct v4l2_async_subdev *asd; |
fb45f436 | 377 | int ret; |
e9e31049 GL |
378 | int i; |
379 | ||
a3620cb4 | 380 | if (notifier->num_subdevs > V4L2_MAX_SUBDEVS) |
e9e31049 GL |
381 | return -EINVAL; |
382 | ||
e9e31049 GL |
383 | INIT_LIST_HEAD(¬ifier->waiting); |
384 | INIT_LIST_HEAD(¬ifier->done); | |
385 | ||
466cae66 SA |
386 | mutex_lock(&list_lock); |
387 | ||
e9e31049 | 388 | for (i = 0; i < notifier->num_subdevs; i++) { |
e8419d08 | 389 | asd = notifier->subdevs[i]; |
e9e31049 | 390 | |
cfca7644 SN |
391 | switch (asd->match_type) { |
392 | case V4L2_ASYNC_MATCH_CUSTOM: | |
393 | case V4L2_ASYNC_MATCH_DEVNAME: | |
394 | case V4L2_ASYNC_MATCH_I2C: | |
466cae66 | 395 | break; |
ecdf0cfe | 396 | case V4L2_ASYNC_MATCH_FWNODE: |
466cae66 SA |
397 | if (v4l2_async_notifier_fwnode_has_async_subdev( |
398 | notifier, asd->match.fwnode.fwnode, i)) { | |
399 | dev_err(dev, | |
400 | "fwnode has already been registered or in notifier's subdev list\n"); | |
401 | ret = -EEXIST; | |
402 | goto err_unlock; | |
403 | } | |
e9e31049 GL |
404 | break; |
405 | default: | |
466cae66 | 406 | dev_err(dev, "Invalid match type %u on %p\n", |
cfca7644 | 407 | asd->match_type, asd); |
466cae66 SA |
408 | ret = -EINVAL; |
409 | goto err_unlock; | |
e9e31049 GL |
410 | } |
411 | list_add_tail(&asd->list, ¬ifier->waiting); | |
412 | } | |
413 | ||
a3620cb4 | 414 | ret = v4l2_async_notifier_try_all_subdevs(notifier); |
466cae66 | 415 | if (ret < 0) |
2cab00bb | 416 | goto err_unbind; |
e9e31049 | 417 | |
2cab00bb | 418 | ret = v4l2_async_notifier_try_complete(notifier); |
466cae66 | 419 | if (ret < 0) |
2cab00bb | 420 | goto err_unbind; |
fb45f436 | 421 | |
47b037a0 TT |
422 | /* Keep also completed notifiers on the list */ |
423 | list_add(¬ifier->list, ¬ifier_list); | |
424 | ||
e9e31049 GL |
425 | mutex_unlock(&list_lock); |
426 | ||
427 | return 0; | |
fb45f436 | 428 | |
2cab00bb SA |
429 | err_unbind: |
430 | /* | |
431 | * On failure, unbind all sub-devices registered through this notifier. | |
432 | */ | |
fb45f436 SA |
433 | v4l2_async_notifier_unbind_all_subdevs(notifier); |
434 | ||
466cae66 | 435 | err_unlock: |
fb45f436 SA |
436 | mutex_unlock(&list_lock); |
437 | ||
438 | return ret; | |
e9e31049 | 439 | } |
a3620cb4 SA |
440 | |
441 | int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, | |
442 | struct v4l2_async_notifier *notifier) | |
443 | { | |
444 | int ret; | |
445 | ||
2cab00bb | 446 | if (WARN_ON(!v4l2_dev || notifier->sd)) |
a3620cb4 SA |
447 | return -EINVAL; |
448 | ||
449 | notifier->v4l2_dev = v4l2_dev; | |
450 | ||
451 | ret = __v4l2_async_notifier_register(notifier); | |
452 | if (ret) | |
453 | notifier->v4l2_dev = NULL; | |
454 | ||
455 | return ret; | |
456 | } | |
e9e31049 GL |
457 | EXPORT_SYMBOL(v4l2_async_notifier_register); |
458 | ||
2cab00bb SA |
459 | int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd, |
460 | struct v4l2_async_notifier *notifier) | |
461 | { | |
462 | int ret; | |
463 | ||
464 | if (WARN_ON(!sd || notifier->v4l2_dev)) | |
465 | return -EINVAL; | |
466 | ||
467 | notifier->sd = sd; | |
468 | ||
469 | ret = __v4l2_async_notifier_register(notifier); | |
470 | if (ret) | |
471 | notifier->sd = NULL; | |
472 | ||
473 | return ret; | |
474 | } | |
475 | EXPORT_SYMBOL(v4l2_async_subdev_notifier_register); | |
476 | ||
aef69d54 SA |
477 | static void __v4l2_async_notifier_unregister( |
478 | struct v4l2_async_notifier *notifier) | |
e9e31049 | 479 | { |
aef69d54 | 480 | if (!notifier || (!notifier->v4l2_dev && !notifier->sd)) |
8e3fbfee LP |
481 | return; |
482 | ||
fb45f436 | 483 | v4l2_async_notifier_unbind_all_subdevs(notifier); |
e9e31049 | 484 | |
2cab00bb | 485 | notifier->sd = NULL; |
8e3fbfee | 486 | notifier->v4l2_dev = NULL; |
2cab00bb SA |
487 | |
488 | list_del(¬ifier->list); | |
aef69d54 SA |
489 | } |
490 | ||
491 | void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) | |
492 | { | |
493 | mutex_lock(&list_lock); | |
494 | ||
495 | __v4l2_async_notifier_unregister(notifier); | |
2cab00bb SA |
496 | |
497 | mutex_unlock(&list_lock); | |
e9e31049 GL |
498 | } |
499 | EXPORT_SYMBOL(v4l2_async_notifier_unregister); | |
500 | ||
9ca46531 SA |
501 | void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) |
502 | { | |
503 | unsigned int i; | |
504 | ||
1453ad81 | 505 | if (!notifier || !notifier->max_subdevs) |
9ca46531 SA |
506 | return; |
507 | ||
508 | for (i = 0; i < notifier->num_subdevs; i++) { | |
509 | struct v4l2_async_subdev *asd = notifier->subdevs[i]; | |
510 | ||
511 | switch (asd->match_type) { | |
512 | case V4L2_ASYNC_MATCH_FWNODE: | |
513 | fwnode_handle_put(asd->match.fwnode.fwnode); | |
514 | break; | |
515 | default: | |
516 | WARN_ON_ONCE(true); | |
517 | break; | |
518 | } | |
519 | ||
520 | kfree(asd); | |
521 | } | |
522 | ||
523 | notifier->max_subdevs = 0; | |
524 | notifier->num_subdevs = 0; | |
525 | ||
526 | kvfree(notifier->subdevs); | |
527 | notifier->subdevs = NULL; | |
528 | } | |
529 | EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup); | |
530 | ||
e9e31049 GL |
531 | int v4l2_async_register_subdev(struct v4l2_subdev *sd) |
532 | { | |
2cab00bb | 533 | struct v4l2_async_notifier *subdev_notifier; |
e9e31049 | 534 | struct v4l2_async_notifier *notifier; |
fb45f436 | 535 | int ret; |
e9e31049 | 536 | |
86217651 SA |
537 | /* |
538 | * No reference taken. The reference is held by the device | |
539 | * (struct v4l2_subdev.dev), and async sub-device does not | |
540 | * exist independently of the device at any point of time. | |
541 | */ | |
859969b3 SA |
542 | if (!sd->fwnode && sd->dev) |
543 | sd->fwnode = dev_fwnode(sd->dev); | |
86217651 | 544 | |
e9e31049 GL |
545 | mutex_lock(&list_lock); |
546 | ||
b426b3a6 | 547 | INIT_LIST_HEAD(&sd->async_list); |
e9e31049 GL |
548 | |
549 | list_for_each_entry(notifier, ¬ifier_list, list) { | |
2cab00bb SA |
550 | struct v4l2_device *v4l2_dev = |
551 | v4l2_async_notifier_find_v4l2_dev(notifier); | |
552 | struct v4l2_async_subdev *asd; | |
fb45f436 | 553 | |
2cab00bb SA |
554 | if (!v4l2_dev) |
555 | continue; | |
556 | ||
557 | asd = v4l2_async_find_match(notifier, sd); | |
fb45f436 SA |
558 | if (!asd) |
559 | continue; | |
560 | ||
487cc857 | 561 | ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd); |
fb45f436 | 562 | if (ret) |
2cab00bb | 563 | goto err_unbind; |
fb45f436 | 564 | |
2cab00bb | 565 | ret = v4l2_async_notifier_try_complete(notifier); |
fb45f436 | 566 | if (ret) |
2cab00bb | 567 | goto err_unbind; |
fb45f436 SA |
568 | |
569 | goto out_unlock; | |
e9e31049 GL |
570 | } |
571 | ||
572 | /* None matched, wait for hot-plugging */ | |
b426b3a6 | 573 | list_add(&sd->async_list, &subdev_list); |
e9e31049 | 574 | |
fb45f436 | 575 | out_unlock: |
e9e31049 GL |
576 | mutex_unlock(&list_lock); |
577 | ||
578 | return 0; | |
fb45f436 | 579 | |
2cab00bb SA |
580 | err_unbind: |
581 | /* | |
582 | * Complete failed. Unbind the sub-devices bound through registering | |
583 | * this async sub-device. | |
584 | */ | |
585 | subdev_notifier = v4l2_async_find_subdev_notifier(sd); | |
586 | if (subdev_notifier) | |
587 | v4l2_async_notifier_unbind_all_subdevs(subdev_notifier); | |
588 | ||
589 | if (sd->asd) | |
590 | v4l2_async_notifier_call_unbind(notifier, sd, sd->asd); | |
fb45f436 SA |
591 | v4l2_async_cleanup(sd); |
592 | ||
fb45f436 SA |
593 | mutex_unlock(&list_lock); |
594 | ||
595 | return ret; | |
e9e31049 GL |
596 | } |
597 | EXPORT_SYMBOL(v4l2_async_register_subdev); | |
598 | ||
599 | void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) | |
600 | { | |
e9e31049 GL |
601 | mutex_lock(&list_lock); |
602 | ||
aef69d54 SA |
603 | __v4l2_async_notifier_unregister(sd->subdev_notifier); |
604 | v4l2_async_notifier_cleanup(sd->subdev_notifier); | |
605 | kfree(sd->subdev_notifier); | |
606 | sd->subdev_notifier = NULL; | |
607 | ||
7fc4fdb9 SA |
608 | if (sd->asd) { |
609 | struct v4l2_async_notifier *notifier = sd->notifier; | |
e9e31049 | 610 | |
7fc4fdb9 SA |
611 | list_add(&sd->asd->list, ¬ifier->waiting); |
612 | ||
ddddc18b | 613 | v4l2_async_notifier_call_unbind(notifier, sd, sd->asd); |
7fc4fdb9 | 614 | } |
e9e31049 | 615 | |
633d185b NS |
616 | v4l2_async_cleanup(sd); |
617 | ||
e9e31049 GL |
618 | mutex_unlock(&list_lock); |
619 | } | |
620 | EXPORT_SYMBOL(v4l2_async_unregister_subdev); |