brintos

brintos / linux-shallow public Read only

0
0
Text · 14.8 KiB · bfe48cc Raw
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