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