1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Xilinx Video IP Composite Device 4 * 5 * Copyright (C) 2013-2015 Ideas on Board 6 * Copyright (C) 2013-2015 Xilinx, Inc. 7 * 8 * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> 9 * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 10 */ 11 12 #include <linux/list.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/of_graph.h> 16 #include <linux/platform_device.h> 17 #include <linux/slab.h> 18 19 #include <media/v4l2-async.h> 20 #include <media/v4l2-common.h> 21 #include <media/v4l2-device.h> 22 #include <media/v4l2-fwnode.h> 23 24 #include "xilinx-dma.h" 25 #include "xilinx-vipp.h" 26 27 #define XVIPP_DMA_S2MM 0 28 #define XVIPP_DMA_MM2S 1 29 30 /** 31 * struct xvip_graph_entity - Entity in the video graph 32 * @asd: subdev asynchronous registration information 33 * @entity: media entity, from the corresponding V4L2 subdev 34 * @subdev: V4L2 subdev 35 */ 36 struct xvip_graph_entity { 37 struct v4l2_async_connection asd; /* must be first */ 38 struct media_entity *entity; 39 struct v4l2_subdev *subdev; 40 }; 41 42 static inline struct xvip_graph_entity * 43 to_xvip_entity(struct v4l2_async_connection *asd) 44 { 45 return container_of(asd, struct xvip_graph_entity, asd); 46 } 47 48 /* ----------------------------------------------------------------------------- 49 * Graph Management 50 */ 51 52 static struct xvip_graph_entity * 53 xvip_graph_find_entity(struct xvip_composite_device *xdev, 54 const struct fwnode_handle *fwnode) 55 { 56 struct xvip_graph_entity *entity; 57 struct v4l2_async_connection *asd; 58 struct list_head *lists[] = { 59 &xdev->notifier.done_list, 60 &xdev->notifier.waiting_list 61 }; 62 unsigned int i; 63 64 for (i = 0; i < ARRAY_SIZE(lists); i++) { 65 list_for_each_entry(asd, lists[i], asc_entry) { 66 entity = to_xvip_entity(asd); 67 if (entity->asd.match.fwnode == fwnode) 68 return entity; 69 } 70 } 71 72 return NULL; 73 } 74 75 static int xvip_graph_build_one(struct xvip_composite_device *xdev, 76 struct xvip_graph_entity *entity) 77 { 78 u32 link_flags = MEDIA_LNK_FL_ENABLED; 79 struct media_entity *local = entity->entity; 80 struct media_entity *remote; 81 struct media_pad *local_pad; 82 struct media_pad *remote_pad; 83 struct xvip_graph_entity *ent; 84 struct v4l2_fwnode_link link; 85 struct fwnode_handle *ep = NULL; 86 int ret = 0; 87 88 dev_dbg(xdev->dev, "creating links for entity %s\n", local->name); 89 90 while (1) { 91 /* Get the next endpoint and parse its link. */ 92 ep = fwnode_graph_get_next_endpoint(entity->asd.match.fwnode, 93 ep); 94 if (ep == NULL) 95 break; 96 97 dev_dbg(xdev->dev, "processing endpoint %p\n", ep); 98 99 ret = v4l2_fwnode_parse_link(ep, &link); 100 if (ret < 0) { 101 dev_err(xdev->dev, "failed to parse link for %p\n", 102 ep); 103 continue; 104 } 105 106 /* Skip sink ports, they will be processed from the other end of 107 * the link. 108 */ 109 if (link.local_port >= local->num_pads) { 110 dev_err(xdev->dev, "invalid port number %u for %p\n", 111 link.local_port, link.local_node); 112 v4l2_fwnode_put_link(&link); 113 ret = -EINVAL; 114 break; 115 } 116 117 local_pad = &local->pads[link.local_port]; 118 119 if (local_pad->flags & MEDIA_PAD_FL_SINK) { 120 dev_dbg(xdev->dev, "skipping sink port %p:%u\n", 121 link.local_node, link.local_port); 122 v4l2_fwnode_put_link(&link); 123 continue; 124 } 125 126 /* Skip DMA engines, they will be processed separately. */ 127 if (link.remote_node == of_fwnode_handle(xdev->dev->of_node)) { 128 dev_dbg(xdev->dev, "skipping DMA port %p:%u\n", 129 link.local_node, link.local_port); 130 v4l2_fwnode_put_link(&link); 131 continue; 132 } 133 134 /* Find the remote entity. */ 135 ent = xvip_graph_find_entity(xdev, link.remote_node); 136 if (ent == NULL) { 137 dev_err(xdev->dev, "no entity found for %p\n", 138 link.remote_node); 139 v4l2_fwnode_put_link(&link); 140 ret = -ENODEV; 141 break; 142 } 143 144 remote = ent->entity; 145 146 if (link.remote_port >= remote->num_pads) { 147 dev_err(xdev->dev, "invalid port number %u on %p\n", 148 link.remote_port, link.remote_node); 149 v4l2_fwnode_put_link(&link); 150 ret = -EINVAL; 151 break; 152 } 153 154 remote_pad = &remote->pads[link.remote_port]; 155 156 v4l2_fwnode_put_link(&link); 157 158 /* Create the media link. */ 159 dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", 160 local->name, local_pad->index, 161 remote->name, remote_pad->index); 162 163 ret = media_create_pad_link(local, local_pad->index, 164 remote, remote_pad->index, 165 link_flags); 166 if (ret < 0) { 167 dev_err(xdev->dev, 168 "failed to create %s:%u -> %s:%u link\n", 169 local->name, local_pad->index, 170 remote->name, remote_pad->index); 171 break; 172 } 173 } 174 175 fwnode_handle_put(ep); 176 return ret; 177 } 178 179 static struct xvip_dma * 180 xvip_graph_find_dma(struct xvip_composite_device *xdev, unsigned int port) 181 { 182 struct xvip_dma *dma; 183 184 list_for_each_entry(dma, &xdev->dmas, list) { 185 if (dma->port == port) 186 return dma; 187 } 188 189 return NULL; 190 } 191 192 static int xvip_graph_build_dma(struct xvip_composite_device *xdev) 193 { 194 u32 link_flags = MEDIA_LNK_FL_ENABLED; 195 struct device_node *node = xdev->dev->of_node; 196 struct media_entity *source; 197 struct media_entity *sink; 198 struct media_pad *source_pad; 199 struct media_pad *sink_pad; 200 struct xvip_graph_entity *ent; 201 struct v4l2_fwnode_link link; 202 struct device_node *ep; 203 struct xvip_dma *dma; 204 int ret = 0; 205 206 dev_dbg(xdev->dev, "creating links for DMA engines\n"); 207 208 for_each_endpoint_of_node(node, ep) { 209 dev_dbg(xdev->dev, "processing endpoint %pOF\n", ep); 210 211 ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); 212 if (ret < 0) { 213 dev_err(xdev->dev, "failed to parse link for %pOF\n", 214 ep); 215 continue; 216 } 217 218 /* Find the DMA engine. */ 219 dma = xvip_graph_find_dma(xdev, link.local_port); 220 if (dma == NULL) { 221 dev_err(xdev->dev, "no DMA engine found for port %u\n", 222 link.local_port); 223 v4l2_fwnode_put_link(&link); 224 ret = -EINVAL; 225 break; 226 } 227 228 dev_dbg(xdev->dev, "creating link for DMA engine %s\n", 229 dma->video.name); 230 231 /* Find the remote entity. */ 232 ent = xvip_graph_find_entity(xdev, link.remote_node); 233 if (ent == NULL) { 234 dev_err(xdev->dev, "no entity found for %pOF\n", 235 to_of_node(link.remote_node)); 236 v4l2_fwnode_put_link(&link); 237 ret = -ENODEV; 238 break; 239 } 240 241 if (link.remote_port >= ent->entity->num_pads) { 242 dev_err(xdev->dev, "invalid port number %u on %pOF\n", 243 link.remote_port, 244 to_of_node(link.remote_node)); 245 v4l2_fwnode_put_link(&link); 246 ret = -EINVAL; 247 break; 248 } 249 250 if (dma->pad.flags & MEDIA_PAD_FL_SOURCE) { 251 source = &dma->video.entity; 252 source_pad = &dma->pad; 253 sink = ent->entity; 254 sink_pad = &sink->pads[link.remote_port]; 255 } else { 256 source = ent->entity; 257 source_pad = &source->pads[link.remote_port]; 258 sink = &dma->video.entity; 259 sink_pad = &dma->pad; 260 } 261 262 v4l2_fwnode_put_link(&link); 263 264 /* Create the media link. */ 265 dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", 266 source->name, source_pad->index, 267 sink->name, sink_pad->index); 268 269 ret = media_create_pad_link(source, source_pad->index, 270 sink, sink_pad->index, 271 link_flags); 272 if (ret < 0) { 273 dev_err(xdev->dev, 274 "failed to create %s:%u -> %s:%u link\n", 275 source->name, source_pad->index, 276 sink->name, sink_pad->index); 277 break; 278 } 279 } 280 281 of_node_put(ep); 282 return ret; 283 } 284 285 static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier) 286 { 287 struct xvip_composite_device *xdev = 288 container_of(notifier, struct xvip_composite_device, notifier); 289 struct xvip_graph_entity *entity; 290 struct v4l2_async_connection *asd; 291 int ret; 292 293 dev_dbg(xdev->dev, "notify complete, all subdevs registered\n"); 294 295 /* Create links for every entity. */ 296 list_for_each_entry(asd, &xdev->notifier.done_list, asc_entry) { 297 entity = to_xvip_entity(asd); 298 ret = xvip_graph_build_one(xdev, entity); 299 if (ret < 0) 300 return ret; 301 } 302 303 /* Create links for DMA channels. */ 304 ret = xvip_graph_build_dma(xdev); 305 if (ret < 0) 306 return ret; 307 308 ret = v4l2_device_register_subdev_nodes(&xdev->v4l2_dev); 309 if (ret < 0) 310 dev_err(xdev->dev, "failed to register subdev nodes\n"); 311 312 return media_device_register(&xdev->media_dev); 313 } 314 315 static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier, 316 struct v4l2_subdev *subdev, 317 struct v4l2_async_connection *asc) 318 { 319 struct xvip_graph_entity *entity = to_xvip_entity(asc); 320 321 entity->entity = &subdev->entity; 322 entity->subdev = subdev; 323 324 return 0; 325 } 326 327 static const struct v4l2_async_notifier_operations xvip_graph_notify_ops = { 328 .bound = xvip_graph_notify_bound, 329 .complete = xvip_graph_notify_complete, 330 }; 331 332 static int xvip_graph_parse_one(struct xvip_composite_device *xdev, 333 struct fwnode_handle *fwnode) 334 { 335 struct fwnode_handle *remote; 336 struct fwnode_handle *ep = NULL; 337 int ret = 0; 338 339 dev_dbg(xdev->dev, "parsing node %p\n", fwnode); 340 341 while (1) { 342 struct xvip_graph_entity *xge; 343 344 ep = fwnode_graph_get_next_endpoint(fwnode, ep); 345 if (ep == NULL) 346 break; 347 348 dev_dbg(xdev->dev, "handling endpoint %p\n", ep); 349 350 remote = fwnode_graph_get_remote_port_parent(ep); 351 if (remote == NULL) { 352 ret = -EINVAL; 353 goto err_notifier_cleanup; 354 } 355 356 fwnode_handle_put(ep); 357 358 /* Skip entities that we have already processed. */ 359 if (remote == of_fwnode_handle(xdev->dev->of_node) || 360 xvip_graph_find_entity(xdev, remote)) { 361 fwnode_handle_put(remote); 362 continue; 363 } 364 365 xge = v4l2_async_nf_add_fwnode(&xdev->notifier, remote, 366 struct xvip_graph_entity); 367 fwnode_handle_put(remote); 368 if (IS_ERR(xge)) { 369 ret = PTR_ERR(xge); 370 goto err_notifier_cleanup; 371 } 372 } 373 374 return 0; 375 376 err_notifier_cleanup: 377 v4l2_async_nf_cleanup(&xdev->notifier); 378 fwnode_handle_put(ep); 379 return ret; 380 } 381 382 static int xvip_graph_parse(struct xvip_composite_device *xdev) 383 { 384 struct xvip_graph_entity *entity; 385 struct v4l2_async_connection *asd; 386 int ret; 387 388 /* 389 * Walk the links to parse the full graph. Start by parsing the 390 * composite node and then parse entities in turn. The list_for_each 391 * loop will handle entities added at the end of the list while walking 392 * the links. 393 */ 394 ret = xvip_graph_parse_one(xdev, of_fwnode_handle(xdev->dev->of_node)); 395 if (ret < 0) 396 return 0; 397 398 list_for_each_entry(asd, &xdev->notifier.waiting_list, asc_entry) { 399 entity = to_xvip_entity(asd); 400 ret = xvip_graph_parse_one(xdev, entity->asd.match.fwnode); 401 if (ret < 0) { 402 v4l2_async_nf_cleanup(&xdev->notifier); 403 break; 404 } 405 } 406 407 return ret; 408 } 409 410 static int xvip_graph_dma_init_one(struct xvip_composite_device *xdev, 411 struct device_node *node) 412 { 413 struct xvip_dma *dma; 414 enum v4l2_buf_type type; 415 const char *direction; 416 unsigned int index; 417 int ret; 418 419 ret = of_property_read_string(node, "direction", &direction); 420 if (ret < 0) 421 return ret; 422 423 if (strcmp(direction, "input") == 0) 424 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 425 else if (strcmp(direction, "output") == 0) 426 type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 427 else 428 return -EINVAL; 429 430 of_property_read_u32(node, "reg", &index); 431 432 dma = devm_kzalloc(xdev->dev, sizeof(*dma), GFP_KERNEL); 433 if (dma == NULL) 434 return -ENOMEM; 435 436 ret = xvip_dma_init(xdev, dma, type, index); 437 if (ret < 0) { 438 dev_err(xdev->dev, "%pOF initialization failed\n", node); 439 return ret; 440 } 441 442 list_add_tail(&dma->list, &xdev->dmas); 443 444 xdev->v4l2_caps |= type == V4L2_BUF_TYPE_VIDEO_CAPTURE 445 ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_VIDEO_OUTPUT; 446 447 return 0; 448 } 449 450 static int xvip_graph_dma_init(struct xvip_composite_device *xdev) 451 { 452 struct device_node *ports; 453 struct device_node *port; 454 int ret = 0; 455 456 ports = of_get_child_by_name(xdev->dev->of_node, "ports"); 457 if (ports == NULL) { 458 dev_err(xdev->dev, "ports node not present\n"); 459 return -EINVAL; 460 } 461 462 for_each_child_of_node(ports, port) { 463 ret = xvip_graph_dma_init_one(xdev, port); 464 if (ret) { 465 of_node_put(port); 466 break; 467 } 468 } 469 470 of_node_put(ports); 471 return ret; 472 } 473 474 static void xvip_graph_cleanup(struct xvip_composite_device *xdev) 475 { 476 struct xvip_dma *dmap; 477 struct xvip_dma *dma; 478 479 v4l2_async_nf_unregister(&xdev->notifier); 480 v4l2_async_nf_cleanup(&xdev->notifier); 481 482 list_for_each_entry_safe(dma, dmap, &xdev->dmas, list) { 483 xvip_dma_cleanup(dma); 484 list_del(&dma->list); 485 } 486 } 487 488 static int xvip_graph_init(struct xvip_composite_device *xdev) 489 { 490 int ret; 491 492 /* Init the DMA channels. */ 493 ret = xvip_graph_dma_init(xdev); 494 if (ret < 0) { 495 dev_err(xdev->dev, "DMA initialization failed\n"); 496 goto done; 497 } 498 499 v4l2_async_nf_init(&xdev->notifier, &xdev->v4l2_dev); 500 501 /* Parse the graph to extract a list of subdevice DT nodes. */ 502 ret = xvip_graph_parse(xdev); 503 if (ret < 0) { 504 dev_err(xdev->dev, "graph parsing failed\n"); 505 goto done; 506 } 507 508 if (list_empty(&xdev->notifier.waiting_list)) { 509 dev_err(xdev->dev, "no subdev found in graph\n"); 510 ret = -ENOENT; 511 goto done; 512 } 513 514 /* Register the subdevices notifier. */ 515 xdev->notifier.ops = &xvip_graph_notify_ops; 516 517 ret = v4l2_async_nf_register(&xdev->notifier); 518 if (ret < 0) { 519 dev_err(xdev->dev, "notifier registration failed\n"); 520 goto done; 521 } 522 523 ret = 0; 524 525 done: 526 if (ret < 0) 527 xvip_graph_cleanup(xdev); 528 529 return ret; 530 } 531 532 /* ----------------------------------------------------------------------------- 533 * Media Controller and V4L2 534 */ 535 536 static void xvip_composite_v4l2_cleanup(struct xvip_composite_device *xdev) 537 { 538 v4l2_device_unregister(&xdev->v4l2_dev); 539 media_device_unregister(&xdev->media_dev); 540 media_device_cleanup(&xdev->media_dev); 541 } 542 543 static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev) 544 { 545 int ret; 546 547 xdev->media_dev.dev = xdev->dev; 548 strscpy(xdev->media_dev.model, "Xilinx Video Composite Device", 549 sizeof(xdev->media_dev.model)); 550 xdev->media_dev.hw_revision = 0; 551 552 media_device_init(&xdev->media_dev); 553 554 xdev->v4l2_dev.mdev = &xdev->media_dev; 555 ret = v4l2_device_register(xdev->dev, &xdev->v4l2_dev); 556 if (ret < 0) { 557 dev_err(xdev->dev, "V4L2 device registration failed (%d)\n", 558 ret); 559 media_device_cleanup(&xdev->media_dev); 560 return ret; 561 } 562 563 return 0; 564 } 565 566 /* ----------------------------------------------------------------------------- 567 * Platform Device Driver 568 */ 569 570 static int xvip_composite_probe(struct platform_device *pdev) 571 { 572 struct xvip_composite_device *xdev; 573 int ret; 574 575 xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL); 576 if (!xdev) 577 return -ENOMEM; 578 579 xdev->dev = &pdev->dev; 580 INIT_LIST_HEAD(&xdev->dmas); 581 582 ret = xvip_composite_v4l2_init(xdev); 583 if (ret < 0) 584 return ret; 585 586 ret = xvip_graph_init(xdev); 587 if (ret < 0) 588 goto error; 589 590 platform_set_drvdata(pdev, xdev); 591 592 dev_info(xdev->dev, "device registered\n"); 593 594 return 0; 595 596 error: 597 xvip_composite_v4l2_cleanup(xdev); 598 return ret; 599 } 600 601 static void xvip_composite_remove(struct platform_device *pdev) 602 { 603 struct xvip_composite_device *xdev = platform_get_drvdata(pdev); 604 605 xvip_graph_cleanup(xdev); 606 xvip_composite_v4l2_cleanup(xdev); 607 } 608 609 static const struct of_device_id xvip_composite_of_id_table[] = { 610 { .compatible = "xlnx,video" }, 611 { } 612 }; 613 MODULE_DEVICE_TABLE(of, xvip_composite_of_id_table); 614 615 static struct platform_driver xvip_composite_driver = { 616 .driver = { 617 .name = "xilinx-video", 618 .of_match_table = xvip_composite_of_id_table, 619 }, 620 .probe = xvip_composite_probe, 621 .remove = xvip_composite_remove, 622 }; 623 624 module_platform_driver(xvip_composite_driver); 625 626 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 627 MODULE_DESCRIPTION("Xilinx Video IP Composite Driver"); 628 MODULE_LICENSE("GPL v2"); 629