xref: /linux/drivers/accel/rocket/rocket_drv.c (revision 58809f614e0e3f4e12b489bddf680bfeb31c0a20)
1ed98261bSTomeu Vizoso // SPDX-License-Identifier: GPL-2.0-only
2ed98261bSTomeu Vizoso /* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
3ed98261bSTomeu Vizoso 
4ed98261bSTomeu Vizoso #include <drm/drm_accel.h>
5ed98261bSTomeu Vizoso #include <drm/drm_drv.h>
6ed98261bSTomeu Vizoso #include <drm/drm_gem.h>
7ed98261bSTomeu Vizoso #include <drm/drm_ioctl.h>
8658ebeacSTomeu Vizoso #include <drm/rocket_accel.h>
9ed98261bSTomeu Vizoso #include <linux/clk.h>
10ed98261bSTomeu Vizoso #include <linux/err.h>
11ed98261bSTomeu Vizoso #include <linux/iommu.h>
12ed98261bSTomeu Vizoso #include <linux/of.h>
13ed98261bSTomeu Vizoso #include <linux/platform_device.h>
14ed98261bSTomeu Vizoso #include <linux/pm_runtime.h>
15ed98261bSTomeu Vizoso 
16ed98261bSTomeu Vizoso #include "rocket_drv.h"
17658ebeacSTomeu Vizoso #include "rocket_gem.h"
180810d5adSTomeu Vizoso #include "rocket_job.h"
19ed98261bSTomeu Vizoso 
20ed98261bSTomeu Vizoso /*
21ed98261bSTomeu Vizoso  * Facade device, used to expose a single DRM device to userspace, that
22ed98261bSTomeu Vizoso  * schedules jobs to any RKNN cores in the system.
23ed98261bSTomeu Vizoso  */
24ed98261bSTomeu Vizoso static struct platform_device *drm_dev;
25ed98261bSTomeu Vizoso static struct rocket_device *rdev;
26ed98261bSTomeu Vizoso 
27ed98261bSTomeu Vizoso static void
28ed98261bSTomeu Vizoso rocket_iommu_domain_destroy(struct kref *kref)
29ed98261bSTomeu Vizoso {
30ed98261bSTomeu Vizoso 	struct rocket_iommu_domain *domain = container_of(kref, struct rocket_iommu_domain, kref);
31ed98261bSTomeu Vizoso 
32ed98261bSTomeu Vizoso 	iommu_domain_free(domain->domain);
33ed98261bSTomeu Vizoso 	domain->domain = NULL;
34ed98261bSTomeu Vizoso 	kfree(domain);
35ed98261bSTomeu Vizoso }
36ed98261bSTomeu Vizoso 
37ed98261bSTomeu Vizoso static struct rocket_iommu_domain*
38ed98261bSTomeu Vizoso rocket_iommu_domain_create(struct device *dev)
39ed98261bSTomeu Vizoso {
40ed98261bSTomeu Vizoso 	struct rocket_iommu_domain *domain = kmalloc(sizeof(*domain), GFP_KERNEL);
41ed98261bSTomeu Vizoso 	void *err;
42ed98261bSTomeu Vizoso 
43ed98261bSTomeu Vizoso 	if (!domain)
44ed98261bSTomeu Vizoso 		return ERR_PTR(-ENOMEM);
45ed98261bSTomeu Vizoso 
46ed98261bSTomeu Vizoso 	domain->domain = iommu_paging_domain_alloc(dev);
47ed98261bSTomeu Vizoso 	if (IS_ERR(domain->domain)) {
48ed98261bSTomeu Vizoso 		err = ERR_CAST(domain->domain);
49ed98261bSTomeu Vizoso 		kfree(domain);
50ed98261bSTomeu Vizoso 		return err;
51ed98261bSTomeu Vizoso 	}
52ed98261bSTomeu Vizoso 	kref_init(&domain->kref);
53ed98261bSTomeu Vizoso 
54ed98261bSTomeu Vizoso 	return domain;
55ed98261bSTomeu Vizoso }
56ed98261bSTomeu Vizoso 
57ed98261bSTomeu Vizoso struct rocket_iommu_domain *
58ed98261bSTomeu Vizoso rocket_iommu_domain_get(struct rocket_file_priv *rocket_priv)
59ed98261bSTomeu Vizoso {
60ed98261bSTomeu Vizoso 	kref_get(&rocket_priv->domain->kref);
61ed98261bSTomeu Vizoso 	return rocket_priv->domain;
62ed98261bSTomeu Vizoso }
63ed98261bSTomeu Vizoso 
64ed98261bSTomeu Vizoso void
65ed98261bSTomeu Vizoso rocket_iommu_domain_put(struct rocket_iommu_domain *domain)
66ed98261bSTomeu Vizoso {
67ed98261bSTomeu Vizoso 	kref_put(&domain->kref, rocket_iommu_domain_destroy);
68ed98261bSTomeu Vizoso }
69ed98261bSTomeu Vizoso 
70ed98261bSTomeu Vizoso static int
71ed98261bSTomeu Vizoso rocket_open(struct drm_device *dev, struct drm_file *file)
72ed98261bSTomeu Vizoso {
73ed98261bSTomeu Vizoso 	struct rocket_device *rdev = to_rocket_device(dev);
74ed98261bSTomeu Vizoso 	struct rocket_file_priv *rocket_priv;
75658ebeacSTomeu Vizoso 	u64 start, end;
76ed98261bSTomeu Vizoso 	int ret;
77ed98261bSTomeu Vizoso 
78ed98261bSTomeu Vizoso 	if (!try_module_get(THIS_MODULE))
79ed98261bSTomeu Vizoso 		return -EINVAL;
80ed98261bSTomeu Vizoso 
81ed98261bSTomeu Vizoso 	rocket_priv = kzalloc(sizeof(*rocket_priv), GFP_KERNEL);
82ed98261bSTomeu Vizoso 	if (!rocket_priv) {
83ed98261bSTomeu Vizoso 		ret = -ENOMEM;
84ed98261bSTomeu Vizoso 		goto err_put_mod;
85ed98261bSTomeu Vizoso 	}
86ed98261bSTomeu Vizoso 
87ed98261bSTomeu Vizoso 	rocket_priv->rdev = rdev;
88ed98261bSTomeu Vizoso 	rocket_priv->domain = rocket_iommu_domain_create(rdev->cores[0].dev);
89ed98261bSTomeu Vizoso 	if (IS_ERR(rocket_priv->domain)) {
90ed98261bSTomeu Vizoso 		ret = PTR_ERR(rocket_priv->domain);
91ed98261bSTomeu Vizoso 		goto err_free;
92ed98261bSTomeu Vizoso 	}
93ed98261bSTomeu Vizoso 
94ed98261bSTomeu Vizoso 	file->driver_priv = rocket_priv;
95ed98261bSTomeu Vizoso 
96658ebeacSTomeu Vizoso 	start = rocket_priv->domain->domain->geometry.aperture_start;
97658ebeacSTomeu Vizoso 	end = rocket_priv->domain->domain->geometry.aperture_end;
98658ebeacSTomeu Vizoso 	drm_mm_init(&rocket_priv->mm, start, end - start + 1);
99658ebeacSTomeu Vizoso 	mutex_init(&rocket_priv->mm_lock);
100658ebeacSTomeu Vizoso 
1010810d5adSTomeu Vizoso 	ret = rocket_job_open(rocket_priv);
1020810d5adSTomeu Vizoso 	if (ret)
1030810d5adSTomeu Vizoso 		goto err_mm_takedown;
1040810d5adSTomeu Vizoso 
105ed98261bSTomeu Vizoso 	return 0;
106ed98261bSTomeu Vizoso 
1070810d5adSTomeu Vizoso err_mm_takedown:
1080810d5adSTomeu Vizoso 	mutex_destroy(&rocket_priv->mm_lock);
1090810d5adSTomeu Vizoso 	drm_mm_takedown(&rocket_priv->mm);
1100810d5adSTomeu Vizoso 	rocket_iommu_domain_put(rocket_priv->domain);
111ed98261bSTomeu Vizoso err_free:
112ed98261bSTomeu Vizoso 	kfree(rocket_priv);
113ed98261bSTomeu Vizoso err_put_mod:
114ed98261bSTomeu Vizoso 	module_put(THIS_MODULE);
115ed98261bSTomeu Vizoso 	return ret;
116ed98261bSTomeu Vizoso }
117ed98261bSTomeu Vizoso 
118ed98261bSTomeu Vizoso static void
119ed98261bSTomeu Vizoso rocket_postclose(struct drm_device *dev, struct drm_file *file)
120ed98261bSTomeu Vizoso {
121ed98261bSTomeu Vizoso 	struct rocket_file_priv *rocket_priv = file->driver_priv;
122ed98261bSTomeu Vizoso 
1230810d5adSTomeu Vizoso 	rocket_job_close(rocket_priv);
124658ebeacSTomeu Vizoso 	mutex_destroy(&rocket_priv->mm_lock);
125658ebeacSTomeu Vizoso 	drm_mm_takedown(&rocket_priv->mm);
126ed98261bSTomeu Vizoso 	rocket_iommu_domain_put(rocket_priv->domain);
127ed98261bSTomeu Vizoso 	kfree(rocket_priv);
128ed98261bSTomeu Vizoso 	module_put(THIS_MODULE);
129ed98261bSTomeu Vizoso }
130ed98261bSTomeu Vizoso 
131ed98261bSTomeu Vizoso static const struct drm_ioctl_desc rocket_drm_driver_ioctls[] = {
132ed98261bSTomeu Vizoso #define ROCKET_IOCTL(n, func) \
133ed98261bSTomeu Vizoso 	DRM_IOCTL_DEF_DRV(ROCKET_##n, rocket_ioctl_##func, 0)
134658ebeacSTomeu Vizoso 
135658ebeacSTomeu Vizoso 	ROCKET_IOCTL(CREATE_BO, create_bo),
1360810d5adSTomeu Vizoso 	ROCKET_IOCTL(SUBMIT, submit),
137*525ad89dSTomeu Vizoso 	ROCKET_IOCTL(PREP_BO, prep_bo),
138*525ad89dSTomeu Vizoso 	ROCKET_IOCTL(FINI_BO, fini_bo),
139ed98261bSTomeu Vizoso };
140ed98261bSTomeu Vizoso 
141ed98261bSTomeu Vizoso DEFINE_DRM_ACCEL_FOPS(rocket_accel_driver_fops);
142ed98261bSTomeu Vizoso 
143ed98261bSTomeu Vizoso /*
144ed98261bSTomeu Vizoso  * Rocket driver version:
145ed98261bSTomeu Vizoso  * - 1.0 - initial interface
146ed98261bSTomeu Vizoso  */
147ed98261bSTomeu Vizoso static const struct drm_driver rocket_drm_driver = {
148658ebeacSTomeu Vizoso 	.driver_features	= DRIVER_COMPUTE_ACCEL | DRIVER_GEM,
149ed98261bSTomeu Vizoso 	.open			= rocket_open,
150ed98261bSTomeu Vizoso 	.postclose		= rocket_postclose,
151658ebeacSTomeu Vizoso 	.gem_create_object	= rocket_gem_create_object,
152ed98261bSTomeu Vizoso 	.ioctls			= rocket_drm_driver_ioctls,
153ed98261bSTomeu Vizoso 	.num_ioctls		= ARRAY_SIZE(rocket_drm_driver_ioctls),
154ed98261bSTomeu Vizoso 	.fops			= &rocket_accel_driver_fops,
155ed98261bSTomeu Vizoso 	.name			= "rocket",
156ed98261bSTomeu Vizoso 	.desc			= "rocket DRM",
157ed98261bSTomeu Vizoso };
158ed98261bSTomeu Vizoso 
159ed98261bSTomeu Vizoso static int rocket_probe(struct platform_device *pdev)
160ed98261bSTomeu Vizoso {
161ed98261bSTomeu Vizoso 	if (rdev == NULL) {
162ed98261bSTomeu Vizoso 		/* First core probing, initialize DRM device. */
163ed98261bSTomeu Vizoso 		rdev = rocket_device_init(drm_dev, &rocket_drm_driver);
164ed98261bSTomeu Vizoso 		if (IS_ERR(rdev)) {
165ed98261bSTomeu Vizoso 			dev_err(&pdev->dev, "failed to initialize rocket device\n");
166ed98261bSTomeu Vizoso 			return PTR_ERR(rdev);
167ed98261bSTomeu Vizoso 		}
168ed98261bSTomeu Vizoso 	}
169ed98261bSTomeu Vizoso 
170ed98261bSTomeu Vizoso 	unsigned int core = rdev->num_cores;
171ed98261bSTomeu Vizoso 
172ed98261bSTomeu Vizoso 	dev_set_drvdata(&pdev->dev, rdev);
173ed98261bSTomeu Vizoso 
174ed98261bSTomeu Vizoso 	rdev->cores[core].rdev = rdev;
175ed98261bSTomeu Vizoso 	rdev->cores[core].dev = &pdev->dev;
176ed98261bSTomeu Vizoso 	rdev->cores[core].index = core;
177ed98261bSTomeu Vizoso 
178ed98261bSTomeu Vizoso 	rdev->num_cores++;
179ed98261bSTomeu Vizoso 
180ed98261bSTomeu Vizoso 	return rocket_core_init(&rdev->cores[core]);
181ed98261bSTomeu Vizoso }
182ed98261bSTomeu Vizoso 
183ed98261bSTomeu Vizoso static void rocket_remove(struct platform_device *pdev)
184ed98261bSTomeu Vizoso {
185ed98261bSTomeu Vizoso 	struct device *dev = &pdev->dev;
186ed98261bSTomeu Vizoso 
187ed98261bSTomeu Vizoso 	for (unsigned int core = 0; core < rdev->num_cores; core++) {
188ed98261bSTomeu Vizoso 		if (rdev->cores[core].dev == dev) {
189ed98261bSTomeu Vizoso 			rocket_core_fini(&rdev->cores[core]);
190ed98261bSTomeu Vizoso 			rdev->num_cores--;
191ed98261bSTomeu Vizoso 			break;
192ed98261bSTomeu Vizoso 		}
193ed98261bSTomeu Vizoso 	}
194ed98261bSTomeu Vizoso 
195ed98261bSTomeu Vizoso 	if (rdev->num_cores == 0) {
196ed98261bSTomeu Vizoso 		/* Last core removed, deinitialize DRM device. */
197ed98261bSTomeu Vizoso 		rocket_device_fini(rdev);
198ed98261bSTomeu Vizoso 		rdev = NULL;
199ed98261bSTomeu Vizoso 	}
200ed98261bSTomeu Vizoso }
201ed98261bSTomeu Vizoso 
202ed98261bSTomeu Vizoso static const struct of_device_id dt_match[] = {
203ed98261bSTomeu Vizoso 	{ .compatible = "rockchip,rk3588-rknn-core" },
204ed98261bSTomeu Vizoso 	{}
205ed98261bSTomeu Vizoso };
206ed98261bSTomeu Vizoso MODULE_DEVICE_TABLE(of, dt_match);
207ed98261bSTomeu Vizoso 
208ed98261bSTomeu Vizoso static int find_core_for_dev(struct device *dev)
209ed98261bSTomeu Vizoso {
210ed98261bSTomeu Vizoso 	struct rocket_device *rdev = dev_get_drvdata(dev);
211ed98261bSTomeu Vizoso 
212ed98261bSTomeu Vizoso 	for (unsigned int core = 0; core < rdev->num_cores; core++) {
213ed98261bSTomeu Vizoso 		if (dev == rdev->cores[core].dev)
214ed98261bSTomeu Vizoso 			return core;
215ed98261bSTomeu Vizoso 	}
216ed98261bSTomeu Vizoso 
217ed98261bSTomeu Vizoso 	return -1;
218ed98261bSTomeu Vizoso }
219ed98261bSTomeu Vizoso 
220ed98261bSTomeu Vizoso static int rocket_device_runtime_resume(struct device *dev)
221ed98261bSTomeu Vizoso {
222ed98261bSTomeu Vizoso 	struct rocket_device *rdev = dev_get_drvdata(dev);
223ed98261bSTomeu Vizoso 	int core = find_core_for_dev(dev);
224ed98261bSTomeu Vizoso 	int err = 0;
225ed98261bSTomeu Vizoso 
226ed98261bSTomeu Vizoso 	if (core < 0)
227ed98261bSTomeu Vizoso 		return -ENODEV;
228ed98261bSTomeu Vizoso 
229ed98261bSTomeu Vizoso 	err = clk_bulk_prepare_enable(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
230ed98261bSTomeu Vizoso 	if (err) {
231ed98261bSTomeu Vizoso 		dev_err(dev, "failed to enable (%d) clocks for core %d\n", err, core);
232ed98261bSTomeu Vizoso 		return err;
233ed98261bSTomeu Vizoso 	}
234ed98261bSTomeu Vizoso 
235ed98261bSTomeu Vizoso 	return 0;
236ed98261bSTomeu Vizoso }
237ed98261bSTomeu Vizoso 
238ed98261bSTomeu Vizoso static int rocket_device_runtime_suspend(struct device *dev)
239ed98261bSTomeu Vizoso {
240ed98261bSTomeu Vizoso 	struct rocket_device *rdev = dev_get_drvdata(dev);
241ed98261bSTomeu Vizoso 	int core = find_core_for_dev(dev);
242ed98261bSTomeu Vizoso 
243ed98261bSTomeu Vizoso 	if (core < 0)
244ed98261bSTomeu Vizoso 		return -ENODEV;
245ed98261bSTomeu Vizoso 
2460810d5adSTomeu Vizoso 	if (!rocket_job_is_idle(&rdev->cores[core]))
2470810d5adSTomeu Vizoso 		return -EBUSY;
2480810d5adSTomeu Vizoso 
249ed98261bSTomeu Vizoso 	clk_bulk_disable_unprepare(ARRAY_SIZE(rdev->cores[core].clks), rdev->cores[core].clks);
250ed98261bSTomeu Vizoso 
251ed98261bSTomeu Vizoso 	return 0;
252ed98261bSTomeu Vizoso }
253ed98261bSTomeu Vizoso 
254ed98261bSTomeu Vizoso EXPORT_GPL_DEV_PM_OPS(rocket_pm_ops) = {
255ed98261bSTomeu Vizoso 	RUNTIME_PM_OPS(rocket_device_runtime_suspend, rocket_device_runtime_resume, NULL)
256ed98261bSTomeu Vizoso 	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
257ed98261bSTomeu Vizoso };
258ed98261bSTomeu Vizoso 
259ed98261bSTomeu Vizoso static struct platform_driver rocket_driver = {
260ed98261bSTomeu Vizoso 	.probe = rocket_probe,
261ed98261bSTomeu Vizoso 	.remove = rocket_remove,
262ed98261bSTomeu Vizoso 	.driver	 = {
263ed98261bSTomeu Vizoso 		.name = "rocket",
264ed98261bSTomeu Vizoso 		.pm = pm_ptr(&rocket_pm_ops),
265ed98261bSTomeu Vizoso 		.of_match_table = dt_match,
266ed98261bSTomeu Vizoso 	},
267ed98261bSTomeu Vizoso };
268ed98261bSTomeu Vizoso 
269ed98261bSTomeu Vizoso static int __init rocket_register(void)
270ed98261bSTomeu Vizoso {
271ed98261bSTomeu Vizoso 	drm_dev = platform_device_register_simple("rknn", -1, NULL, 0);
272ed98261bSTomeu Vizoso 	if (IS_ERR(drm_dev))
273ed98261bSTomeu Vizoso 		return PTR_ERR(drm_dev);
274ed98261bSTomeu Vizoso 
275ed98261bSTomeu Vizoso 	return platform_driver_register(&rocket_driver);
276ed98261bSTomeu Vizoso }
277ed98261bSTomeu Vizoso 
278ed98261bSTomeu Vizoso static void __exit rocket_unregister(void)
279ed98261bSTomeu Vizoso {
280ed98261bSTomeu Vizoso 	platform_driver_unregister(&rocket_driver);
281ed98261bSTomeu Vizoso 
282ed98261bSTomeu Vizoso 	platform_device_unregister(drm_dev);
283ed98261bSTomeu Vizoso }
284ed98261bSTomeu Vizoso 
285ed98261bSTomeu Vizoso module_init(rocket_register);
286ed98261bSTomeu Vizoso module_exit(rocket_unregister);
287ed98261bSTomeu Vizoso 
288ed98261bSTomeu Vizoso MODULE_LICENSE("GPL");
289ed98261bSTomeu Vizoso MODULE_DESCRIPTION("DRM driver for the Rockchip NPU IP");
290ed98261bSTomeu Vizoso MODULE_AUTHOR("Tomeu Vizoso");
291