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
14static 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(vi: vid->vi);
22
23 v4l2_device_unregister(v4l2_dev);
24 media_device_unregister(mdev: &vid->media_dev);
25 media_device_cleanup(mdev: &vid->media_dev);
26 kfree(objp: vid);
27}
28
29static 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(vdev: &chan->video, ev: arg);
40 if (ev->type == V4L2_EVENT_SOURCE_CHANGE && vb2_is_streaming(q: &chan->queue))
41 vb2_queue_error(q: &chan->queue);
42}
43
44static int host1x_video_probe(struct host1x_device *dev)
45{
46 struct tegra_video_device *vid;
47 int ret;
48
49 vid = kzalloc(size: sizeof(*vid), GFP_KERNEL);
50 if (!vid)
51 return -ENOMEM;
52
53 dev_set_drvdata(dev: &dev->dev, data: 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(mdev: &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->dev, v4l2_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(device: 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
96device_exit:
97 host1x_device_exit(device: dev);
98 /* vi exit ops does not clean channels, so clean them here */
99 tegra_channels_cleanup(vi: vid->vi);
100unregister_v4l2:
101 v4l2_device_unregister(v4l2_dev: &vid->v4l2_dev);
102unregister_media:
103 media_device_unregister(mdev: &vid->media_dev);
104cleanup:
105 media_device_cleanup(mdev: &vid->media_dev);
106 kfree(objp: vid);
107 return ret;
108}
109
110static int host1x_video_remove(struct host1x_device *dev)
111{
112 struct tegra_video_device *vid = dev_get_drvdata(dev: &dev->dev);
113
114 if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
115 tegra_v4l2_nodes_cleanup_tpg(vid);
116
117 host1x_device_exit(device: dev);
118
119 /* This calls v4l2_dev release callback on last reference */
120 v4l2_device_put(v4l2_dev: &vid->v4l2_dev);
121
122 return 0;
123}
124
125static 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
137static 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
146static struct platform_driver * const drivers[] = {
147 &tegra_csi_driver,
148 &tegra_vip_driver,
149 &tegra_vi_driver,
150};
151
152static 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
166unregister_host1x:
167 host1x_driver_unregister(driver: &host1x_video_driver);
168 return err;
169}
170module_init(host1x_video_init);
171
172static void __exit host1x_video_exit(void)
173{
174 platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
175 host1x_driver_unregister(driver: &host1x_video_driver);
176}
177module_exit(host1x_video_exit);
178
179MODULE_AUTHOR("Sowjanya Komatineni <skomatineni@nvidia.com>");
180MODULE_DESCRIPTION("NVIDIA Tegra Host1x Video driver");
181MODULE_LICENSE("GPL v2");
182

source code of linux/drivers/staging/media/tegra-video/video.c