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