.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | 3 | * Xilinx Video IP Composite Device |
---|
3 | 4 | * |
---|
.. | .. |
---|
6 | 7 | * |
---|
7 | 8 | * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> |
---|
8 | 9 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
---|
9 | | - * |
---|
10 | | - * This program is free software; you can redistribute it and/or modify |
---|
11 | | - * it under the terms of the GNU General Public License version 2 as |
---|
12 | | - * published by the Free Software Foundation. |
---|
13 | 10 | */ |
---|
14 | 11 | |
---|
15 | 12 | #include <linux/list.h> |
---|
.. | .. |
---|
32 | 29 | |
---|
33 | 30 | /** |
---|
34 | 31 | * struct xvip_graph_entity - Entity in the video graph |
---|
35 | | - * @list: list entry in a graph entities list |
---|
36 | | - * @node: the entity's DT node |
---|
37 | | - * @entity: media entity, from the corresponding V4L2 subdev |
---|
38 | 32 | * @asd: subdev asynchronous registration information |
---|
| 33 | + * @entity: media entity, from the corresponding V4L2 subdev |
---|
39 | 34 | * @subdev: V4L2 subdev |
---|
40 | 35 | */ |
---|
41 | 36 | struct xvip_graph_entity { |
---|
42 | | - struct list_head list; |
---|
43 | | - struct device_node *node; |
---|
| 37 | + struct v4l2_async_subdev asd; /* must be first */ |
---|
44 | 38 | struct media_entity *entity; |
---|
45 | | - |
---|
46 | | - struct v4l2_async_subdev asd; |
---|
47 | 39 | struct v4l2_subdev *subdev; |
---|
48 | 40 | }; |
---|
| 41 | + |
---|
| 42 | +static inline struct xvip_graph_entity * |
---|
| 43 | +to_xvip_entity(struct v4l2_async_subdev *asd) |
---|
| 44 | +{ |
---|
| 45 | + return container_of(asd, struct xvip_graph_entity, asd); |
---|
| 46 | +} |
---|
49 | 47 | |
---|
50 | 48 | /* ----------------------------------------------------------------------------- |
---|
51 | 49 | * Graph Management |
---|
.. | .. |
---|
53 | 51 | |
---|
54 | 52 | static struct xvip_graph_entity * |
---|
55 | 53 | xvip_graph_find_entity(struct xvip_composite_device *xdev, |
---|
56 | | - const struct device_node *node) |
---|
| 54 | + const struct fwnode_handle *fwnode) |
---|
57 | 55 | { |
---|
58 | 56 | struct xvip_graph_entity *entity; |
---|
| 57 | + struct v4l2_async_subdev *asd; |
---|
59 | 58 | |
---|
60 | | - list_for_each_entry(entity, &xdev->entities, list) { |
---|
61 | | - if (entity->node == node) |
---|
| 59 | + list_for_each_entry(asd, &xdev->notifier.asd_list, asd_list) { |
---|
| 60 | + entity = to_xvip_entity(asd); |
---|
| 61 | + if (entity->asd.match.fwnode == fwnode) |
---|
62 | 62 | return entity; |
---|
63 | 63 | } |
---|
64 | 64 | |
---|
.. | .. |
---|
75 | 75 | struct media_pad *remote_pad; |
---|
76 | 76 | struct xvip_graph_entity *ent; |
---|
77 | 77 | struct v4l2_fwnode_link link; |
---|
78 | | - struct device_node *ep = NULL; |
---|
| 78 | + struct fwnode_handle *ep = NULL; |
---|
79 | 79 | int ret = 0; |
---|
80 | 80 | |
---|
81 | 81 | dev_dbg(xdev->dev, "creating links for entity %s\n", local->name); |
---|
82 | 82 | |
---|
83 | 83 | while (1) { |
---|
84 | 84 | /* Get the next endpoint and parse its link. */ |
---|
85 | | - ep = of_graph_get_next_endpoint(entity->node, ep); |
---|
| 85 | + ep = fwnode_graph_get_next_endpoint(entity->asd.match.fwnode, |
---|
| 86 | + ep); |
---|
86 | 87 | if (ep == NULL) |
---|
87 | 88 | break; |
---|
88 | 89 | |
---|
89 | | - dev_dbg(xdev->dev, "processing endpoint %pOF\n", ep); |
---|
| 90 | + dev_dbg(xdev->dev, "processing endpoint %p\n", ep); |
---|
90 | 91 | |
---|
91 | | - ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); |
---|
| 92 | + ret = v4l2_fwnode_parse_link(ep, &link); |
---|
92 | 93 | if (ret < 0) { |
---|
93 | | - dev_err(xdev->dev, "failed to parse link for %pOF\n", |
---|
| 94 | + dev_err(xdev->dev, "failed to parse link for %p\n", |
---|
94 | 95 | ep); |
---|
95 | 96 | continue; |
---|
96 | 97 | } |
---|
.. | .. |
---|
99 | 100 | * the link. |
---|
100 | 101 | */ |
---|
101 | 102 | if (link.local_port >= local->num_pads) { |
---|
102 | | - dev_err(xdev->dev, "invalid port number %u for %pOF\n", |
---|
103 | | - link.local_port, |
---|
104 | | - to_of_node(link.local_node)); |
---|
| 103 | + dev_err(xdev->dev, "invalid port number %u for %p\n", |
---|
| 104 | + link.local_port, link.local_node); |
---|
105 | 105 | v4l2_fwnode_put_link(&link); |
---|
106 | 106 | ret = -EINVAL; |
---|
107 | 107 | break; |
---|
.. | .. |
---|
110 | 110 | local_pad = &local->pads[link.local_port]; |
---|
111 | 111 | |
---|
112 | 112 | if (local_pad->flags & MEDIA_PAD_FL_SINK) { |
---|
113 | | - dev_dbg(xdev->dev, "skipping sink port %pOF:%u\n", |
---|
114 | | - to_of_node(link.local_node), |
---|
115 | | - link.local_port); |
---|
| 113 | + dev_dbg(xdev->dev, "skipping sink port %p:%u\n", |
---|
| 114 | + link.local_node, link.local_port); |
---|
116 | 115 | v4l2_fwnode_put_link(&link); |
---|
117 | 116 | continue; |
---|
118 | 117 | } |
---|
119 | 118 | |
---|
120 | 119 | /* Skip DMA engines, they will be processed separately. */ |
---|
121 | 120 | if (link.remote_node == of_fwnode_handle(xdev->dev->of_node)) { |
---|
122 | | - dev_dbg(xdev->dev, "skipping DMA port %pOF:%u\n", |
---|
123 | | - to_of_node(link.local_node), |
---|
124 | | - link.local_port); |
---|
| 121 | + dev_dbg(xdev->dev, "skipping DMA port %p:%u\n", |
---|
| 122 | + link.local_node, link.local_port); |
---|
125 | 123 | v4l2_fwnode_put_link(&link); |
---|
126 | 124 | continue; |
---|
127 | 125 | } |
---|
128 | 126 | |
---|
129 | 127 | /* Find the remote entity. */ |
---|
130 | | - ent = xvip_graph_find_entity(xdev, |
---|
131 | | - to_of_node(link.remote_node)); |
---|
| 128 | + ent = xvip_graph_find_entity(xdev, link.remote_node); |
---|
132 | 129 | if (ent == NULL) { |
---|
133 | | - dev_err(xdev->dev, "no entity found for %pOF\n", |
---|
134 | | - to_of_node(link.remote_node)); |
---|
| 130 | + dev_err(xdev->dev, "no entity found for %p\n", |
---|
| 131 | + link.remote_node); |
---|
135 | 132 | v4l2_fwnode_put_link(&link); |
---|
136 | 133 | ret = -ENODEV; |
---|
137 | 134 | break; |
---|
.. | .. |
---|
140 | 137 | remote = ent->entity; |
---|
141 | 138 | |
---|
142 | 139 | if (link.remote_port >= remote->num_pads) { |
---|
143 | | - dev_err(xdev->dev, "invalid port number %u on %pOF\n", |
---|
144 | | - link.remote_port, to_of_node(link.remote_node)); |
---|
| 140 | + dev_err(xdev->dev, "invalid port number %u on %p\n", |
---|
| 141 | + link.remote_port, link.remote_node); |
---|
145 | 142 | v4l2_fwnode_put_link(&link); |
---|
146 | 143 | ret = -EINVAL; |
---|
147 | 144 | break; |
---|
.. | .. |
---|
168 | 165 | } |
---|
169 | 166 | } |
---|
170 | 167 | |
---|
171 | | - of_node_put(ep); |
---|
| 168 | + fwnode_handle_put(ep); |
---|
172 | 169 | return ret; |
---|
173 | 170 | } |
---|
174 | 171 | |
---|
.. | .. |
---|
230 | 227 | dma->video.name); |
---|
231 | 228 | |
---|
232 | 229 | /* Find the remote entity. */ |
---|
233 | | - ent = xvip_graph_find_entity(xdev, |
---|
234 | | - to_of_node(link.remote_node)); |
---|
| 230 | + ent = xvip_graph_find_entity(xdev, link.remote_node); |
---|
235 | 231 | if (ent == NULL) { |
---|
236 | 232 | dev_err(xdev->dev, "no entity found for %pOF\n", |
---|
237 | 233 | to_of_node(link.remote_node)); |
---|
.. | .. |
---|
289 | 285 | struct xvip_composite_device *xdev = |
---|
290 | 286 | container_of(notifier, struct xvip_composite_device, notifier); |
---|
291 | 287 | struct xvip_graph_entity *entity; |
---|
| 288 | + struct v4l2_async_subdev *asd; |
---|
292 | 289 | int ret; |
---|
293 | 290 | |
---|
294 | 291 | dev_dbg(xdev->dev, "notify complete, all subdevs registered\n"); |
---|
295 | 292 | |
---|
296 | 293 | /* Create links for every entity. */ |
---|
297 | | - list_for_each_entry(entity, &xdev->entities, list) { |
---|
| 294 | + list_for_each_entry(asd, &xdev->notifier.asd_list, asd_list) { |
---|
| 295 | + entity = to_xvip_entity(asd); |
---|
298 | 296 | ret = xvip_graph_build_one(xdev, entity); |
---|
299 | 297 | if (ret < 0) |
---|
300 | 298 | return ret; |
---|
.. | .. |
---|
314 | 312 | |
---|
315 | 313 | static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier, |
---|
316 | 314 | struct v4l2_subdev *subdev, |
---|
317 | | - struct v4l2_async_subdev *asd) |
---|
| 315 | + struct v4l2_async_subdev *unused) |
---|
318 | 316 | { |
---|
319 | 317 | struct xvip_composite_device *xdev = |
---|
320 | 318 | container_of(notifier, struct xvip_composite_device, notifier); |
---|
321 | 319 | struct xvip_graph_entity *entity; |
---|
| 320 | + struct v4l2_async_subdev *asd; |
---|
322 | 321 | |
---|
323 | 322 | /* Locate the entity corresponding to the bound subdev and store the |
---|
324 | 323 | * subdev pointer. |
---|
325 | 324 | */ |
---|
326 | | - list_for_each_entry(entity, &xdev->entities, list) { |
---|
327 | | - if (entity->node != subdev->dev->of_node) |
---|
| 325 | + list_for_each_entry(asd, &xdev->notifier.asd_list, asd_list) { |
---|
| 326 | + entity = to_xvip_entity(asd); |
---|
| 327 | + |
---|
| 328 | + if (entity->asd.match.fwnode != subdev->fwnode) |
---|
328 | 329 | continue; |
---|
329 | 330 | |
---|
330 | 331 | if (entity->subdev) { |
---|
331 | | - dev_err(xdev->dev, "duplicate subdev for node %pOF\n", |
---|
332 | | - entity->node); |
---|
| 332 | + dev_err(xdev->dev, "duplicate subdev for node %p\n", |
---|
| 333 | + entity->asd.match.fwnode); |
---|
333 | 334 | return -EINVAL; |
---|
334 | 335 | } |
---|
335 | 336 | |
---|
.. | .. |
---|
349 | 350 | }; |
---|
350 | 351 | |
---|
351 | 352 | static int xvip_graph_parse_one(struct xvip_composite_device *xdev, |
---|
352 | | - struct device_node *node) |
---|
| 353 | + struct fwnode_handle *fwnode) |
---|
353 | 354 | { |
---|
354 | | - struct xvip_graph_entity *entity; |
---|
355 | | - struct device_node *remote; |
---|
356 | | - struct device_node *ep = NULL; |
---|
| 355 | + struct fwnode_handle *remote; |
---|
| 356 | + struct fwnode_handle *ep = NULL; |
---|
357 | 357 | int ret = 0; |
---|
358 | 358 | |
---|
359 | | - dev_dbg(xdev->dev, "parsing node %pOF\n", node); |
---|
| 359 | + dev_dbg(xdev->dev, "parsing node %p\n", fwnode); |
---|
360 | 360 | |
---|
361 | 361 | while (1) { |
---|
362 | | - ep = of_graph_get_next_endpoint(node, ep); |
---|
| 362 | + struct v4l2_async_subdev *asd; |
---|
| 363 | + |
---|
| 364 | + ep = fwnode_graph_get_next_endpoint(fwnode, ep); |
---|
363 | 365 | if (ep == NULL) |
---|
364 | 366 | break; |
---|
365 | 367 | |
---|
366 | | - dev_dbg(xdev->dev, "handling endpoint %pOF\n", ep); |
---|
| 368 | + dev_dbg(xdev->dev, "handling endpoint %p\n", ep); |
---|
367 | 369 | |
---|
368 | | - remote = of_graph_get_remote_port_parent(ep); |
---|
| 370 | + remote = fwnode_graph_get_remote_port_parent(ep); |
---|
369 | 371 | if (remote == NULL) { |
---|
370 | 372 | ret = -EINVAL; |
---|
371 | | - break; |
---|
| 373 | + goto err_notifier_cleanup; |
---|
372 | 374 | } |
---|
373 | 375 | |
---|
| 376 | + fwnode_handle_put(ep); |
---|
| 377 | + |
---|
374 | 378 | /* Skip entities that we have already processed. */ |
---|
375 | | - if (remote == xdev->dev->of_node || |
---|
| 379 | + if (remote == of_fwnode_handle(xdev->dev->of_node) || |
---|
376 | 380 | xvip_graph_find_entity(xdev, remote)) { |
---|
377 | | - of_node_put(remote); |
---|
| 381 | + fwnode_handle_put(remote); |
---|
378 | 382 | continue; |
---|
379 | 383 | } |
---|
380 | 384 | |
---|
381 | | - entity = devm_kzalloc(xdev->dev, sizeof(*entity), GFP_KERNEL); |
---|
382 | | - if (entity == NULL) { |
---|
383 | | - of_node_put(remote); |
---|
384 | | - ret = -ENOMEM; |
---|
385 | | - break; |
---|
| 385 | + asd = v4l2_async_notifier_add_fwnode_subdev( |
---|
| 386 | + &xdev->notifier, remote, |
---|
| 387 | + sizeof(struct xvip_graph_entity)); |
---|
| 388 | + fwnode_handle_put(remote); |
---|
| 389 | + if (IS_ERR(asd)) { |
---|
| 390 | + ret = PTR_ERR(asd); |
---|
| 391 | + goto err_notifier_cleanup; |
---|
386 | 392 | } |
---|
387 | | - |
---|
388 | | - entity->node = remote; |
---|
389 | | - entity->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; |
---|
390 | | - entity->asd.match.fwnode = of_fwnode_handle(remote); |
---|
391 | | - list_add_tail(&entity->list, &xdev->entities); |
---|
392 | | - xdev->num_subdevs++; |
---|
393 | 393 | } |
---|
394 | 394 | |
---|
395 | | - of_node_put(ep); |
---|
| 395 | + return 0; |
---|
| 396 | + |
---|
| 397 | +err_notifier_cleanup: |
---|
| 398 | + v4l2_async_notifier_cleanup(&xdev->notifier); |
---|
| 399 | + fwnode_handle_put(ep); |
---|
396 | 400 | return ret; |
---|
397 | 401 | } |
---|
398 | 402 | |
---|
399 | 403 | static int xvip_graph_parse(struct xvip_composite_device *xdev) |
---|
400 | 404 | { |
---|
401 | 405 | struct xvip_graph_entity *entity; |
---|
| 406 | + struct v4l2_async_subdev *asd; |
---|
402 | 407 | int ret; |
---|
403 | 408 | |
---|
404 | 409 | /* |
---|
.. | .. |
---|
407 | 412 | * loop will handle entities added at the end of the list while walking |
---|
408 | 413 | * the links. |
---|
409 | 414 | */ |
---|
410 | | - ret = xvip_graph_parse_one(xdev, xdev->dev->of_node); |
---|
| 415 | + ret = xvip_graph_parse_one(xdev, of_fwnode_handle(xdev->dev->of_node)); |
---|
411 | 416 | if (ret < 0) |
---|
412 | 417 | return 0; |
---|
413 | 418 | |
---|
414 | | - list_for_each_entry(entity, &xdev->entities, list) { |
---|
415 | | - ret = xvip_graph_parse_one(xdev, entity->node); |
---|
416 | | - if (ret < 0) |
---|
| 419 | + list_for_each_entry(asd, &xdev->notifier.asd_list, asd_list) { |
---|
| 420 | + entity = to_xvip_entity(asd); |
---|
| 421 | + ret = xvip_graph_parse_one(xdev, entity->asd.match.fwnode); |
---|
| 422 | + if (ret < 0) { |
---|
| 423 | + v4l2_async_notifier_cleanup(&xdev->notifier); |
---|
417 | 424 | break; |
---|
| 425 | + } |
---|
418 | 426 | } |
---|
419 | 427 | |
---|
420 | 428 | return ret; |
---|
.. | .. |
---|
464 | 472 | { |
---|
465 | 473 | struct device_node *ports; |
---|
466 | 474 | struct device_node *port; |
---|
467 | | - int ret; |
---|
| 475 | + int ret = 0; |
---|
468 | 476 | |
---|
469 | 477 | ports = of_get_child_by_name(xdev->dev->of_node, "ports"); |
---|
470 | 478 | if (ports == NULL) { |
---|
.. | .. |
---|
474 | 482 | |
---|
475 | 483 | for_each_child_of_node(ports, port) { |
---|
476 | 484 | ret = xvip_graph_dma_init_one(xdev, port); |
---|
477 | | - if (ret < 0) { |
---|
| 485 | + if (ret) { |
---|
478 | 486 | of_node_put(port); |
---|
479 | | - return ret; |
---|
| 487 | + break; |
---|
480 | 488 | } |
---|
481 | 489 | } |
---|
482 | 490 | |
---|
483 | | - return 0; |
---|
| 491 | + of_node_put(ports); |
---|
| 492 | + return ret; |
---|
484 | 493 | } |
---|
485 | 494 | |
---|
486 | 495 | static void xvip_graph_cleanup(struct xvip_composite_device *xdev) |
---|
487 | 496 | { |
---|
488 | | - struct xvip_graph_entity *entityp; |
---|
489 | | - struct xvip_graph_entity *entity; |
---|
490 | 497 | struct xvip_dma *dmap; |
---|
491 | 498 | struct xvip_dma *dma; |
---|
492 | 499 | |
---|
493 | 500 | v4l2_async_notifier_unregister(&xdev->notifier); |
---|
494 | | - |
---|
495 | | - list_for_each_entry_safe(entity, entityp, &xdev->entities, list) { |
---|
496 | | - of_node_put(entity->node); |
---|
497 | | - list_del(&entity->list); |
---|
498 | | - } |
---|
| 501 | + v4l2_async_notifier_cleanup(&xdev->notifier); |
---|
499 | 502 | |
---|
500 | 503 | list_for_each_entry_safe(dma, dmap, &xdev->dmas, list) { |
---|
501 | 504 | xvip_dma_cleanup(dma); |
---|
.. | .. |
---|
505 | 508 | |
---|
506 | 509 | static int xvip_graph_init(struct xvip_composite_device *xdev) |
---|
507 | 510 | { |
---|
508 | | - struct xvip_graph_entity *entity; |
---|
509 | | - struct v4l2_async_subdev **subdevs = NULL; |
---|
510 | | - unsigned int num_subdevs; |
---|
511 | | - unsigned int i; |
---|
512 | 511 | int ret; |
---|
513 | 512 | |
---|
514 | 513 | /* Init the DMA channels. */ |
---|
.. | .. |
---|
525 | 524 | goto done; |
---|
526 | 525 | } |
---|
527 | 526 | |
---|
528 | | - if (!xdev->num_subdevs) { |
---|
| 527 | + if (list_empty(&xdev->notifier.asd_list)) { |
---|
529 | 528 | dev_err(xdev->dev, "no subdev found in graph\n"); |
---|
530 | 529 | goto done; |
---|
531 | 530 | } |
---|
532 | 531 | |
---|
533 | 532 | /* Register the subdevices notifier. */ |
---|
534 | | - num_subdevs = xdev->num_subdevs; |
---|
535 | | - subdevs = devm_kcalloc(xdev->dev, num_subdevs, sizeof(*subdevs), |
---|
536 | | - GFP_KERNEL); |
---|
537 | | - if (subdevs == NULL) { |
---|
538 | | - ret = -ENOMEM; |
---|
539 | | - goto done; |
---|
540 | | - } |
---|
541 | | - |
---|
542 | | - i = 0; |
---|
543 | | - list_for_each_entry(entity, &xdev->entities, list) |
---|
544 | | - subdevs[i++] = &entity->asd; |
---|
545 | | - |
---|
546 | | - xdev->notifier.subdevs = subdevs; |
---|
547 | | - xdev->notifier.num_subdevs = num_subdevs; |
---|
548 | 533 | xdev->notifier.ops = &xvip_graph_notify_ops; |
---|
549 | 534 | |
---|
550 | 535 | ret = v4l2_async_notifier_register(&xdev->v4l2_dev, &xdev->notifier); |
---|
.. | .. |
---|
578 | 563 | int ret; |
---|
579 | 564 | |
---|
580 | 565 | xdev->media_dev.dev = xdev->dev; |
---|
581 | | - strlcpy(xdev->media_dev.model, "Xilinx Video Composite Device", |
---|
| 566 | + strscpy(xdev->media_dev.model, "Xilinx Video Composite Device", |
---|
582 | 567 | sizeof(xdev->media_dev.model)); |
---|
583 | 568 | xdev->media_dev.hw_revision = 0; |
---|
584 | 569 | |
---|
.. | .. |
---|
610 | 595 | return -ENOMEM; |
---|
611 | 596 | |
---|
612 | 597 | xdev->dev = &pdev->dev; |
---|
613 | | - INIT_LIST_HEAD(&xdev->entities); |
---|
614 | 598 | INIT_LIST_HEAD(&xdev->dmas); |
---|
| 599 | + v4l2_async_notifier_init(&xdev->notifier); |
---|
615 | 600 | |
---|
616 | 601 | ret = xvip_composite_v4l2_init(xdev); |
---|
617 | 602 | if (ret < 0) |
---|