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