629 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Xilinx Video IP Composite Device4 *5 * Copyright (C) 2013-2015 Ideas on Board6 * 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 028#define XVIPP_DMA_MM2S 129 30/**31 * struct xvip_graph_entity - Entity in the video graph32 * @asd: subdev asynchronous registration information33 * @entity: media entity, from the corresponding V4L2 subdev34 * @subdev: V4L2 subdev35 */36struct xvip_graph_entity {37 struct v4l2_async_connection asd; /* must be first */38 struct media_entity *entity;39 struct v4l2_subdev *subdev;40};41 42static inline struct xvip_graph_entity *43to_xvip_entity(struct v4l2_async_connection *asd)44{45 return container_of(asd, struct xvip_graph_entity, asd);46}47 48/* -----------------------------------------------------------------------------49 * Graph Management50 */51 52static struct xvip_graph_entity *53xvip_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_list61 };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 75static 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 of107 * 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 179static struct xvip_dma *180xvip_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 192static 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 285static 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 315static 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 327static const struct v4l2_async_notifier_operations xvip_graph_notify_ops = {328 .bound = xvip_graph_notify_bound,329 .complete = xvip_graph_notify_complete,330};331 332static 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 376err_notifier_cleanup:377 v4l2_async_nf_cleanup(&xdev->notifier);378 fwnode_handle_put(ep);379 return ret;380}381 382static 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 the390 * composite node and then parse entities in turn. The list_for_each391 * loop will handle entities added at the end of the list while walking392 * 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 410static 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 else428 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_CAPTURE445 ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_VIDEO_OUTPUT;446 447 return 0;448}449 450static 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 474static 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 488static 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 525done:526 if (ret < 0)527 xvip_graph_cleanup(xdev);528 529 return ret;530}531 532/* -----------------------------------------------------------------------------533 * Media Controller and V4L2534 */535 536static 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 543static 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 Driver568 */569 570static 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 596error:597 xvip_composite_v4l2_cleanup(xdev);598 return ret;599}600 601static 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 609static const struct of_device_id xvip_composite_of_id_table[] = {610 { .compatible = "xlnx,video" },611 { }612};613MODULE_DEVICE_TABLE(of, xvip_composite_of_id_table);614 615static 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_new = xvip_composite_remove,622};623 624module_platform_driver(xvip_composite_driver);625 626MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");627MODULE_DESCRIPTION("Xilinx Video IP Composite Driver");628MODULE_LICENSE("GPL v2");629