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