1df330515SLaurent Pinchart /* 2df330515SLaurent Pinchart * Xilinx Video IP Composite Device 3df330515SLaurent Pinchart * 4df330515SLaurent Pinchart * Copyright (C) 2013-2015 Ideas on Board 5df330515SLaurent Pinchart * Copyright (C) 2013-2015 Xilinx, Inc. 6df330515SLaurent Pinchart * 7df330515SLaurent Pinchart * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> 8df330515SLaurent Pinchart * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 9df330515SLaurent Pinchart * 10df330515SLaurent Pinchart * This program is free software; you can redistribute it and/or modify 11df330515SLaurent Pinchart * it under the terms of the GNU General Public License version 2 as 12df330515SLaurent Pinchart * published by the Free Software Foundation. 13df330515SLaurent Pinchart */ 14df330515SLaurent Pinchart 15df330515SLaurent Pinchart #include <linux/list.h> 16df330515SLaurent Pinchart #include <linux/module.h> 17df330515SLaurent Pinchart #include <linux/of.h> 18df330515SLaurent Pinchart #include <linux/of_graph.h> 19df330515SLaurent Pinchart #include <linux/platform_device.h> 20df330515SLaurent Pinchart #include <linux/slab.h> 21df330515SLaurent Pinchart 22df330515SLaurent Pinchart #include <media/v4l2-async.h> 23df330515SLaurent Pinchart #include <media/v4l2-common.h> 24df330515SLaurent Pinchart #include <media/v4l2-device.h> 25859969b3SSakari Ailus #include <media/v4l2-fwnode.h> 26df330515SLaurent Pinchart 27df330515SLaurent Pinchart #include "xilinx-dma.h" 28df330515SLaurent Pinchart #include "xilinx-vipp.h" 29df330515SLaurent Pinchart 30df330515SLaurent Pinchart #define XVIPP_DMA_S2MM 0 31df330515SLaurent Pinchart #define XVIPP_DMA_MM2S 1 32df330515SLaurent Pinchart 33df330515SLaurent Pinchart /** 34df330515SLaurent Pinchart * struct xvip_graph_entity - Entity in the video graph 35df330515SLaurent Pinchart * @list: list entry in a graph entities list 36df330515SLaurent Pinchart * @node: the entity's DT node 37df330515SLaurent Pinchart * @entity: media entity, from the corresponding V4L2 subdev 38df330515SLaurent Pinchart * @asd: subdev asynchronous registration information 39df330515SLaurent Pinchart * @subdev: V4L2 subdev 40df330515SLaurent Pinchart */ 41df330515SLaurent Pinchart struct xvip_graph_entity { 42df330515SLaurent Pinchart struct list_head list; 43df330515SLaurent Pinchart struct device_node *node; 44df330515SLaurent Pinchart struct media_entity *entity; 45df330515SLaurent Pinchart 46df330515SLaurent Pinchart struct v4l2_async_subdev asd; 47df330515SLaurent Pinchart struct v4l2_subdev *subdev; 48df330515SLaurent Pinchart }; 49df330515SLaurent Pinchart 50df330515SLaurent Pinchart /* ----------------------------------------------------------------------------- 51df330515SLaurent Pinchart * Graph Management 52df330515SLaurent Pinchart */ 53df330515SLaurent Pinchart 54df330515SLaurent Pinchart static struct xvip_graph_entity * 55df330515SLaurent Pinchart xvip_graph_find_entity(struct xvip_composite_device *xdev, 56df330515SLaurent Pinchart const struct device_node *node) 57df330515SLaurent Pinchart { 58df330515SLaurent Pinchart struct xvip_graph_entity *entity; 59df330515SLaurent Pinchart 60df330515SLaurent Pinchart list_for_each_entry(entity, &xdev->entities, list) { 61df330515SLaurent Pinchart if (entity->node == node) 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; 78df330515SLaurent Pinchart struct device_node *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. */ 85*ef94711aSAkinobu Mita ep = of_graph_get_next_endpoint(entity->node, ep); 86*ef94711aSAkinobu Mita if (ep == NULL) 87df330515SLaurent Pinchart break; 88df330515SLaurent Pinchart 8968d9c47bSRob Herring dev_dbg(xdev->dev, "processing endpoint %pOF\n", ep); 90df330515SLaurent Pinchart 91859969b3SSakari Ailus ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); 92df330515SLaurent Pinchart if (ret < 0) { 9368d9c47bSRob Herring dev_err(xdev->dev, "failed to parse link for %pOF\n", 9468d9c47bSRob Herring ep); 95df330515SLaurent Pinchart continue; 96df330515SLaurent Pinchart } 97df330515SLaurent Pinchart 98df330515SLaurent Pinchart /* Skip sink ports, they will be processed from the other end of 99df330515SLaurent Pinchart * the link. 100df330515SLaurent Pinchart */ 101df330515SLaurent Pinchart if (link.local_port >= local->num_pads) { 10268d9c47bSRob Herring dev_err(xdev->dev, "invalid port number %u for %pOF\n", 103859969b3SSakari Ailus link.local_port, 10468d9c47bSRob Herring to_of_node(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) { 11368d9c47bSRob Herring dev_dbg(xdev->dev, "skipping sink port %pOF:%u\n", 11468d9c47bSRob Herring to_of_node(link.local_node), 115859969b3SSakari Ailus link.local_port); 116859969b3SSakari Ailus v4l2_fwnode_put_link(&link); 117df330515SLaurent Pinchart continue; 118df330515SLaurent Pinchart } 119df330515SLaurent Pinchart 120df330515SLaurent Pinchart /* Skip DMA engines, they will be processed separately. */ 121859969b3SSakari Ailus if (link.remote_node == of_fwnode_handle(xdev->dev->of_node)) { 12268d9c47bSRob Herring dev_dbg(xdev->dev, "skipping DMA port %pOF:%u\n", 12368d9c47bSRob Herring to_of_node(link.local_node), 124859969b3SSakari Ailus link.local_port); 125859969b3SSakari Ailus v4l2_fwnode_put_link(&link); 126df330515SLaurent Pinchart continue; 127df330515SLaurent Pinchart } 128df330515SLaurent Pinchart 129df330515SLaurent Pinchart /* Find the remote entity. */ 130859969b3SSakari Ailus ent = xvip_graph_find_entity(xdev, 131859969b3SSakari Ailus to_of_node(link.remote_node)); 132df330515SLaurent Pinchart if (ent == NULL) { 13368d9c47bSRob Herring dev_err(xdev->dev, "no entity found for %pOF\n", 13468d9c47bSRob Herring to_of_node(link.remote_node)); 135859969b3SSakari Ailus v4l2_fwnode_put_link(&link); 136df330515SLaurent Pinchart ret = -ENODEV; 137df330515SLaurent Pinchart break; 138df330515SLaurent Pinchart } 139df330515SLaurent Pinchart 140df330515SLaurent Pinchart remote = ent->entity; 141df330515SLaurent Pinchart 142df330515SLaurent Pinchart if (link.remote_port >= remote->num_pads) { 14368d9c47bSRob Herring dev_err(xdev->dev, "invalid port number %u on %pOF\n", 14468d9c47bSRob Herring link.remote_port, to_of_node(link.remote_node)); 145859969b3SSakari Ailus v4l2_fwnode_put_link(&link); 146df330515SLaurent Pinchart ret = -EINVAL; 147df330515SLaurent Pinchart break; 148df330515SLaurent Pinchart } 149df330515SLaurent Pinchart 150df330515SLaurent Pinchart remote_pad = &remote->pads[link.remote_port]; 151df330515SLaurent Pinchart 152859969b3SSakari Ailus v4l2_fwnode_put_link(&link); 153df330515SLaurent Pinchart 154df330515SLaurent Pinchart /* Create the media link. */ 155df330515SLaurent Pinchart dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", 156df330515SLaurent Pinchart local->name, local_pad->index, 157df330515SLaurent Pinchart remote->name, remote_pad->index); 158df330515SLaurent Pinchart 1598df00a15SMauro Carvalho Chehab ret = media_create_pad_link(local, local_pad->index, 160df330515SLaurent Pinchart remote, remote_pad->index, 161df330515SLaurent Pinchart link_flags); 162df330515SLaurent Pinchart if (ret < 0) { 163df330515SLaurent Pinchart dev_err(xdev->dev, 164df330515SLaurent Pinchart "failed to create %s:%u -> %s:%u link\n", 165df330515SLaurent Pinchart local->name, local_pad->index, 166df330515SLaurent Pinchart remote->name, remote_pad->index); 167df330515SLaurent Pinchart break; 168df330515SLaurent Pinchart } 169df330515SLaurent Pinchart } 170df330515SLaurent Pinchart 171df330515SLaurent Pinchart of_node_put(ep); 172df330515SLaurent Pinchart return ret; 173df330515SLaurent Pinchart } 174df330515SLaurent Pinchart 175df330515SLaurent Pinchart static struct xvip_dma * 176df330515SLaurent Pinchart xvip_graph_find_dma(struct xvip_composite_device *xdev, unsigned int port) 177df330515SLaurent Pinchart { 178df330515SLaurent Pinchart struct xvip_dma *dma; 179df330515SLaurent Pinchart 180df330515SLaurent Pinchart list_for_each_entry(dma, &xdev->dmas, list) { 181df330515SLaurent Pinchart if (dma->port == port) 182df330515SLaurent Pinchart return dma; 183df330515SLaurent Pinchart } 184df330515SLaurent Pinchart 185df330515SLaurent Pinchart return NULL; 186df330515SLaurent Pinchart } 187df330515SLaurent Pinchart 188df330515SLaurent Pinchart static int xvip_graph_build_dma(struct xvip_composite_device *xdev) 189df330515SLaurent Pinchart { 190df330515SLaurent Pinchart u32 link_flags = MEDIA_LNK_FL_ENABLED; 191df330515SLaurent Pinchart struct device_node *node = xdev->dev->of_node; 192df330515SLaurent Pinchart struct media_entity *source; 193df330515SLaurent Pinchart struct media_entity *sink; 194df330515SLaurent Pinchart struct media_pad *source_pad; 195df330515SLaurent Pinchart struct media_pad *sink_pad; 196df330515SLaurent Pinchart struct xvip_graph_entity *ent; 197859969b3SSakari Ailus struct v4l2_fwnode_link link; 198df330515SLaurent Pinchart struct device_node *ep = NULL; 199df330515SLaurent Pinchart struct xvip_dma *dma; 200df330515SLaurent Pinchart int ret = 0; 201df330515SLaurent Pinchart 202df330515SLaurent Pinchart dev_dbg(xdev->dev, "creating links for DMA engines\n"); 203df330515SLaurent Pinchart 204df330515SLaurent Pinchart while (1) { 205df330515SLaurent Pinchart /* Get the next endpoint and parse its link. */ 206*ef94711aSAkinobu Mita ep = of_graph_get_next_endpoint(node, ep); 207*ef94711aSAkinobu Mita if (ep == NULL) 208df330515SLaurent Pinchart break; 209df330515SLaurent Pinchart 21068d9c47bSRob Herring dev_dbg(xdev->dev, "processing endpoint %pOF\n", ep); 211df330515SLaurent Pinchart 212859969b3SSakari Ailus ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); 213df330515SLaurent Pinchart if (ret < 0) { 21468d9c47bSRob Herring dev_err(xdev->dev, "failed to parse link for %pOF\n", 21568d9c47bSRob Herring ep); 216df330515SLaurent Pinchart continue; 217df330515SLaurent Pinchart } 218df330515SLaurent Pinchart 219df330515SLaurent Pinchart /* Find the DMA engine. */ 220df330515SLaurent Pinchart dma = xvip_graph_find_dma(xdev, link.local_port); 221df330515SLaurent Pinchart if (dma == NULL) { 222df330515SLaurent Pinchart dev_err(xdev->dev, "no DMA engine found for port %u\n", 223df330515SLaurent Pinchart link.local_port); 224859969b3SSakari Ailus v4l2_fwnode_put_link(&link); 225df330515SLaurent Pinchart ret = -EINVAL; 226df330515SLaurent Pinchart break; 227df330515SLaurent Pinchart } 228df330515SLaurent Pinchart 229df330515SLaurent Pinchart dev_dbg(xdev->dev, "creating link for DMA engine %s\n", 230df330515SLaurent Pinchart dma->video.name); 231df330515SLaurent Pinchart 232df330515SLaurent Pinchart /* Find the remote entity. */ 233859969b3SSakari Ailus ent = xvip_graph_find_entity(xdev, 234859969b3SSakari Ailus to_of_node(link.remote_node)); 235df330515SLaurent Pinchart if (ent == NULL) { 23668d9c47bSRob Herring dev_err(xdev->dev, "no entity found for %pOF\n", 23768d9c47bSRob Herring to_of_node(link.remote_node)); 238859969b3SSakari Ailus v4l2_fwnode_put_link(&link); 239df330515SLaurent Pinchart ret = -ENODEV; 240df330515SLaurent Pinchart break; 241df330515SLaurent Pinchart } 242df330515SLaurent Pinchart 243df330515SLaurent Pinchart if (link.remote_port >= ent->entity->num_pads) { 24468d9c47bSRob Herring dev_err(xdev->dev, "invalid port number %u on %pOF\n", 245859969b3SSakari Ailus link.remote_port, 24668d9c47bSRob Herring to_of_node(link.remote_node)); 247859969b3SSakari Ailus v4l2_fwnode_put_link(&link); 248df330515SLaurent Pinchart ret = -EINVAL; 249df330515SLaurent Pinchart break; 250df330515SLaurent Pinchart } 251df330515SLaurent Pinchart 252df330515SLaurent Pinchart if (dma->pad.flags & MEDIA_PAD_FL_SOURCE) { 253df330515SLaurent Pinchart source = &dma->video.entity; 254df330515SLaurent Pinchart source_pad = &dma->pad; 255df330515SLaurent Pinchart sink = ent->entity; 256df330515SLaurent Pinchart sink_pad = &sink->pads[link.remote_port]; 257df330515SLaurent Pinchart } else { 258df330515SLaurent Pinchart source = ent->entity; 259df330515SLaurent Pinchart source_pad = &source->pads[link.remote_port]; 260df330515SLaurent Pinchart sink = &dma->video.entity; 261df330515SLaurent Pinchart sink_pad = &dma->pad; 262df330515SLaurent Pinchart } 263df330515SLaurent Pinchart 264859969b3SSakari Ailus v4l2_fwnode_put_link(&link); 265df330515SLaurent Pinchart 266df330515SLaurent Pinchart /* Create the media link. */ 267df330515SLaurent Pinchart dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", 268df330515SLaurent Pinchart source->name, source_pad->index, 269df330515SLaurent Pinchart sink->name, sink_pad->index); 270df330515SLaurent Pinchart 2718df00a15SMauro Carvalho Chehab ret = media_create_pad_link(source, source_pad->index, 272df330515SLaurent Pinchart sink, sink_pad->index, 273df330515SLaurent Pinchart link_flags); 274df330515SLaurent Pinchart if (ret < 0) { 275df330515SLaurent Pinchart dev_err(xdev->dev, 276df330515SLaurent Pinchart "failed to create %s:%u -> %s:%u link\n", 277df330515SLaurent Pinchart source->name, source_pad->index, 278df330515SLaurent Pinchart sink->name, sink_pad->index); 279df330515SLaurent Pinchart break; 280df330515SLaurent Pinchart } 281df330515SLaurent Pinchart } 282df330515SLaurent Pinchart 283df330515SLaurent Pinchart of_node_put(ep); 284df330515SLaurent Pinchart return ret; 285df330515SLaurent Pinchart } 286df330515SLaurent Pinchart 287df330515SLaurent Pinchart static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier) 288df330515SLaurent Pinchart { 289df330515SLaurent Pinchart struct xvip_composite_device *xdev = 290df330515SLaurent Pinchart container_of(notifier, struct xvip_composite_device, notifier); 291df330515SLaurent Pinchart struct xvip_graph_entity *entity; 292df330515SLaurent Pinchart int ret; 293df330515SLaurent Pinchart 294df330515SLaurent Pinchart dev_dbg(xdev->dev, "notify complete, all subdevs registered\n"); 295df330515SLaurent Pinchart 296df330515SLaurent Pinchart /* Create links for every entity. */ 297df330515SLaurent Pinchart list_for_each_entry(entity, &xdev->entities, list) { 298df330515SLaurent Pinchart ret = xvip_graph_build_one(xdev, entity); 299df330515SLaurent Pinchart if (ret < 0) 300df330515SLaurent Pinchart return ret; 301df330515SLaurent Pinchart } 302df330515SLaurent Pinchart 303df330515SLaurent Pinchart /* Create links for DMA channels. */ 304df330515SLaurent Pinchart ret = xvip_graph_build_dma(xdev); 305df330515SLaurent Pinchart if (ret < 0) 306df330515SLaurent Pinchart return ret; 307df330515SLaurent Pinchart 308df330515SLaurent Pinchart ret = v4l2_device_register_subdev_nodes(&xdev->v4l2_dev); 309df330515SLaurent Pinchart if (ret < 0) 310df330515SLaurent Pinchart dev_err(xdev->dev, "failed to register subdev nodes\n"); 311df330515SLaurent Pinchart 3129832e155SJavier Martinez Canillas return media_device_register(&xdev->media_dev); 313df330515SLaurent Pinchart } 314df330515SLaurent Pinchart 315df330515SLaurent Pinchart static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier, 316df330515SLaurent Pinchart struct v4l2_subdev *subdev, 317df330515SLaurent Pinchart struct v4l2_async_subdev *asd) 318df330515SLaurent Pinchart { 319df330515SLaurent Pinchart struct xvip_composite_device *xdev = 320df330515SLaurent Pinchart container_of(notifier, struct xvip_composite_device, notifier); 321df330515SLaurent Pinchart struct xvip_graph_entity *entity; 322df330515SLaurent Pinchart 323df330515SLaurent Pinchart /* Locate the entity corresponding to the bound subdev and store the 324df330515SLaurent Pinchart * subdev pointer. 325df330515SLaurent Pinchart */ 326df330515SLaurent Pinchart list_for_each_entry(entity, &xdev->entities, list) { 327df330515SLaurent Pinchart if (entity->node != subdev->dev->of_node) 328df330515SLaurent Pinchart continue; 329df330515SLaurent Pinchart 330df330515SLaurent Pinchart if (entity->subdev) { 33168d9c47bSRob Herring dev_err(xdev->dev, "duplicate subdev for node %pOF\n", 33268d9c47bSRob Herring entity->node); 333df330515SLaurent Pinchart return -EINVAL; 334df330515SLaurent Pinchart } 335df330515SLaurent Pinchart 336df330515SLaurent Pinchart dev_dbg(xdev->dev, "subdev %s bound\n", subdev->name); 337df330515SLaurent Pinchart entity->entity = &subdev->entity; 338df330515SLaurent Pinchart entity->subdev = subdev; 339df330515SLaurent Pinchart return 0; 340df330515SLaurent Pinchart } 341df330515SLaurent Pinchart 342df330515SLaurent Pinchart dev_err(xdev->dev, "no entity for subdev %s\n", subdev->name); 343df330515SLaurent Pinchart return -EINVAL; 344df330515SLaurent Pinchart } 345df330515SLaurent Pinchart 346b6ee3f0dSLaurent Pinchart static const struct v4l2_async_notifier_operations xvip_graph_notify_ops = { 347b6ee3f0dSLaurent Pinchart .bound = xvip_graph_notify_bound, 348b6ee3f0dSLaurent Pinchart .complete = xvip_graph_notify_complete, 349b6ee3f0dSLaurent Pinchart }; 350b6ee3f0dSLaurent Pinchart 351df330515SLaurent Pinchart static int xvip_graph_parse_one(struct xvip_composite_device *xdev, 352df330515SLaurent Pinchart struct device_node *node) 353df330515SLaurent Pinchart { 354df330515SLaurent Pinchart struct xvip_graph_entity *entity; 355df330515SLaurent Pinchart struct device_node *remote; 356df330515SLaurent Pinchart struct device_node *ep = NULL; 357df330515SLaurent Pinchart int ret = 0; 358df330515SLaurent Pinchart 35968d9c47bSRob Herring dev_dbg(xdev->dev, "parsing node %pOF\n", node); 360df330515SLaurent Pinchart 361df330515SLaurent Pinchart while (1) { 362c64ee347SFranck Jullien ep = of_graph_get_next_endpoint(node, ep); 363c64ee347SFranck Jullien if (ep == NULL) 364df330515SLaurent Pinchart break; 365df330515SLaurent Pinchart 36668d9c47bSRob Herring dev_dbg(xdev->dev, "handling endpoint %pOF\n", ep); 367df330515SLaurent Pinchart 368df330515SLaurent Pinchart remote = of_graph_get_remote_port_parent(ep); 369df330515SLaurent Pinchart if (remote == NULL) { 370df330515SLaurent Pinchart ret = -EINVAL; 371df330515SLaurent Pinchart break; 372df330515SLaurent Pinchart } 373df330515SLaurent Pinchart 374df330515SLaurent Pinchart /* Skip entities that we have already processed. */ 375df330515SLaurent Pinchart if (remote == xdev->dev->of_node || 376df330515SLaurent Pinchart xvip_graph_find_entity(xdev, remote)) { 377df330515SLaurent Pinchart of_node_put(remote); 378df330515SLaurent Pinchart continue; 379df330515SLaurent Pinchart } 380df330515SLaurent Pinchart 381df330515SLaurent Pinchart entity = devm_kzalloc(xdev->dev, sizeof(*entity), GFP_KERNEL); 382df330515SLaurent Pinchart if (entity == NULL) { 383df330515SLaurent Pinchart of_node_put(remote); 384df330515SLaurent Pinchart ret = -ENOMEM; 385df330515SLaurent Pinchart break; 386df330515SLaurent Pinchart } 387df330515SLaurent Pinchart 388df330515SLaurent Pinchart entity->node = remote; 389859969b3SSakari Ailus entity->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; 390859969b3SSakari Ailus entity->asd.match.fwnode.fwnode = of_fwnode_handle(remote); 391df330515SLaurent Pinchart list_add_tail(&entity->list, &xdev->entities); 392df330515SLaurent Pinchart xdev->num_subdevs++; 393df330515SLaurent Pinchart } 394df330515SLaurent Pinchart 395df330515SLaurent Pinchart of_node_put(ep); 396df330515SLaurent Pinchart return ret; 397df330515SLaurent Pinchart } 398df330515SLaurent Pinchart 399df330515SLaurent Pinchart static int xvip_graph_parse(struct xvip_composite_device *xdev) 400df330515SLaurent Pinchart { 401df330515SLaurent Pinchart struct xvip_graph_entity *entity; 402df330515SLaurent Pinchart int ret; 403df330515SLaurent Pinchart 404df330515SLaurent Pinchart /* 405df330515SLaurent Pinchart * Walk the links to parse the full graph. Start by parsing the 406df330515SLaurent Pinchart * composite node and then parse entities in turn. The list_for_each 407df330515SLaurent Pinchart * loop will handle entities added at the end of the list while walking 408df330515SLaurent Pinchart * the links. 409df330515SLaurent Pinchart */ 410df330515SLaurent Pinchart ret = xvip_graph_parse_one(xdev, xdev->dev->of_node); 411df330515SLaurent Pinchart if (ret < 0) 412df330515SLaurent Pinchart return 0; 413df330515SLaurent Pinchart 414df330515SLaurent Pinchart list_for_each_entry(entity, &xdev->entities, list) { 415df330515SLaurent Pinchart ret = xvip_graph_parse_one(xdev, entity->node); 416df330515SLaurent Pinchart if (ret < 0) 417df330515SLaurent Pinchart break; 418df330515SLaurent Pinchart } 419df330515SLaurent Pinchart 420df330515SLaurent Pinchart return ret; 421df330515SLaurent Pinchart } 422df330515SLaurent Pinchart 423df330515SLaurent Pinchart static int xvip_graph_dma_init_one(struct xvip_composite_device *xdev, 424df330515SLaurent Pinchart struct device_node *node) 425df330515SLaurent Pinchart { 426df330515SLaurent Pinchart struct xvip_dma *dma; 427df330515SLaurent Pinchart enum v4l2_buf_type type; 428df330515SLaurent Pinchart const char *direction; 429df330515SLaurent Pinchart unsigned int index; 430df330515SLaurent Pinchart int ret; 431df330515SLaurent Pinchart 432df330515SLaurent Pinchart ret = of_property_read_string(node, "direction", &direction); 433df330515SLaurent Pinchart if (ret < 0) 434df330515SLaurent Pinchart return ret; 435df330515SLaurent Pinchart 436df330515SLaurent Pinchart if (strcmp(direction, "input") == 0) 437df330515SLaurent Pinchart type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 438df330515SLaurent Pinchart else if (strcmp(direction, "output") == 0) 439df330515SLaurent Pinchart type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 440df330515SLaurent Pinchart else 441df330515SLaurent Pinchart return -EINVAL; 442df330515SLaurent Pinchart 443df330515SLaurent Pinchart of_property_read_u32(node, "reg", &index); 444df330515SLaurent Pinchart 445df330515SLaurent Pinchart dma = devm_kzalloc(xdev->dev, sizeof(*dma), GFP_KERNEL); 446df330515SLaurent Pinchart if (dma == NULL) 447df330515SLaurent Pinchart return -ENOMEM; 448df330515SLaurent Pinchart 449df330515SLaurent Pinchart ret = xvip_dma_init(xdev, dma, type, index); 450df330515SLaurent Pinchart if (ret < 0) { 45168d9c47bSRob Herring dev_err(xdev->dev, "%pOF initialization failed\n", node); 452df330515SLaurent Pinchart return ret; 453df330515SLaurent Pinchart } 454df330515SLaurent Pinchart 455df330515SLaurent Pinchart list_add_tail(&dma->list, &xdev->dmas); 456df330515SLaurent Pinchart 457df330515SLaurent Pinchart xdev->v4l2_caps |= type == V4L2_BUF_TYPE_VIDEO_CAPTURE 458df330515SLaurent Pinchart ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_VIDEO_OUTPUT; 459df330515SLaurent Pinchart 460df330515SLaurent Pinchart return 0; 461df330515SLaurent Pinchart } 462df330515SLaurent Pinchart 463df330515SLaurent Pinchart static int xvip_graph_dma_init(struct xvip_composite_device *xdev) 464df330515SLaurent Pinchart { 465df330515SLaurent Pinchart struct device_node *ports; 466df330515SLaurent Pinchart struct device_node *port; 467df330515SLaurent Pinchart int ret; 468df330515SLaurent Pinchart 469df330515SLaurent Pinchart ports = of_get_child_by_name(xdev->dev->of_node, "ports"); 470df330515SLaurent Pinchart if (ports == NULL) { 471df330515SLaurent Pinchart dev_err(xdev->dev, "ports node not present\n"); 472df330515SLaurent Pinchart return -EINVAL; 473df330515SLaurent Pinchart } 474df330515SLaurent Pinchart 475df330515SLaurent Pinchart for_each_child_of_node(ports, port) { 476df330515SLaurent Pinchart ret = xvip_graph_dma_init_one(xdev, port); 477cea590d4SJulia Lawall if (ret < 0) { 478cea590d4SJulia Lawall of_node_put(port); 479df330515SLaurent Pinchart return ret; 480df330515SLaurent Pinchart } 481cea590d4SJulia Lawall } 482df330515SLaurent Pinchart 483df330515SLaurent Pinchart return 0; 484df330515SLaurent Pinchart } 485df330515SLaurent Pinchart 486df330515SLaurent Pinchart static void xvip_graph_cleanup(struct xvip_composite_device *xdev) 487df330515SLaurent Pinchart { 488df330515SLaurent Pinchart struct xvip_graph_entity *entityp; 489df330515SLaurent Pinchart struct xvip_graph_entity *entity; 490df330515SLaurent Pinchart struct xvip_dma *dmap; 491df330515SLaurent Pinchart struct xvip_dma *dma; 492df330515SLaurent Pinchart 493df330515SLaurent Pinchart v4l2_async_notifier_unregister(&xdev->notifier); 494df330515SLaurent Pinchart 495df330515SLaurent Pinchart list_for_each_entry_safe(entity, entityp, &xdev->entities, list) { 496df330515SLaurent Pinchart of_node_put(entity->node); 497df330515SLaurent Pinchart list_del(&entity->list); 498df330515SLaurent Pinchart } 499df330515SLaurent Pinchart 500df330515SLaurent Pinchart list_for_each_entry_safe(dma, dmap, &xdev->dmas, list) { 501df330515SLaurent Pinchart xvip_dma_cleanup(dma); 502df330515SLaurent Pinchart list_del(&dma->list); 503df330515SLaurent Pinchart } 504df330515SLaurent Pinchart } 505df330515SLaurent Pinchart 506df330515SLaurent Pinchart static int xvip_graph_init(struct xvip_composite_device *xdev) 507df330515SLaurent Pinchart { 508df330515SLaurent Pinchart struct xvip_graph_entity *entity; 509df330515SLaurent Pinchart struct v4l2_async_subdev **subdevs = NULL; 510df330515SLaurent Pinchart unsigned int num_subdevs; 511df330515SLaurent Pinchart unsigned int i; 512df330515SLaurent Pinchart int ret; 513df330515SLaurent Pinchart 514df330515SLaurent Pinchart /* Init the DMA channels. */ 515df330515SLaurent Pinchart ret = xvip_graph_dma_init(xdev); 516df330515SLaurent Pinchart if (ret < 0) { 517df330515SLaurent Pinchart dev_err(xdev->dev, "DMA initialization failed\n"); 518df330515SLaurent Pinchart goto done; 519df330515SLaurent Pinchart } 520df330515SLaurent Pinchart 521df330515SLaurent Pinchart /* Parse the graph to extract a list of subdevice DT nodes. */ 522df330515SLaurent Pinchart ret = xvip_graph_parse(xdev); 523df330515SLaurent Pinchart if (ret < 0) { 524df330515SLaurent Pinchart dev_err(xdev->dev, "graph parsing failed\n"); 525df330515SLaurent Pinchart goto done; 526df330515SLaurent Pinchart } 527df330515SLaurent Pinchart 528df330515SLaurent Pinchart if (!xdev->num_subdevs) { 529df330515SLaurent Pinchart dev_err(xdev->dev, "no subdev found in graph\n"); 530df330515SLaurent Pinchart goto done; 531df330515SLaurent Pinchart } 532df330515SLaurent Pinchart 533df330515SLaurent Pinchart /* Register the subdevices notifier. */ 534df330515SLaurent Pinchart num_subdevs = xdev->num_subdevs; 535df330515SLaurent Pinchart subdevs = devm_kzalloc(xdev->dev, sizeof(*subdevs) * num_subdevs, 536df330515SLaurent Pinchart GFP_KERNEL); 537df330515SLaurent Pinchart if (subdevs == NULL) { 538df330515SLaurent Pinchart ret = -ENOMEM; 539df330515SLaurent Pinchart goto done; 540df330515SLaurent Pinchart } 541df330515SLaurent Pinchart 542df330515SLaurent Pinchart i = 0; 543df330515SLaurent Pinchart list_for_each_entry(entity, &xdev->entities, list) 544df330515SLaurent Pinchart subdevs[i++] = &entity->asd; 545df330515SLaurent Pinchart 546df330515SLaurent Pinchart xdev->notifier.subdevs = subdevs; 547df330515SLaurent Pinchart xdev->notifier.num_subdevs = num_subdevs; 548b6ee3f0dSLaurent Pinchart xdev->notifier.ops = &xvip_graph_notify_ops; 549df330515SLaurent Pinchart 550df330515SLaurent Pinchart ret = v4l2_async_notifier_register(&xdev->v4l2_dev, &xdev->notifier); 551df330515SLaurent Pinchart if (ret < 0) { 552df330515SLaurent Pinchart dev_err(xdev->dev, "notifier registration failed\n"); 553df330515SLaurent Pinchart goto done; 554df330515SLaurent Pinchart } 555df330515SLaurent Pinchart 556df330515SLaurent Pinchart ret = 0; 557df330515SLaurent Pinchart 558df330515SLaurent Pinchart done: 559df330515SLaurent Pinchart if (ret < 0) 560df330515SLaurent Pinchart xvip_graph_cleanup(xdev); 561df330515SLaurent Pinchart 562df330515SLaurent Pinchart return ret; 563df330515SLaurent Pinchart } 564df330515SLaurent Pinchart 565df330515SLaurent Pinchart /* ----------------------------------------------------------------------------- 566df330515SLaurent Pinchart * Media Controller and V4L2 567df330515SLaurent Pinchart */ 568df330515SLaurent Pinchart 569df330515SLaurent Pinchart static void xvip_composite_v4l2_cleanup(struct xvip_composite_device *xdev) 570df330515SLaurent Pinchart { 571df330515SLaurent Pinchart v4l2_device_unregister(&xdev->v4l2_dev); 572df330515SLaurent Pinchart media_device_unregister(&xdev->media_dev); 5739832e155SJavier Martinez Canillas media_device_cleanup(&xdev->media_dev); 574df330515SLaurent Pinchart } 575df330515SLaurent Pinchart 576df330515SLaurent Pinchart static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev) 577df330515SLaurent Pinchart { 578df330515SLaurent Pinchart int ret; 579df330515SLaurent Pinchart 580df330515SLaurent Pinchart xdev->media_dev.dev = xdev->dev; 581df330515SLaurent Pinchart strlcpy(xdev->media_dev.model, "Xilinx Video Composite Device", 582df330515SLaurent Pinchart sizeof(xdev->media_dev.model)); 583df330515SLaurent Pinchart xdev->media_dev.hw_revision = 0; 584df330515SLaurent Pinchart 5859832e155SJavier Martinez Canillas media_device_init(&xdev->media_dev); 586df330515SLaurent Pinchart 587df330515SLaurent Pinchart xdev->v4l2_dev.mdev = &xdev->media_dev; 588df330515SLaurent Pinchart ret = v4l2_device_register(xdev->dev, &xdev->v4l2_dev); 589df330515SLaurent Pinchart if (ret < 0) { 590df330515SLaurent Pinchart dev_err(xdev->dev, "V4L2 device registration failed (%d)\n", 591df330515SLaurent Pinchart ret); 5929832e155SJavier Martinez Canillas media_device_cleanup(&xdev->media_dev); 593df330515SLaurent Pinchart return ret; 594df330515SLaurent Pinchart } 595df330515SLaurent Pinchart 596df330515SLaurent Pinchart return 0; 597df330515SLaurent Pinchart } 598df330515SLaurent Pinchart 599df330515SLaurent Pinchart /* ----------------------------------------------------------------------------- 600df330515SLaurent Pinchart * Platform Device Driver 601df330515SLaurent Pinchart */ 602df330515SLaurent Pinchart 603df330515SLaurent Pinchart static int xvip_composite_probe(struct platform_device *pdev) 604df330515SLaurent Pinchart { 605df330515SLaurent Pinchart struct xvip_composite_device *xdev; 606df330515SLaurent Pinchart int ret; 607df330515SLaurent Pinchart 608df330515SLaurent Pinchart xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL); 609df330515SLaurent Pinchart if (!xdev) 610df330515SLaurent Pinchart return -ENOMEM; 611df330515SLaurent Pinchart 612df330515SLaurent Pinchart xdev->dev = &pdev->dev; 613df330515SLaurent Pinchart INIT_LIST_HEAD(&xdev->entities); 614df330515SLaurent Pinchart INIT_LIST_HEAD(&xdev->dmas); 615df330515SLaurent Pinchart 616df330515SLaurent Pinchart ret = xvip_composite_v4l2_init(xdev); 617df330515SLaurent Pinchart if (ret < 0) 618df330515SLaurent Pinchart return ret; 619df330515SLaurent Pinchart 620df330515SLaurent Pinchart ret = xvip_graph_init(xdev); 621df330515SLaurent Pinchart if (ret < 0) 622df330515SLaurent Pinchart goto error; 623df330515SLaurent Pinchart 624df330515SLaurent Pinchart platform_set_drvdata(pdev, xdev); 625df330515SLaurent Pinchart 626df330515SLaurent Pinchart dev_info(xdev->dev, "device registered\n"); 627df330515SLaurent Pinchart 628df330515SLaurent Pinchart return 0; 629df330515SLaurent Pinchart 630df330515SLaurent Pinchart error: 631df330515SLaurent Pinchart xvip_composite_v4l2_cleanup(xdev); 632df330515SLaurent Pinchart return ret; 633df330515SLaurent Pinchart } 634df330515SLaurent Pinchart 635df330515SLaurent Pinchart static int xvip_composite_remove(struct platform_device *pdev) 636df330515SLaurent Pinchart { 637df330515SLaurent Pinchart struct xvip_composite_device *xdev = platform_get_drvdata(pdev); 638df330515SLaurent Pinchart 639df330515SLaurent Pinchart xvip_graph_cleanup(xdev); 640df330515SLaurent Pinchart xvip_composite_v4l2_cleanup(xdev); 641df330515SLaurent Pinchart 642df330515SLaurent Pinchart return 0; 643df330515SLaurent Pinchart } 644df330515SLaurent Pinchart 645df330515SLaurent Pinchart static const struct of_device_id xvip_composite_of_id_table[] = { 646df330515SLaurent Pinchart { .compatible = "xlnx,video" }, 647df330515SLaurent Pinchart { } 648df330515SLaurent Pinchart }; 649df330515SLaurent Pinchart MODULE_DEVICE_TABLE(of, xvip_composite_of_id_table); 650df330515SLaurent Pinchart 651df330515SLaurent Pinchart static struct platform_driver xvip_composite_driver = { 652df330515SLaurent Pinchart .driver = { 653df330515SLaurent Pinchart .name = "xilinx-video", 654df330515SLaurent Pinchart .of_match_table = xvip_composite_of_id_table, 655df330515SLaurent Pinchart }, 656df330515SLaurent Pinchart .probe = xvip_composite_probe, 657df330515SLaurent Pinchart .remove = xvip_composite_remove, 658df330515SLaurent Pinchart }; 659df330515SLaurent Pinchart 660df330515SLaurent Pinchart module_platform_driver(xvip_composite_driver); 661df330515SLaurent Pinchart 662df330515SLaurent Pinchart MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 663df330515SLaurent Pinchart MODULE_DESCRIPTION("Xilinx Video IP Composite Driver"); 664df330515SLaurent Pinchart MODULE_LICENSE("GPL v2"); 665