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