GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / staging / media / tegra-video / video.c
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
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
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
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
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_210_SOC)
127         { .compatible = "nvidia,tegra210-csi", },
128         { .compatible = "nvidia,tegra210-vi", },
129 #endif
130         { }
131 };
132
133 static struct host1x_driver host1x_video_driver = {
134         .driver = {
135                 .name = "tegra-video",
136         },
137         .probe = host1x_video_probe,
138         .remove = host1x_video_remove,
139         .subdevs = host1x_video_subdevs,
140 };
141
142 static struct platform_driver * const drivers[] = {
143         &tegra_csi_driver,
144         &tegra_vi_driver,
145 };
146
147 static int __init host1x_video_init(void)
148 {
149         int err;
150
151         err = host1x_driver_register(&host1x_video_driver);
152         if (err < 0)
153                 return err;
154
155         err = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
156         if (err < 0)
157                 goto unregister_host1x;
158
159         return 0;
160
161 unregister_host1x:
162         host1x_driver_unregister(&host1x_video_driver);
163         return err;
164 }
165 module_init(host1x_video_init);
166
167 static void __exit host1x_video_exit(void)
168 {
169         platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
170         host1x_driver_unregister(&host1x_video_driver);
171 }
172 module_exit(host1x_video_exit);
173
174 MODULE_AUTHOR("Sowjanya Komatineni <skomatineni@nvidia.com>");
175 MODULE_DESCRIPTION("NVIDIA Tegra Host1x Video driver");
176 MODULE_LICENSE("GPL v2");