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