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