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