File Index
Files and directories
Sign in
FILE

xilinx-vipp.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/xilinx/xilinx-vipp.c C
  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_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 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_connection *asd;
 58	struct list_head *lists[] = {
 59		&xdev->notifier.done_list,
 60		&xdev->notifier.waiting_list
 61	};
 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 of
107		 * 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 the
390	 * composite node and then parse entities in turn. The list_for_each
391	 * loop will handle entities added at the end of the list while walking
392	 * 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	else
428		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_CAPTURE
445			 ? 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	int ret = 0;
454
455	ports = of_get_child_by_name(xdev->dev->of_node, "ports");
456	if (ports == NULL) {
457		dev_err(xdev->dev, "ports node not present\n");
458		return -EINVAL;
459	}
460
461	for_each_child_of_node_scoped(ports, port) {
462		ret = xvip_graph_dma_init_one(xdev, port);
463		if (ret)
464			break;
465	}
466
467	of_node_put(ports);
468	return ret;
469}
470
471static void xvip_graph_cleanup(struct xvip_composite_device *xdev)
472{
473	struct xvip_dma *dmap;
474	struct xvip_dma *dma;
475
476	v4l2_async_nf_unregister(&xdev->notifier);
477	v4l2_async_nf_cleanup(&xdev->notifier);
478
479	list_for_each_entry_safe(dma, dmap, &xdev->dmas, list) {
480		xvip_dma_cleanup(dma);
481		list_del(&dma->list);
482	}
483}
484
485static int xvip_graph_init(struct xvip_composite_device *xdev)
486{
487	int ret;
488
489	/* Init the DMA channels. */
490	ret = xvip_graph_dma_init(xdev);
491	if (ret < 0) {
492		dev_err(xdev->dev, "DMA initialization failed\n");
493		goto done;
494	}
495
496	v4l2_async_nf_init(&xdev->notifier, &xdev->v4l2_dev);
497
498	/* Parse the graph to extract a list of subdevice DT nodes. */
499	ret = xvip_graph_parse(xdev);
500	if (ret < 0) {
501		dev_err(xdev->dev, "graph parsing failed\n");
502		goto done;
503	}
504
505	if (list_empty(&xdev->notifier.waiting_list)) {
506		dev_err(xdev->dev, "no subdev found in graph\n");
507		ret = -ENOENT;
508		goto done;
509	}
510
511	/* Register the subdevices notifier. */
512	xdev->notifier.ops = &xvip_graph_notify_ops;
513
514	ret = v4l2_async_nf_register(&xdev->notifier);
515	if (ret < 0) {
516		dev_err(xdev->dev, "notifier registration failed\n");
517		goto done;
518	}
519
520	ret = 0;
521
522done:
523	if (ret < 0)
524		xvip_graph_cleanup(xdev);
525
526	return ret;
527}
528
529/* -----------------------------------------------------------------------------
530 * Media Controller and V4L2
531 */
532
533static void xvip_composite_v4l2_cleanup(struct xvip_composite_device *xdev)
534{
535	v4l2_device_unregister(&xdev->v4l2_dev);
536	media_device_unregister(&xdev->media_dev);
537	media_device_cleanup(&xdev->media_dev);
538}
539
540static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev)
541{
542	int ret;
543
544	xdev->media_dev.dev = xdev->dev;
545	strscpy(xdev->media_dev.model, "Xilinx Video Composite Device",
546		sizeof(xdev->media_dev.model));
547	xdev->media_dev.hw_revision = 0;
548
549	media_device_init(&xdev->media_dev);
550
551	xdev->v4l2_dev.mdev = &xdev->media_dev;
552	ret = v4l2_device_register(xdev->dev, &xdev->v4l2_dev);
553	if (ret < 0) {
554		dev_err(xdev->dev, "V4L2 device registration failed (%d)\n",
555			ret);
556		media_device_cleanup(&xdev->media_dev);
557		return ret;
558	}
559
560	return 0;
561}
562
563/* -----------------------------------------------------------------------------
564 * Platform Device Driver
565 */
566
567static int xvip_composite_probe(struct platform_device *pdev)
568{
569	struct xvip_composite_device *xdev;
570	int ret;
571
572	xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
573	if (!xdev)
574		return -ENOMEM;
575
576	xdev->dev = &pdev->dev;
577	INIT_LIST_HEAD(&xdev->dmas);
578
579	ret = xvip_composite_v4l2_init(xdev);
580	if (ret < 0)
581		return ret;
582
583	ret = xvip_graph_init(xdev);
584	if (ret < 0)
585		goto error;
586
587	platform_set_drvdata(pdev, xdev);
588
589	dev_info(xdev->dev, "device registered\n");
590
591	return 0;
592
593error:
594	xvip_composite_v4l2_cleanup(xdev);
595	return ret;
596}
597
598static void xvip_composite_remove(struct platform_device *pdev)
599{
600	struct xvip_composite_device *xdev = platform_get_drvdata(pdev);
601
602	xvip_graph_cleanup(xdev);
603	xvip_composite_v4l2_cleanup(xdev);
604}
605
606static const struct of_device_id xvip_composite_of_id_table[] = {
607	{ .compatible = "xlnx,video" },
608	{ }
609};
610MODULE_DEVICE_TABLE(of, xvip_composite_of_id_table);
611
612static struct platform_driver xvip_composite_driver = {
613	.driver = {
614		.name = "xilinx-video",
615		.of_match_table = xvip_composite_of_id_table,
616	},
617	.probe = xvip_composite_probe,
618	.remove = xvip_composite_remove,
619};
620
621module_platform_driver(xvip_composite_driver);
622
623MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
624MODULE_DESCRIPTION("Xilinx Video IP Composite Driver");
625MODULE_LICENSE("GPL v2");
~/ Read-only file preview.