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 <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