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