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