xref: /linux/drivers/accel/rocket/rocket_drv.c (revision ed98261b41687323ffa02ca20fef1e60b38fd1aa)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
3 
4 #include <drm/drm_accel.h>
5 #include <drm/drm_drv.h>
6 #include <drm/drm_gem.h>
7 #include <drm/drm_ioctl.h>
8 #include <linux/clk.h>
9 #include <linux/err.h>
10 #include <linux/iommu.h>
11 #include <linux/of.h>
12 #include <linux/platform_device.h>
13 #include <linux/pm_runtime.h>
14 
15 #include "rocket_drv.h"
16 
17 /*
18  * Facade device, used to expose a single DRM device to userspace, that
19  * schedules jobs to any RKNN cores in the system.
20  */
21 static struct platform_device *drm_dev;
22 static struct rocket_device *rdev;
23 
24 static void
25 rocket_iommu_domain_destroy(struct kref *kref)
26 {
27 	struct rocket_iommu_domain *domain = container_of(kref, struct rocket_iommu_domain, kref);
28 
29 	iommu_domain_free(domain->domain);
30 	domain->domain = NULL;
31 	kfree(domain);
32 }
33 
34 static struct rocket_iommu_domain*
35 rocket_iommu_domain_create(struct device *dev)
36 {
37 	struct rocket_iommu_domain *domain = kmalloc(sizeof(*domain), GFP_KERNEL);
38 	void *err;
39 
40 	if (!domain)
41 		return ERR_PTR(-ENOMEM);
42 
43 	domain->domain = iommu_paging_domain_alloc(dev);
44 	if (IS_ERR(domain->domain)) {
45 		err = ERR_CAST(domain->domain);
46 		kfree(domain);
47 		return err;
48 	}
49 	kref_init(&domain->kref);
50 
51 	return domain;
52 }
53 
54 struct rocket_iommu_domain *
55 rocket_iommu_domain_get(struct rocket_file_priv *rocket_priv)
56 {
57 	kref_get(&rocket_priv->domain->kref);
58 	return rocket_priv->domain;
59 }
60 
61 void
62 rocket_iommu_domain_put(struct rocket_iommu_domain *domain)
63 {
64 	kref_put(&domain->kref, rocket_iommu_domain_destroy);
65 }
66 
67 static int
68 rocket_open(struct drm_device *dev, struct drm_file *file)
69 {
70 	struct rocket_device *rdev = to_rocket_device(dev);
71 	struct rocket_file_priv *rocket_priv;
72 	int ret;
73 
74 	if (!try_module_get(THIS_MODULE))
75 		return -EINVAL;
76 
77 	rocket_priv = kzalloc(sizeof(*rocket_priv), GFP_KERNEL);
78 	if (!rocket_priv) {
79 		ret = -ENOMEM;
80 		goto err_put_mod;
81 	}
82 
83 	rocket_priv->rdev = rdev;
84 	rocket_priv->domain = rocket_iommu_domain_create(rdev->cores[0].dev);
85 	if (IS_ERR(rocket_priv->domain)) {
86 		ret = PTR_ERR(rocket_priv->domain);
87 		goto err_free;
88 	}
89 
90 	file->driver_priv = rocket_priv;
91 
92 	return 0;
93 
94 err_free:
95 	kfree(rocket_priv);
96 err_put_mod:
97 	module_put(THIS_MODULE);
98 	return ret;
99 }
100 
101 static void
102 rocket_postclose(struct drm_device *dev, struct drm_file *file)
103 {
104 	struct rocket_file_priv *rocket_priv = file->driver_priv;
105 
106 	rocket_iommu_domain_put(rocket_priv->domain);
107 	kfree(rocket_priv);
108 	module_put(THIS_MODULE);
109 }
110 
111 static const struct drm_ioctl_desc rocket_drm_driver_ioctls[] = {
112 #define ROCKET_IOCTL(n, func) \
113 	DRM_IOCTL_DEF_DRV(ROCKET_##n, rocket_ioctl_##func, 0)
114 };
115 
116 DEFINE_DRM_ACCEL_FOPS(rocket_accel_driver_fops);
117 
118 /*
119  * Rocket driver version:
120  * - 1.0 - initial interface
121  */
122 static const struct drm_driver rocket_drm_driver = {
123 	.driver_features	= DRIVER_COMPUTE_ACCEL,
124 	.open			= rocket_open,
125 	.postclose		= rocket_postclose,
126 	.ioctls			= rocket_drm_driver_ioctls,
127 	.num_ioctls		= ARRAY_SIZE(rocket_drm_driver_ioctls),
128 	.fops			= &rocket_accel_driver_fops,
129 	.name			= "rocket",
130 	.desc			= "rocket DRM",
131 };
132 
133 static int rocket_probe(struct platform_device *pdev)
134 {
135 	if (rdev == NULL) {
136 		/* First core probing, initialize DRM device. */
137 		rdev = rocket_device_init(drm_dev, &rocket_drm_driver);
138 		if (IS_ERR(rdev)) {
139 			dev_err(&pdev->dev, "failed to initialize rocket device\n");
140 			return PTR_ERR(rdev);
141 		}
142 	}
143 
144 	unsigned int core = rdev->num_cores;
145 
146 	dev_set_drvdata(&pdev->dev, rdev);
147 
148 	rdev->cores[core].rdev = rdev;
149 	rdev->cores[core].dev = &pdev->dev;
150 	rdev->cores[core].index = core;
151 
152 	rdev->num_cores++;
153 
154 	return rocket_core_init(&rdev->cores[core]);
155 }
156 
157 static void rocket_remove(struct platform_device *pdev)
158 {
159 	struct device *dev = &pdev->dev;
160 
161 	for (unsigned int core = 0; core < rdev->num_cores; core++) {
162 		if (rdev->cores[core].dev == dev) {
163 			rocket_core_fini(&rdev->cores[core]);
164 			rdev->num_cores--;
165 			break;
166 		}
167 	}
168 
169 	if (rdev->num_cores == 0) {
170 		/* Last core removed, deinitialize DRM device. */
171 		rocket_device_fini(rdev);
172 		rdev = NULL;
173 	}
174 }
175 
176 static const struct of_device_id dt_match[] = {
177 	{ .compatible = "rockchip,rk3588-rknn-core" },
178 	{}
179 };
180 MODULE_DEVICE_TABLE(of, dt_match);
181 
182 static int find_core_for_dev(struct device *dev)
183 {
184 	struct rocket_device *rdev = dev_get_drvdata(dev);
185 
186 	for (unsigned int core = 0; core < rdev->num_cores; core++) {
187 		if (dev == rdev->cores[core].dev)
188 			return core;
189 	}
190 
191 	return -1;
192 }
193 
194 static int rocket_device_runtime_resume(struct device *dev)
195 {
196 	struct rocket_device *rdev = dev_get_drvdata(dev);
197 	int core = find_core_for_dev(dev);
198 	int err = 0;
199 
200 	if (core < 0)
201 		return -ENODEV;
202 
203 	err = clk_bulk_prepare_enable(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
204 	if (err) {
205 		dev_err(dev, "failed to enable (%d) clocks for core %d\n", err, core);
206 		return err;
207 	}
208 
209 	return 0;
210 }
211 
212 static int rocket_device_runtime_suspend(struct device *dev)
213 {
214 	struct rocket_device *rdev = dev_get_drvdata(dev);
215 	int core = find_core_for_dev(dev);
216 
217 	if (core < 0)
218 		return -ENODEV;
219 
220 	clk_bulk_disable_unprepare(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
221 
222 	return 0;
223 }
224 
225 EXPORT_GPL_DEV_PM_OPS(rocket_pm_ops) = {
226 	RUNTIME_PM_OPS(rocket_device_runtime_suspend, rocket_device_runtime_resume, NULL)
227 	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
228 };
229 
230 static struct platform_driver rocket_driver = {
231 	.probe = rocket_probe,
232 	.remove = rocket_remove,
233 	.driver	 = {
234 		.name = "rocket",
235 		.pm = pm_ptr(&rocket_pm_ops),
236 		.of_match_table = dt_match,
237 	},
238 };
239 
240 static int __init rocket_register(void)
241 {
242 	drm_dev = platform_device_register_simple("rknn", -1, NULL, 0);
243 	if (IS_ERR(drm_dev))
244 		return PTR_ERR(drm_dev);
245 
246 	return platform_driver_register(&rocket_driver);
247 }
248 
249 static void __exit rocket_unregister(void)
250 {
251 	platform_driver_unregister(&rocket_driver);
252 
253 	platform_device_unregister(drm_dev);
254 }
255 
256 module_init(rocket_register);
257 module_exit(rocket_unregister);
258 
259 MODULE_LICENSE("GPL");
260 MODULE_DESCRIPTION("DRM driver for the Rockchip NPU IP");
261 MODULE_AUTHOR("Tomeu Vizoso");
262