1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2020 NVIDIA CORPORATION.  All rights reserved.
4  */
5 
6 #include <linux/host1x.h>
7 #include <linux/module.h>
8 #include <linux/platform_device.h>
9 
10 #include <media/v4l2-event.h>
11 
12 #include "video.h"
13 
tegra_v4l2_dev_release(struct v4l2_device * v4l2_dev)14 static void tegra_v4l2_dev_release(struct v4l2_device *v4l2_dev)
15 {
16 	struct tegra_video_device *vid;
17 
18 	vid = container_of(v4l2_dev, struct tegra_video_device, v4l2_dev);
19 
20 	/* cleanup channels here as all video device nodes are released */
21 	tegra_channels_cleanup(vid->vi);
22 
23 	v4l2_device_unregister(v4l2_dev);
24 	media_device_unregister(&vid->media_dev);
25 	media_device_cleanup(&vid->media_dev);
26 	kfree(vid);
27 }
28 
tegra_v4l2_dev_notify(struct v4l2_subdev * sd,unsigned int notification,void * arg)29 static void tegra_v4l2_dev_notify(struct v4l2_subdev *sd,
30 				  unsigned int notification, void *arg)
31 {
32 	struct tegra_vi_channel *chan;
33 	const struct v4l2_event *ev = arg;
34 
35 	if (notification != V4L2_DEVICE_NOTIFY_EVENT)
36 		return;
37 
38 	chan = v4l2_get_subdev_hostdata(sd);
39 	v4l2_event_queue(&chan->video, arg);
40 	if (ev->type == V4L2_EVENT_SOURCE_CHANGE && vb2_is_streaming(&chan->queue))
41 		vb2_queue_error(&chan->queue);
42 }
43 
host1x_video_probe(struct host1x_device * dev)44 static int host1x_video_probe(struct host1x_device *dev)
45 {
46 	struct tegra_video_device *vid;
47 	int ret;
48 
49 	vid = kzalloc(sizeof(*vid), GFP_KERNEL);
50 	if (!vid)
51 		return -ENOMEM;
52 
53 	dev_set_drvdata(&dev->dev, vid);
54 
55 	vid->media_dev.dev = &dev->dev;
56 	strscpy(vid->media_dev.model, "NVIDIA Tegra Video Input Device",
57 		sizeof(vid->media_dev.model));
58 
59 	media_device_init(&vid->media_dev);
60 	ret = media_device_register(&vid->media_dev);
61 	if (ret < 0) {
62 		dev_err(&dev->dev,
63 			"failed to register media device: %d\n", ret);
64 		goto cleanup;
65 	}
66 
67 	vid->v4l2_dev.mdev = &vid->media_dev;
68 	vid->v4l2_dev.release = tegra_v4l2_dev_release;
69 	vid->v4l2_dev.notify = tegra_v4l2_dev_notify;
70 	ret = v4l2_device_register(&dev->dev, &vid->v4l2_dev);
71 	if (ret < 0) {
72 		dev_err(&dev->dev,
73 			"V4L2 device registration failed: %d\n", ret);
74 		goto unregister_media;
75 	}
76 
77 	ret = host1x_device_init(dev);
78 	if (ret < 0)
79 		goto unregister_v4l2;
80 
81 	if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) {
82 		/*
83 		 * Both vi and csi channels are available now.
84 		 * Register v4l2 nodes and create media links for TPG.
85 		 */
86 		ret = tegra_v4l2_nodes_setup_tpg(vid);
87 		if (ret < 0) {
88 			dev_err(&dev->dev,
89 				"failed to setup tpg graph: %d\n", ret);
90 			goto device_exit;
91 		}
92 	}
93 
94 	return 0;
95 
96 device_exit:
97 	host1x_device_exit(dev);
98 	/* vi exit ops does not clean channels, so clean them here */
99 	tegra_channels_cleanup(vid->vi);
100 unregister_v4l2:
101 	v4l2_device_unregister(&vid->v4l2_dev);
102 unregister_media:
103 	media_device_unregister(&vid->media_dev);
104 cleanup:
105 	media_device_cleanup(&vid->media_dev);
106 	kfree(vid);
107 	return ret;
108 }
109 
host1x_video_remove(struct host1x_device * dev)110 static int host1x_video_remove(struct host1x_device *dev)
111 {
112 	struct tegra_video_device *vid = dev_get_drvdata(&dev->dev);
113 
114 	if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
115 		tegra_v4l2_nodes_cleanup_tpg(vid);
116 
117 	host1x_device_exit(dev);
118 
119 	/* This calls v4l2_dev release callback on last reference */
120 	v4l2_device_put(&vid->v4l2_dev);
121 
122 	return 0;
123 }
124 
125 static const struct of_device_id host1x_video_subdevs[] = {
126 #if defined(CONFIG_ARCH_TEGRA_2x_SOC)
127 	{ .compatible = "nvidia,tegra20-vip", },
128 	{ .compatible = "nvidia,tegra20-vi", },
129 #endif
130 #if defined(CONFIG_ARCH_TEGRA_210_SOC)
131 	{ .compatible = "nvidia,tegra210-csi", },
132 	{ .compatible = "nvidia,tegra210-vi", },
133 #endif
134 	{ }
135 };
136 
137 static struct host1x_driver host1x_video_driver = {
138 	.driver = {
139 		.name = "tegra-video",
140 	},
141 	.probe = host1x_video_probe,
142 	.remove = host1x_video_remove,
143 	.subdevs = host1x_video_subdevs,
144 };
145 
146 static struct platform_driver * const drivers[] = {
147 	&tegra_csi_driver,
148 	&tegra_vip_driver,
149 	&tegra_vi_driver,
150 };
151 
host1x_video_init(void)152 static int __init host1x_video_init(void)
153 {
154 	int err;
155 
156 	err = host1x_driver_register(&host1x_video_driver);
157 	if (err < 0)
158 		return err;
159 
160 	err = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
161 	if (err < 0)
162 		goto unregister_host1x;
163 
164 	return 0;
165 
166 unregister_host1x:
167 	host1x_driver_unregister(&host1x_video_driver);
168 	return err;
169 }
170 module_init(host1x_video_init);
171 
host1x_video_exit(void)172 static void __exit host1x_video_exit(void)
173 {
174 	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
175 	host1x_driver_unregister(&host1x_video_driver);
176 }
177 module_exit(host1x_video_exit);
178 
179 MODULE_AUTHOR("Sowjanya Komatineni <skomatineni@nvidia.com>");
180 MODULE_DESCRIPTION("NVIDIA Tegra Host1x Video driver");
181 MODULE_LICENSE("GPL v2");
182