cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

xilinx-vipp.c (15689B)


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