Commit | Line | Data |
---|---|---|
3d8a97ea SK |
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 | ||
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 | ||
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 | ||
341187bf SK |
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 | } | |
3d8a97ea SK |
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 | ||
92 | static int host1x_video_remove(struct host1x_device *dev) | |
93 | { | |
94 | struct tegra_video_device *vid = dev_get_drvdata(&dev->dev); | |
95 | ||
341187bf SK |
96 | if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) |
97 | tegra_v4l2_nodes_cleanup_tpg(vid); | |
3d8a97ea SK |
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 | ||
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 | ||
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"); |