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); |
6087b215 | 60 | |
e9e31049 | 61 | return client && |
e9e31049 GL |
62 | asd->match.i2c.adapter_id == client->adapter->nr && |
63 | asd->match.i2c.address == client->addr; | |
fe05e141 GL |
64 | #else |
65 | return false; | |
66 | #endif | |
e9e31049 GL |
67 | } |
68 | ||
86217651 SA |
69 | static bool match_devname(struct v4l2_subdev *sd, |
70 | struct v4l2_async_subdev *asd) | |
e9e31049 | 71 | { |
4e48afec | 72 | return !strcmp(asd->match.device_name, dev_name(sd->dev)); |
e9e31049 GL |
73 | } |
74 | ||
ecdf0cfe SA |
75 | static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) |
76 | { | |
4e48afec | 77 | return sd->fwnode == asd->match.fwnode; |
ecdf0cfe SA |
78 | } |
79 | ||
86217651 SA |
80 | static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) |
81 | { | |
82 | if (!asd->match.custom.match) | |
83 | /* Match always */ | |
84 | return true; | |
85 | ||
86 | return asd->match.custom.match(sd->dev, asd); | |
e7359f8e SN |
87 | } |
88 | ||
e9e31049 GL |
89 | static LIST_HEAD(subdev_list); |
90 | static LIST_HEAD(notifier_list); | |
91 | static DEFINE_MUTEX(list_lock); | |
92 | ||
6087b215 MCC |
93 | static struct v4l2_async_subdev * |
94 | v4l2_async_find_match(struct v4l2_async_notifier *notifier, | |
95 | struct v4l2_subdev *sd) | |
e9e31049 | 96 | { |
6087b215 | 97 | bool (*match)(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd); |
e9e31049 | 98 | struct v4l2_async_subdev *asd; |
e9e31049 GL |
99 | |
100 | list_for_each_entry(asd, ¬ifier->waiting, list) { | |
101 | /* bus_type has been verified valid before */ | |
cfca7644 SN |
102 | switch (asd->match_type) { |
103 | case V4L2_ASYNC_MATCH_CUSTOM: | |
86217651 | 104 | match = match_custom; |
e9e31049 | 105 | break; |
cfca7644 SN |
106 | case V4L2_ASYNC_MATCH_DEVNAME: |
107 | match = match_devname; | |
e9e31049 | 108 | break; |
cfca7644 | 109 | case V4L2_ASYNC_MATCH_I2C: |
e9e31049 GL |
110 | match = match_i2c; |
111 | break; | |
ecdf0cfe SA |
112 | case V4L2_ASYNC_MATCH_FWNODE: |
113 | match = match_fwnode; | |
114 | break; | |
e9e31049 GL |
115 | default: |
116 | /* Cannot happen, unless someone breaks us */ | |
117 | WARN_ON(true); | |
118 | return NULL; | |
119 | } | |
120 | ||
121 | /* match cannot be NULL here */ | |
86217651 | 122 | if (match(sd, asd)) |
e9e31049 GL |
123 | return asd; |
124 | } | |
125 | ||
126 | return NULL; | |
127 | } | |
128 | ||
a6e7003c SL |
129 | /* Compare two async sub-device descriptors for equivalence */ |
130 | static bool asd_equal(struct v4l2_async_subdev *asd_x, | |
131 | struct v4l2_async_subdev *asd_y) | |
132 | { | |
133 | if (asd_x->match_type != asd_y->match_type) | |
134 | return false; | |
135 | ||
136 | switch (asd_x->match_type) { | |
137 | case V4L2_ASYNC_MATCH_DEVNAME: | |
138 | return strcmp(asd_x->match.device_name, | |
139 | asd_y->match.device_name) == 0; | |
140 | case V4L2_ASYNC_MATCH_I2C: | |
141 | return asd_x->match.i2c.adapter_id == | |
142 | asd_y->match.i2c.adapter_id && | |
143 | asd_x->match.i2c.address == | |
144 | asd_y->match.i2c.address; | |
145 | case V4L2_ASYNC_MATCH_FWNODE: | |
146 | return asd_x->match.fwnode == asd_y->match.fwnode; | |
147 | default: | |
148 | break; | |
149 | } | |
150 | ||
151 | return false; | |
152 | } | |
153 | ||
2cab00bb | 154 | /* Find the sub-device notifier registered by a sub-device driver. */ |
6087b215 MCC |
155 | static struct v4l2_async_notifier * |
156 | v4l2_async_find_subdev_notifier(struct v4l2_subdev *sd) | |
2cab00bb SA |
157 | { |
158 | struct v4l2_async_notifier *n; | |
159 | ||
160 | list_for_each_entry(n, ¬ifier_list, list) | |
161 | if (n->sd == sd) | |
162 | return n; | |
163 | ||
164 | return NULL; | |
165 | } | |
166 | ||
167 | /* Get v4l2_device related to the notifier if one can be found. */ | |
6087b215 MCC |
168 | static struct v4l2_device * |
169 | v4l2_async_notifier_find_v4l2_dev(struct v4l2_async_notifier *notifier) | |
2cab00bb SA |
170 | { |
171 | while (notifier->parent) | |
172 | notifier = notifier->parent; | |
173 | ||
174 | return notifier->v4l2_dev; | |
175 | } | |
176 | ||
177 | /* | |
178 | * Return true if all child sub-device notifiers are complete, false otherwise. | |
179 | */ | |
6087b215 MCC |
180 | static bool |
181 | v4l2_async_notifier_can_complete(struct v4l2_async_notifier *notifier) | |
2cab00bb SA |
182 | { |
183 | struct v4l2_subdev *sd; | |
184 | ||
185 | if (!list_empty(¬ifier->waiting)) | |
186 | return false; | |
187 | ||
188 | list_for_each_entry(sd, ¬ifier->done, async_list) { | |
189 | struct v4l2_async_notifier *subdev_notifier = | |
190 | v4l2_async_find_subdev_notifier(sd); | |
191 | ||
192 | if (subdev_notifier && | |
193 | !v4l2_async_notifier_can_complete(subdev_notifier)) | |
194 | return false; | |
195 | } | |
196 | ||
197 | return true; | |
198 | } | |
199 | ||
200 | /* | |
201 | * Complete the master notifier if possible. This is done when all async | |
202 | * sub-devices have been bound; v4l2_device is also available then. | |
203 | */ | |
6087b215 MCC |
204 | static int |
205 | v4l2_async_notifier_try_complete(struct v4l2_async_notifier *notifier) | |
2cab00bb SA |
206 | { |
207 | /* Quick check whether there are still more sub-devices here. */ | |
208 | if (!list_empty(¬ifier->waiting)) | |
209 | return 0; | |
210 | ||
211 | /* Check the entire notifier tree; find the root notifier first. */ | |
212 | while (notifier->parent) | |
213 | notifier = notifier->parent; | |
214 | ||
215 | /* This is root if it has v4l2_dev. */ | |
216 | if (!notifier->v4l2_dev) | |
217 | return 0; | |
218 | ||
219 | /* Is everything ready? */ | |
220 | if (!v4l2_async_notifier_can_complete(notifier)) | |
221 | return 0; | |
222 | ||
223 | return v4l2_async_notifier_call_complete(notifier); | |
224 | } | |
225 | ||
6087b215 MCC |
226 | static int |
227 | v4l2_async_notifier_try_all_subdevs(struct v4l2_async_notifier *notifier); | |
2cab00bb | 228 | |
c8114d90 | 229 | static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, |
a3620cb4 | 230 | struct v4l2_device *v4l2_dev, |
c8114d90 SA |
231 | struct v4l2_subdev *sd, |
232 | struct v4l2_async_subdev *asd) | |
e9e31049 | 233 | { |
2cab00bb | 234 | struct v4l2_async_notifier *subdev_notifier; |
e9e31049 GL |
235 | int ret; |
236 | ||
a3620cb4 | 237 | ret = v4l2_device_register_subdev(v4l2_dev, sd); |
ddddc18b SA |
238 | if (ret < 0) |
239 | return ret; | |
e9e31049 | 240 | |
24def9b5 | 241 | ret = v4l2_async_notifier_call_bound(notifier, sd, asd); |
e9e31049 | 242 | if (ret < 0) { |
24def9b5 | 243 | v4l2_device_unregister_subdev(sd); |
e9e31049 GL |
244 | return ret; |
245 | } | |
246 | ||
47b037a0 TT |
247 | /* Remove from the waiting list */ |
248 | list_del(&asd->list); | |
249 | sd->asd = asd; | |
250 | sd->notifier = notifier; | |
251 | ||
252 | /* Move from the global subdevice list to notifier's done */ | |
253 | list_move(&sd->async_list, ¬ifier->done); | |
254 | ||
2cab00bb SA |
255 | /* |
256 | * See if the sub-device has a notifier. If not, return here. | |
257 | */ | |
258 | subdev_notifier = v4l2_async_find_subdev_notifier(sd); | |
259 | if (!subdev_notifier || subdev_notifier->parent) | |
260 | return 0; | |
261 | ||
262 | /* | |
263 | * Proceed with checking for the sub-device notifier's async | |
264 | * sub-devices, and return the result. The error will be handled by the | |
265 | * caller. | |
266 | */ | |
267 | subdev_notifier->parent = notifier; | |
268 | ||
269 | return v4l2_async_notifier_try_all_subdevs(subdev_notifier); | |
e9e31049 GL |
270 | } |
271 | ||
a3620cb4 | 272 | /* Test all async sub-devices in a notifier for a match. */ |
6087b215 MCC |
273 | static int |
274 | v4l2_async_notifier_try_all_subdevs(struct v4l2_async_notifier *notifier) | |
a3620cb4 | 275 | { |
2cab00bb SA |
276 | struct v4l2_device *v4l2_dev = |
277 | v4l2_async_notifier_find_v4l2_dev(notifier); | |
278 | struct v4l2_subdev *sd; | |
279 | ||
280 | if (!v4l2_dev) | |
281 | return 0; | |
a3620cb4 | 282 | |
2cab00bb SA |
283 | again: |
284 | list_for_each_entry(sd, &subdev_list, async_list) { | |
a3620cb4 SA |
285 | struct v4l2_async_subdev *asd; |
286 | int ret; | |
287 | ||
288 | asd = v4l2_async_find_match(notifier, sd); | |
289 | if (!asd) | |
290 | continue; | |
291 | ||
292 | ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd); | |
293 | if (ret < 0) | |
294 | return ret; | |
2cab00bb SA |
295 | |
296 | /* | |
297 | * v4l2_async_match_notify() may lead to registering a | |
298 | * new notifier and thus changing the async subdevs | |
299 | * list. In order to proceed safely from here, restart | |
300 | * parsing the list from the beginning. | |
301 | */ | |
302 | goto again; | |
a3620cb4 SA |
303 | } |
304 | ||
305 | return 0; | |
306 | } | |
307 | ||
b426b3a6 | 308 | static void v4l2_async_cleanup(struct v4l2_subdev *sd) |
e9e31049 | 309 | { |
e9e31049 | 310 | v4l2_device_unregister_subdev(sd); |
6087b215 MCC |
311 | /* |
312 | * Subdevice driver will reprobe and put the subdev back | |
313 | * onto the list | |
314 | */ | |
b426b3a6 SN |
315 | list_del_init(&sd->async_list); |
316 | sd->asd = NULL; | |
e9e31049 GL |
317 | } |
318 | ||
2cab00bb | 319 | /* Unbind all sub-devices in the notifier tree. */ |
6087b215 MCC |
320 | static void |
321 | v4l2_async_notifier_unbind_all_subdevs(struct v4l2_async_notifier *notifier) | |
fb45f436 SA |
322 | { |
323 | struct v4l2_subdev *sd, *tmp; | |
324 | ||
325 | list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) { | |
2cab00bb SA |
326 | struct v4l2_async_notifier *subdev_notifier = |
327 | v4l2_async_find_subdev_notifier(sd); | |
328 | ||
329 | if (subdev_notifier) | |
330 | v4l2_async_notifier_unbind_all_subdevs(subdev_notifier); | |
331 | ||
ddddc18b | 332 | v4l2_async_notifier_call_unbind(notifier, sd, sd->asd); |
fb45f436 SA |
333 | v4l2_async_cleanup(sd); |
334 | ||
335 | list_move(&sd->async_list, &subdev_list); | |
336 | } | |
2cab00bb SA |
337 | |
338 | notifier->parent = NULL; | |
fb45f436 SA |
339 | } |
340 | ||
a6e7003c SL |
341 | /* See if an async sub-device can be found in a notifier's lists. */ |
342 | static bool | |
343 | __v4l2_async_notifier_has_async_subdev(struct v4l2_async_notifier *notifier, | |
344 | struct v4l2_async_subdev *asd) | |
466cae66 | 345 | { |
a6e7003c | 346 | struct v4l2_async_subdev *asd_y; |
466cae66 SA |
347 | struct v4l2_subdev *sd; |
348 | ||
a6e7003c SL |
349 | list_for_each_entry(asd_y, ¬ifier->waiting, list) |
350 | if (asd_equal(asd, asd_y)) | |
466cae66 | 351 | return true; |
466cae66 SA |
352 | |
353 | list_for_each_entry(sd, ¬ifier->done, async_list) { | |
354 | if (WARN_ON(!sd->asd)) | |
355 | continue; | |
356 | ||
a6e7003c | 357 | if (asd_equal(asd, sd->asd)) |
466cae66 SA |
358 | return true; |
359 | } | |
360 | ||
361 | return false; | |
362 | } | |
363 | ||
364 | /* | |
a6e7003c | 365 | * Find out whether an async sub-device was set up already or |
466cae66 | 366 | * whether it exists in a given notifier before @this_index. |
66beb323 | 367 | * If @this_index < 0, search the notifier's entire @asd_list. |
466cae66 | 368 | */ |
a6e7003c SL |
369 | static bool |
370 | v4l2_async_notifier_has_async_subdev(struct v4l2_async_notifier *notifier, | |
371 | struct v4l2_async_subdev *asd, | |
66beb323 | 372 | int this_index) |
466cae66 | 373 | { |
b47d7ff1 | 374 | struct v4l2_async_subdev *asd_y; |
66beb323 | 375 | int j = 0; |
466cae66 SA |
376 | |
377 | lockdep_assert_held(&list_lock); | |
378 | ||
a6e7003c | 379 | /* Check that an asd is not being added more than once. */ |
66beb323 SL |
380 | list_for_each_entry(asd_y, ¬ifier->asd_list, asd_list) { |
381 | if (this_index >= 0 && j++ >= this_index) | |
382 | break; | |
383 | if (asd_equal(asd, asd_y)) | |
384 | return true; | |
466cae66 SA |
385 | } |
386 | ||
a6e7003c | 387 | /* Check that an asd does not exist in other notifiers. */ |
466cae66 | 388 | list_for_each_entry(notifier, ¬ifier_list, list) |
a6e7003c | 389 | if (__v4l2_async_notifier_has_async_subdev(notifier, asd)) |
466cae66 SA |
390 | return true; |
391 | ||
392 | return false; | |
393 | } | |
394 | ||
b47d7ff1 SL |
395 | static int v4l2_async_notifier_asd_valid(struct v4l2_async_notifier *notifier, |
396 | struct v4l2_async_subdev *asd, | |
66beb323 | 397 | int this_index) |
e9e31049 | 398 | { |
466cae66 SA |
399 | struct device *dev = |
400 | notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL; | |
b47d7ff1 SL |
401 | |
402 | if (!asd) | |
403 | return -EINVAL; | |
404 | ||
405 | switch (asd->match_type) { | |
406 | case V4L2_ASYNC_MATCH_CUSTOM: | |
407 | case V4L2_ASYNC_MATCH_DEVNAME: | |
408 | case V4L2_ASYNC_MATCH_I2C: | |
409 | case V4L2_ASYNC_MATCH_FWNODE: | |
410 | if (v4l2_async_notifier_has_async_subdev(notifier, asd, | |
411 | this_index)) { | |
412 | dev_dbg(dev, "subdev descriptor already listed in this or other notifiers\n"); | |
413 | return -EEXIST; | |
414 | } | |
415 | break; | |
416 | default: | |
417 | dev_err(dev, "Invalid match type %u on %p\n", | |
418 | asd->match_type, asd); | |
419 | return -EINVAL; | |
420 | } | |
421 | ||
422 | return 0; | |
423 | } | |
424 | ||
425 | void v4l2_async_notifier_init(struct v4l2_async_notifier *notifier) | |
426 | { | |
b47d7ff1 | 427 | INIT_LIST_HEAD(¬ifier->asd_list); |
b47d7ff1 SL |
428 | } |
429 | EXPORT_SYMBOL(v4l2_async_notifier_init); | |
430 | ||
431 | static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) | |
432 | { | |
e9e31049 | 433 | struct v4l2_async_subdev *asd; |
66beb323 | 434 | int ret, i = 0; |
e9e31049 | 435 | |
e9e31049 GL |
436 | INIT_LIST_HEAD(¬ifier->waiting); |
437 | INIT_LIST_HEAD(¬ifier->done); | |
438 | ||
466cae66 SA |
439 | mutex_lock(&list_lock); |
440 | ||
66beb323 SL |
441 | list_for_each_entry(asd, ¬ifier->asd_list, asd_list) { |
442 | ret = v4l2_async_notifier_asd_valid(notifier, asd, i++); | |
443 | if (ret) | |
444 | goto err_unlock; | |
e9e31049 | 445 | |
66beb323 | 446 | list_add_tail(&asd->list, ¬ifier->waiting); |
e9e31049 GL |
447 | } |
448 | ||
a3620cb4 | 449 | ret = v4l2_async_notifier_try_all_subdevs(notifier); |
466cae66 | 450 | if (ret < 0) |
2cab00bb | 451 | goto err_unbind; |
e9e31049 | 452 | |
2cab00bb | 453 | ret = v4l2_async_notifier_try_complete(notifier); |
466cae66 | 454 | if (ret < 0) |
2cab00bb | 455 | goto err_unbind; |
fb45f436 | 456 | |
47b037a0 TT |
457 | /* Keep also completed notifiers on the list */ |
458 | list_add(¬ifier->list, ¬ifier_list); | |
459 | ||
e9e31049 GL |
460 | mutex_unlock(&list_lock); |
461 | ||
462 | return 0; | |
fb45f436 | 463 | |
2cab00bb SA |
464 | err_unbind: |
465 | /* | |
466 | * On failure, unbind all sub-devices registered through this notifier. | |
467 | */ | |
fb45f436 SA |
468 | v4l2_async_notifier_unbind_all_subdevs(notifier); |
469 | ||
466cae66 | 470 | err_unlock: |
fb45f436 SA |
471 | mutex_unlock(&list_lock); |
472 | ||
473 | return ret; | |
e9e31049 | 474 | } |
a3620cb4 SA |
475 | |
476 | int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, | |
477 | struct v4l2_async_notifier *notifier) | |
478 | { | |
479 | int ret; | |
480 | ||
2cab00bb | 481 | if (WARN_ON(!v4l2_dev || notifier->sd)) |
a3620cb4 SA |
482 | return -EINVAL; |
483 | ||
484 | notifier->v4l2_dev = v4l2_dev; | |
485 | ||
486 | ret = __v4l2_async_notifier_register(notifier); | |
487 | if (ret) | |
488 | notifier->v4l2_dev = NULL; | |
489 | ||
490 | return ret; | |
491 | } | |
e9e31049 GL |
492 | EXPORT_SYMBOL(v4l2_async_notifier_register); |
493 | ||
2cab00bb SA |
494 | int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd, |
495 | struct v4l2_async_notifier *notifier) | |
496 | { | |
497 | int ret; | |
498 | ||
499 | if (WARN_ON(!sd || notifier->v4l2_dev)) | |
500 | return -EINVAL; | |
501 | ||
502 | notifier->sd = sd; | |
503 | ||
504 | ret = __v4l2_async_notifier_register(notifier); | |
505 | if (ret) | |
506 | notifier->sd = NULL; | |
507 | ||
508 | return ret; | |
509 | } | |
510 | EXPORT_SYMBOL(v4l2_async_subdev_notifier_register); | |
511 | ||
6087b215 MCC |
512 | static void |
513 | __v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) | |
e9e31049 | 514 | { |
aef69d54 | 515 | if (!notifier || (!notifier->v4l2_dev && !notifier->sd)) |
8e3fbfee LP |
516 | return; |
517 | ||
fb45f436 | 518 | v4l2_async_notifier_unbind_all_subdevs(notifier); |
e9e31049 | 519 | |
2cab00bb | 520 | notifier->sd = NULL; |
8e3fbfee | 521 | notifier->v4l2_dev = NULL; |
2cab00bb SA |
522 | |
523 | list_del(¬ifier->list); | |
aef69d54 SA |
524 | } |
525 | ||
526 | void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) | |
527 | { | |
528 | mutex_lock(&list_lock); | |
529 | ||
530 | __v4l2_async_notifier_unregister(notifier); | |
2cab00bb SA |
531 | |
532 | mutex_unlock(&list_lock); | |
e9e31049 GL |
533 | } |
534 | EXPORT_SYMBOL(v4l2_async_notifier_unregister); | |
535 | ||
b47d7ff1 | 536 | static void __v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) |
9ca46531 | 537 | { |
b47d7ff1 | 538 | struct v4l2_async_subdev *asd, *tmp; |
9ca46531 | 539 | |
b47d7ff1 | 540 | if (!notifier) |
9ca46531 SA |
541 | return; |
542 | ||
66beb323 SL |
543 | list_for_each_entry_safe(asd, tmp, ¬ifier->asd_list, asd_list) { |
544 | switch (asd->match_type) { | |
545 | case V4L2_ASYNC_MATCH_FWNODE: | |
546 | fwnode_handle_put(asd->match.fwnode); | |
547 | break; | |
548 | default: | |
549 | break; | |
9ca46531 SA |
550 | } |
551 | ||
66beb323 SL |
552 | list_del(&asd->asd_list); |
553 | kfree(asd); | |
9ca46531 | 554 | } |
b47d7ff1 SL |
555 | } |
556 | ||
557 | void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) | |
558 | { | |
559 | mutex_lock(&list_lock); | |
560 | ||
561 | __v4l2_async_notifier_cleanup(notifier); | |
9ca46531 | 562 | |
b47d7ff1 | 563 | mutex_unlock(&list_lock); |
9ca46531 SA |
564 | } |
565 | EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup); | |
566 | ||
b47d7ff1 SL |
567 | int v4l2_async_notifier_add_subdev(struct v4l2_async_notifier *notifier, |
568 | struct v4l2_async_subdev *asd) | |
569 | { | |
570 | int ret; | |
571 | ||
572 | mutex_lock(&list_lock); | |
573 | ||
66beb323 | 574 | ret = v4l2_async_notifier_asd_valid(notifier, asd, -1); |
b47d7ff1 SL |
575 | if (ret) |
576 | goto unlock; | |
577 | ||
578 | list_add_tail(&asd->asd_list, ¬ifier->asd_list); | |
b47d7ff1 SL |
579 | |
580 | unlock: | |
581 | mutex_unlock(&list_lock); | |
582 | return ret; | |
583 | } | |
584 | EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_subdev); | |
585 | ||
23989b43 SL |
586 | struct v4l2_async_subdev * |
587 | v4l2_async_notifier_add_fwnode_subdev(struct v4l2_async_notifier *notifier, | |
588 | struct fwnode_handle *fwnode, | |
589 | unsigned int asd_struct_size) | |
590 | { | |
591 | struct v4l2_async_subdev *asd; | |
592 | int ret; | |
593 | ||
594 | asd = kzalloc(asd_struct_size, GFP_KERNEL); | |
595 | if (!asd) | |
596 | return ERR_PTR(-ENOMEM); | |
597 | ||
598 | asd->match_type = V4L2_ASYNC_MATCH_FWNODE; | |
599 | asd->match.fwnode = fwnode; | |
600 | ||
601 | ret = v4l2_async_notifier_add_subdev(notifier, asd); | |
602 | if (ret) { | |
603 | kfree(asd); | |
604 | return ERR_PTR(ret); | |
605 | } | |
606 | ||
607 | return asd; | |
608 | } | |
609 | EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_fwnode_subdev); | |
610 | ||
611 | struct v4l2_async_subdev * | |
612 | v4l2_async_notifier_add_i2c_subdev(struct v4l2_async_notifier *notifier, | |
613 | int adapter_id, unsigned short address, | |
614 | unsigned int asd_struct_size) | |
615 | { | |
616 | struct v4l2_async_subdev *asd; | |
617 | int ret; | |
618 | ||
619 | asd = kzalloc(asd_struct_size, GFP_KERNEL); | |
620 | if (!asd) | |
621 | return ERR_PTR(-ENOMEM); | |
622 | ||
623 | asd->match_type = V4L2_ASYNC_MATCH_I2C; | |
624 | asd->match.i2c.adapter_id = adapter_id; | |
625 | asd->match.i2c.address = address; | |
626 | ||
627 | ret = v4l2_async_notifier_add_subdev(notifier, asd); | |
628 | if (ret) { | |
629 | kfree(asd); | |
630 | return ERR_PTR(ret); | |
631 | } | |
632 | ||
633 | return asd; | |
634 | } | |
635 | EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_i2c_subdev); | |
636 | ||
637 | struct v4l2_async_subdev * | |
638 | v4l2_async_notifier_add_devname_subdev(struct v4l2_async_notifier *notifier, | |
639 | const char *device_name, | |
640 | unsigned int asd_struct_size) | |
641 | { | |
642 | struct v4l2_async_subdev *asd; | |
643 | int ret; | |
644 | ||
645 | asd = kzalloc(asd_struct_size, GFP_KERNEL); | |
646 | if (!asd) | |
647 | return ERR_PTR(-ENOMEM); | |
648 | ||
649 | asd->match_type = V4L2_ASYNC_MATCH_DEVNAME; | |
650 | asd->match.device_name = device_name; | |
651 | ||
652 | ret = v4l2_async_notifier_add_subdev(notifier, asd); | |
653 | if (ret) { | |
654 | kfree(asd); | |
655 | return ERR_PTR(ret); | |
656 | } | |
657 | ||
658 | return asd; | |
659 | } | |
660 | EXPORT_SYMBOL_GPL(v4l2_async_notifier_add_devname_subdev); | |
661 | ||
e9e31049 GL |
662 | int v4l2_async_register_subdev(struct v4l2_subdev *sd) |
663 | { | |
2cab00bb | 664 | struct v4l2_async_notifier *subdev_notifier; |
e9e31049 | 665 | struct v4l2_async_notifier *notifier; |
fb45f436 | 666 | int ret; |
e9e31049 | 667 | |
86217651 SA |
668 | /* |
669 | * No reference taken. The reference is held by the device | |
670 | * (struct v4l2_subdev.dev), and async sub-device does not | |
671 | * exist independently of the device at any point of time. | |
672 | */ | |
859969b3 SA |
673 | if (!sd->fwnode && sd->dev) |
674 | sd->fwnode = dev_fwnode(sd->dev); | |
86217651 | 675 | |
e9e31049 GL |
676 | mutex_lock(&list_lock); |
677 | ||
b426b3a6 | 678 | INIT_LIST_HEAD(&sd->async_list); |
e9e31049 GL |
679 | |
680 | list_for_each_entry(notifier, ¬ifier_list, list) { | |
2cab00bb SA |
681 | struct v4l2_device *v4l2_dev = |
682 | v4l2_async_notifier_find_v4l2_dev(notifier); | |
683 | struct v4l2_async_subdev *asd; | |
fb45f436 | 684 | |
2cab00bb SA |
685 | if (!v4l2_dev) |
686 | continue; | |
687 | ||
688 | asd = v4l2_async_find_match(notifier, sd); | |
fb45f436 SA |
689 | if (!asd) |
690 | continue; | |
691 | ||
487cc857 | 692 | ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd); |
fb45f436 | 693 | if (ret) |
2cab00bb | 694 | goto err_unbind; |
fb45f436 | 695 | |
2cab00bb | 696 | ret = v4l2_async_notifier_try_complete(notifier); |
fb45f436 | 697 | if (ret) |
2cab00bb | 698 | goto err_unbind; |
fb45f436 SA |
699 | |
700 | goto out_unlock; | |
e9e31049 GL |
701 | } |
702 | ||
703 | /* None matched, wait for hot-plugging */ | |
b426b3a6 | 704 | list_add(&sd->async_list, &subdev_list); |
e9e31049 | 705 | |
fb45f436 | 706 | out_unlock: |
e9e31049 GL |
707 | mutex_unlock(&list_lock); |
708 | ||
709 | return 0; | |
fb45f436 | 710 | |
2cab00bb SA |
711 | err_unbind: |
712 | /* | |
713 | * Complete failed. Unbind the sub-devices bound through registering | |
714 | * this async sub-device. | |
715 | */ | |
716 | subdev_notifier = v4l2_async_find_subdev_notifier(sd); | |
717 | if (subdev_notifier) | |
718 | v4l2_async_notifier_unbind_all_subdevs(subdev_notifier); | |
719 | ||
720 | if (sd->asd) | |
721 | v4l2_async_notifier_call_unbind(notifier, sd, sd->asd); | |
fb45f436 SA |
722 | v4l2_async_cleanup(sd); |
723 | ||
fb45f436 SA |
724 | mutex_unlock(&list_lock); |
725 | ||
726 | return ret; | |
e9e31049 GL |
727 | } |
728 | EXPORT_SYMBOL(v4l2_async_register_subdev); | |
729 | ||
730 | void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) | |
731 | { | |
e9e31049 GL |
732 | mutex_lock(&list_lock); |
733 | ||
aef69d54 | 734 | __v4l2_async_notifier_unregister(sd->subdev_notifier); |
b47d7ff1 | 735 | __v4l2_async_notifier_cleanup(sd->subdev_notifier); |
aef69d54 SA |
736 | kfree(sd->subdev_notifier); |
737 | sd->subdev_notifier = NULL; | |
738 | ||
7fc4fdb9 SA |
739 | if (sd->asd) { |
740 | struct v4l2_async_notifier *notifier = sd->notifier; | |
e9e31049 | 741 | |
7fc4fdb9 SA |
742 | list_add(&sd->asd->list, ¬ifier->waiting); |
743 | ||
ddddc18b | 744 | v4l2_async_notifier_call_unbind(notifier, sd, sd->asd); |
7fc4fdb9 | 745 | } |
e9e31049 | 746 | |
633d185b NS |
747 | v4l2_async_cleanup(sd); |
748 | ||
e9e31049 GL |
749 | mutex_unlock(&list_lock); |
750 | } | |
751 | EXPORT_SYMBOL(v4l2_async_unregister_subdev); |