This initial version supports the NPU as shipped in the RK3588 SoC and
described in the first part of its TRM, in Chapter 36.

This NPU contains 3 independent cores that the driver can submit jobs
to.

This commit adds just hardware initialization and power management.

v2:
- Split cores and IOMMUs as independent devices (Sebastian Reichel)
- Add some documentation (Jeffrey Hugo)
- Be more explicit in the Kconfig documentation (Jeffrey Hugo)
- Remove resets, as these haven't been found useful so far (Zenghui Yu)
- Repack structs (Jeffrey Hugo)
- Use DEFINE_DRM_ACCEL_FOPS (Jeffrey Hugo)
- Use devm_drm_dev_alloc (Jeffrey Hugo)
- Use probe log helper (Jeffrey Hugo)
- Introduce UABI header in a later patch (Jeffrey Hugo)

v3:
- Adapt to a split of the register block in the DT bindings (Nicolas
  Frattaroli)
- Move registers header to its own commit (Thomas Zimmermann)
- Misc. cleanups (Thomas Zimmermann and Jeff Hugo)
- Make use of GPL-2.0-only for the copyright notice (Jeff Hugo)
- PM improvements (Nicolas Frattaroli)

Signed-off-by: Tomeu Vizoso <to...@tomeuvizoso.net>
---
 Documentation/accel/index.rst        |   1 +
 Documentation/accel/rocket/index.rst |  25 +++
 MAINTAINERS                          |  10 ++
 drivers/accel/Kconfig                |   1 +
 drivers/accel/Makefile               |   1 +
 drivers/accel/rocket/Kconfig         |  25 +++
 drivers/accel/rocket/Makefile        |   8 +
 drivers/accel/rocket/rocket_core.c   |  93 +++++++++++
 drivers/accel/rocket/rocket_core.h   |  45 +++++
 drivers/accel/rocket/rocket_device.c |  39 +++++
 drivers/accel/rocket/rocket_device.h |  27 +++
 drivers/accel/rocket/rocket_drv.c    | 315 +++++++++++++++++++++++++++++++++++
 drivers/accel/rocket/rocket_drv.h    |  13 ++
 13 files changed, 603 insertions(+)

diff --git a/Documentation/accel/index.rst b/Documentation/accel/index.rst
index 
bc85f26533d88891dde482f91e26c99991b22869..d8fa332d60a890dbb617454d2a26d9b6f9b196aa
 100644
--- a/Documentation/accel/index.rst
+++ b/Documentation/accel/index.rst
@@ -10,6 +10,7 @@ Compute Accelerators
    introduction
    amdxdna/index
    qaic/index
+   rocket/index
 
 .. only::  subproject and html
 
diff --git a/Documentation/accel/rocket/index.rst 
b/Documentation/accel/rocket/index.rst
new file mode 100644
index 
0000000000000000000000000000000000000000..a3389f9a284c0975bc201f6e09082c01970e08a3
--- /dev/null
+++ b/Documentation/accel/rocket/index.rst
@@ -0,0 +1,25 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+=====================================
+ accel/rocket Rockchip NPU driver
+=====================================
+
+The accel/rocket driver supports the Neural Processing Units (NPUs) inside some
+Rockchip SoCs such as the RK3588. Rockchip calls it RKNN and sometimes RKNPU.
+
+This NPU is closely based on the NVDLA IP released by NVIDIA as open hardware 
in
+2018, along with open source kernel and userspace drivers.
+
+The frontend unit in Rockchip's NPU though is completely different from that in
+the open source IP, so this kernel driver is specific to Rockchip's version.
+
+The hardware is described in chapter 36 in the RK3588 TRM.
+
+This driver just powers the hardware on and off, allocates and maps buffers to
+the device and submits jobs to the frontend unit. Everything else is done in
+userspace, as a Gallium driver (also called rocket) that is part of the Mesa3D
+project.
+
+Hardware currently supported:
+
+* RK3588
\ No newline at end of file
diff --git a/MAINTAINERS b/MAINTAINERS
index 
96b82704950184bd71623ff41fc4df31e4c7fe87..2d8833bf1f2db06ca624d703f19066adab2f9fde
 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7263,6 +7263,16 @@ T:       git 
https://gitlab.freedesktop.org/drm/misc/kernel.git
 F:     drivers/accel/ivpu/
 F:     include/uapi/drm/ivpu_accel.h
 
+DRM ACCEL DRIVER FOR ROCKCHIP NPU
+M:     Tomeu Vizoso <to...@tomeuvizoso.net>
+L:     dri-devel@lists.freedesktop.org
+S:     Supported
+T:     git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F:     Documentation/accel/rocket/
+F:     Documentation/devicetree/bindings/npu/rockchip,rknn-core.yaml
+F:     drivers/accel/rocket/
+F:     include/uapi/drm/rocket_accel.h
+
 DRM COMPUTE ACCELERATORS DRIVERS AND FRAMEWORK
 M:     Oded Gabbay <ogab...@kernel.org>
 L:     dri-devel@lists.freedesktop.org
diff --git a/drivers/accel/Kconfig b/drivers/accel/Kconfig
index 
5b9490367a39fd12d35a8d9021768aa186c09308..bb01cebc42bf16ebf02e938040f339ff94869e33
 100644
--- a/drivers/accel/Kconfig
+++ b/drivers/accel/Kconfig
@@ -28,5 +28,6 @@ source "drivers/accel/amdxdna/Kconfig"
 source "drivers/accel/habanalabs/Kconfig"
 source "drivers/accel/ivpu/Kconfig"
 source "drivers/accel/qaic/Kconfig"
+source "drivers/accel/rocket/Kconfig"
 
 endif
diff --git a/drivers/accel/Makefile b/drivers/accel/Makefile
index 
a301fb6089d4c515430175c5e2ba9190f6dc9158..ffc3fa58866616d933184a7659573cd4d4780a8d
 100644
--- a/drivers/accel/Makefile
+++ b/drivers/accel/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_DRM_ACCEL_AMDXDNA)         += amdxdna/
 obj-$(CONFIG_DRM_ACCEL_HABANALABS)     += habanalabs/
 obj-$(CONFIG_DRM_ACCEL_IVPU)           += ivpu/
 obj-$(CONFIG_DRM_ACCEL_QAIC)           += qaic/
+obj-$(CONFIG_DRM_ACCEL_ROCKET)         += rocket/
\ No newline at end of file
diff --git a/drivers/accel/rocket/Kconfig b/drivers/accel/rocket/Kconfig
new file mode 100644
index 
0000000000000000000000000000000000000000..9a59c6c61bf4d6460d8008b16331f001c97de67d
--- /dev/null
+++ b/drivers/accel/rocket/Kconfig
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_ACCEL_ROCKET
+       tristate "Rocket (support for Rockchip NPUs)"
+       depends on DRM
+       depends on ARM64 || COMPILE_TEST
+       depends on MMU
+       select DRM_SCHED
+       select IOMMU_SUPPORT
+       select IOMMU_IO_PGTABLE_LPAE
+       select DRM_GEM_SHMEM_HELPER
+       help
+         Choose this option if you have a Rockchip SoC that contains a
+         compatible Neural Processing Unit (NPU), such as the RK3588. Called by
+         Rockchip either RKNN or RKNPU, it accelerates inference of neural
+         networks.
+
+         The interface exposed to userspace is described in
+         include/uapi/drm/rocket_accel.h and is used by the Rocket userspace
+         driver in Mesa3D.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called rocket.
diff --git a/drivers/accel/rocket/Makefile b/drivers/accel/rocket/Makefile
new file mode 100644
index 
0000000000000000000000000000000000000000..abdd75f2492eaecf8bf5e78a2ac150ea19ac3e96
--- /dev/null
+++ b/drivers/accel/rocket/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_DRM_ACCEL_ROCKET) := rocket.o
+
+rocket-y := \
+       rocket_core.o \
+       rocket_device.o \
+       rocket_drv.o
diff --git a/drivers/accel/rocket/rocket_core.c 
b/drivers/accel/rocket/rocket_core.c
new file mode 100644
index 
0000000000000000000000000000000000000000..a947c3120558e8af90bc0730a4d30ac796d5683d
--- /dev/null
+++ b/drivers/accel/rocket/rocket_core.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2024-2025 Tomeu Vizoso <to...@tomeuvizoso.net> */
+
+#include <linux/clk.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "rocket_core.h"
+
+static int rocket_clk_init(struct rocket_core *core)
+{
+       struct device *dev = core->dev;
+       int err;
+
+       core->a_clk = devm_clk_get(dev, "aclk");
+       if (IS_ERR(core->a_clk)) {
+               err = PTR_ERR(core->a_clk);
+               dev_err(dev, "devm_clk_get failed %d for aclk in core %d\n", 
err, core->index);
+               return err;
+       }
+
+       core->h_clk = devm_clk_get(dev, "hclk");
+       if (IS_ERR(core->h_clk)) {
+               err = PTR_ERR(core->h_clk);
+               dev_err(dev, "devm_clk_get failed %d for hclk in core %d\n", 
err, core->index);
+               clk_disable_unprepare(core->a_clk);
+               return err;
+       }
+
+       return 0;
+}
+
+int rocket_core_init(struct rocket_core *core)
+{
+       struct device *dev = core->dev;
+       struct platform_device *pdev = to_platform_device(dev);
+       u32 version;
+       int err = 0;
+
+       err = rocket_clk_init(core);
+       if (err)
+               return err;
+
+       core->pc_iomem = devm_platform_ioremap_resource_byname(pdev, "pc");
+       if (IS_ERR(core->pc_iomem)) {
+               dev_err(dev, "couldn't find PC registers %ld\n", 
PTR_ERR(core->pc_iomem));
+               return PTR_ERR(core->pc_iomem);
+       }
+
+       core->cna_iomem = devm_platform_ioremap_resource_byname(pdev, "cna");
+       if (IS_ERR(core->cna_iomem)) {
+               dev_err(dev, "couldn't find CNA registers %ld\n", 
PTR_ERR(core->cna_iomem));
+               return PTR_ERR(core->cna_iomem);
+       }
+
+       core->core_iomem = devm_platform_ioremap_resource_byname(pdev, "core");
+       if (IS_ERR(core->core_iomem)) {
+               dev_err(dev, "couldn't find CORE registers %ld\n", 
PTR_ERR(core->core_iomem));
+               return PTR_ERR(core->core_iomem);
+       }
+
+       pm_runtime_use_autosuspend(dev);
+
+       /*
+        * As this NPU will be most often used as part of a media pipeline that
+        * ends presenting in a display, choose 50 ms (~3 frames at 60Hz) as an
+        * autosuspend delay as that will keep the device powered up while the
+        * pipeline is running.
+        */
+       pm_runtime_set_autosuspend_delay(dev, 50);
+
+       pm_runtime_enable(dev);
+
+       err = pm_runtime_get_sync(dev);
+
+       version = rocket_pc_read(core, VERSION);
+       version += rocket_pc_read(core, VERSION_NUM) & 0xffff;
+
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       dev_info(dev, "Rockchip NPU core %d version: %d\n", core->index, 
version);
+
+       return 0;
+}
+
+void rocket_core_fini(struct rocket_core *core)
+{
+       pm_runtime_dont_use_autosuspend(core->dev);
+       pm_runtime_disable(core->dev);
+}
diff --git a/drivers/accel/rocket/rocket_core.h 
b/drivers/accel/rocket/rocket_core.h
new file mode 100644
index 
0000000000000000000000000000000000000000..3bde8ad8e6e45f9000ee377d7a5ea9ca01f9ac53
--- /dev/null
+++ b/drivers/accel/rocket/rocket_core.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2024-2025 Tomeu Vizoso <to...@tomeuvizoso.net> */
+
+#ifndef __ROCKET_CORE_H__
+#define __ROCKET_CORE_H__
+
+#include <drm/gpu_scheduler.h>
+#include <linux/io.h>
+#include <linux/mutex_types.h>
+
+#include "rocket_registers.h"
+
+#define rocket_pc_readl(core, reg) \
+       readl((core)->pc_iomem + (REG_PC_##reg))
+#define rocket_pc_writel(core, reg, value) \
+       writel(value, (core)->pc_iomem + (REG_PC_##reg))
+
+#define rocket_cna_readl(core, reg) \
+       readl((core)->cna_iomem + (REG_CNA_##reg) - REG_CNA_S_STATUS)
+#define rocket_cna_writel(core, reg, value) \
+       writel(value, (core)->cna_iomem + (REG_CNA_##reg) - REG_CNA_S_STATUS)
+
+#define rocket_core_readl(core, reg) \
+       readl((core)->core_iomem + (REG_CORE_##reg) - REG_CORE_S_STATUS)
+#define rocket_core_writel(core, reg, value) \
+       writel(value, (core)->core_iomem + (REG_CORE_##reg) - REG_CORE_S_STATUS)
+
+struct rocket_core {
+       struct device *dev;
+       struct rocket_device *rdev;
+       struct device_link *link;
+       unsigned int index;
+
+       int irq;
+       void __iomem *pc_iomem;
+       void __iomem *cna_iomem;
+       void __iomem *core_iomem;
+       struct clk *a_clk;
+       struct clk *h_clk;
+};
+
+int rocket_core_init(struct rocket_core *core);
+void rocket_core_fini(struct rocket_core *core);
+
+#endif
diff --git a/drivers/accel/rocket/rocket_device.c 
b/drivers/accel/rocket/rocket_device.c
new file mode 100644
index 
0000000000000000000000000000000000000000..bb469ac87d36249157f4ba9d9f7106ad558309e4
--- /dev/null
+++ b/drivers/accel/rocket/rocket_device.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2024-2025 Tomeu Vizoso <to...@tomeuvizoso.net> */
+
+#include <linux/clk.h>
+#include <linux/dev_printk.h>
+
+#include "rocket_device.h"
+
+int rocket_device_init(struct rocket_device *rdev)
+{
+       struct device *dev = rdev->cores[0].dev;
+       int err;
+
+       rdev->clk_npu = devm_clk_get(dev, "npu");
+       if (IS_ERR(rdev->clk_npu)) {
+               err = PTR_ERR(rdev->clk_npu);
+               dev_err(dev, "devm_clk_get failed %d for clock npu\n", err);
+               return err;
+       }
+
+       rdev->pclk = devm_clk_get(dev, "pclk");
+       if (IS_ERR(rdev->pclk)) {
+               err = PTR_ERR(rdev->pclk);
+               dev_err(dev, "devm_clk_get failed %d for clock pclk\n", err);
+               return err;
+       }
+
+       /* Initialize core 0 (top) */
+       err = rocket_core_init(&rdev->cores[0]);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+void rocket_device_fini(struct rocket_device *rdev)
+{
+       rocket_core_fini(&rdev->cores[0]);
+}
diff --git a/drivers/accel/rocket/rocket_device.h 
b/drivers/accel/rocket/rocket_device.h
new file mode 100644
index 
0000000000000000000000000000000000000000..ba2301e9302120ae338c07baa7d12dd99cb925a9
--- /dev/null
+++ b/drivers/accel/rocket/rocket_device.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2024-2025 Tomeu Vizoso <to...@tomeuvizoso.net> */
+
+#ifndef __ROCKET_DEVICE_H__
+#define __ROCKET_DEVICE_H__
+
+#include <drm/drm_device.h>
+
+#include "rocket_core.h"
+
+struct rocket_device {
+       struct drm_device ddev;
+
+       struct clk *clk_npu;
+       struct clk *pclk;
+
+       struct rocket_core *cores;
+       unsigned int num_cores;
+};
+
+int rocket_device_init(struct rocket_device *rdev);
+void rocket_device_fini(struct rocket_device *rdev);
+
+#define to_rocket_device(drm_dev) \
+       ((struct rocket_device *)container_of(drm_dev, struct rocket_device, 
ddev))
+
+#endif
diff --git a/drivers/accel/rocket/rocket_drv.c 
b/drivers/accel/rocket/rocket_drv.c
new file mode 100644
index 
0000000000000000000000000000000000000000..82f4cc374bfaa92678da791849537d51bb4c0ba8
--- /dev/null
+++ b/drivers/accel/rocket/rocket_drv.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2024-2025 Tomeu Vizoso <to...@tomeuvizoso.net> */
+
+#include <drm/drm_accel.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_of.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "rocket_drv.h"
+
+static int
+rocket_open(struct drm_device *dev, struct drm_file *file)
+{
+       struct rocket_device *rdev = to_rocket_device(dev);
+       struct rocket_file_priv *rocket_priv;
+
+       rocket_priv = kzalloc(sizeof(*rocket_priv), GFP_KERNEL);
+       if (!rocket_priv)
+               return -ENOMEM;
+
+       rocket_priv->rdev = rdev;
+       file->driver_priv = rocket_priv;
+
+       return 0;
+}
+
+static void
+rocket_postclose(struct drm_device *dev, struct drm_file *file)
+{
+       struct rocket_file_priv *rocket_priv = file->driver_priv;
+
+       kfree(rocket_priv);
+}
+
+static const struct drm_ioctl_desc rocket_drm_driver_ioctls[] = {
+#define ROCKET_IOCTL(n, func) \
+       DRM_IOCTL_DEF_DRV(ROCKET_##n, rocket_ioctl_##func, 0)
+};
+
+DEFINE_DRM_ACCEL_FOPS(rocket_accel_driver_fops);
+
+/*
+ * Rocket driver version:
+ * - 1.0 - initial interface
+ */
+static const struct drm_driver rocket_drm_driver = {
+       .driver_features        = DRIVER_COMPUTE_ACCEL,
+       .open                   = rocket_open,
+       .postclose              = rocket_postclose,
+       .ioctls                 = rocket_drm_driver_ioctls,
+       .num_ioctls             = ARRAY_SIZE(rocket_drm_driver_ioctls),
+       .fops                   = &rocket_accel_driver_fops,
+       .name                   = "rocket",
+       .desc                   = "rocket DRM",
+};
+
+static int rocket_drm_bind(struct device *dev)
+{
+       struct device_node *core_node;
+       struct rocket_device *rdev;
+       struct drm_device *ddev;
+       unsigned int num_cores = 1;
+       int err;
+
+       rdev = devm_drm_dev_alloc(dev, &rocket_drm_driver, struct 
rocket_device, ddev);
+       if (IS_ERR(rdev))
+               return PTR_ERR(rdev);
+
+       ddev = &rdev->ddev;
+       dev_set_drvdata(dev, rdev);
+
+       for_each_compatible_node(core_node, NULL, "rockchip,rk3588-rknn-core")
+               if (of_device_is_available(core_node))
+                       num_cores++;
+
+       rdev->cores = devm_kmalloc_array(dev, num_cores, sizeof(*rdev->cores),
+                                        GFP_KERNEL | __GFP_ZERO);
+       if (IS_ERR(rdev->cores))
+               return PTR_ERR(rdev->cores);
+
+       /* Add core 0, any other cores will be added later when they are bound 
*/
+       rdev->cores[0].rdev = rdev;
+       rdev->cores[0].dev = dev;
+       rdev->cores[0].index = 0;
+       rdev->num_cores = 1;
+
+       err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+       if (err)
+               return err;
+
+       err = rocket_device_init(rdev);
+       if (err) {
+               dev_err_probe(dev, err, "Fatal error during NPU init\n");
+               goto err_device_fini;
+       }
+
+       err = component_bind_all(dev, rdev);
+       if (err)
+               goto err_device_fini;
+
+       err = drm_dev_register(ddev, 0);
+       if (err < 0)
+               goto err_unbind;
+
+       return 0;
+
+err_unbind:
+       component_unbind_all(dev, rdev);
+err_device_fini:
+       rocket_device_fini(rdev);
+       return err;
+}
+
+static void rocket_drm_unbind(struct device *dev)
+{
+       struct rocket_device *rdev = dev_get_drvdata(dev);
+       struct drm_device *ddev = &rdev->ddev;
+
+       drm_dev_unregister(ddev);
+
+       component_unbind_all(dev, rdev);
+
+       rocket_device_fini(rdev);
+}
+
+const struct component_master_ops rocket_drm_ops = {
+       .bind = rocket_drm_bind,
+       .unbind = rocket_drm_unbind,
+};
+
+static int rocket_core_bind(struct device *dev, struct device *master, void 
*data)
+{
+       struct rocket_device *rdev = data;
+       unsigned int core = rdev->num_cores;
+       int err;
+
+       dev_set_drvdata(dev, rdev);
+
+       rdev->cores[core].rdev = rdev;
+       rdev->cores[core].dev = dev;
+       rdev->cores[core].index = core;
+       rdev->cores[core].link = device_link_add(dev, rdev->cores[0].dev,
+                                                DL_FLAG_STATELESS | 
DL_FLAG_PM_RUNTIME);
+
+       rdev->num_cores++;
+
+       err = rocket_core_init(&rdev->cores[core]);
+       if (err) {
+               rocket_device_fini(rdev);
+               return err;
+       }
+
+       return 0;
+}
+
+static void rocket_core_unbind(struct device *dev, struct device *master, void 
*data)
+{
+       struct rocket_device *rdev = data;
+
+       for (unsigned int core = 1; core < rdev->num_cores; core++) {
+               if (rdev->cores[core].dev == dev) {
+                       rocket_core_fini(&rdev->cores[core]);
+                       device_link_del(rdev->cores[core].link);
+                       break;
+               }
+       }
+}
+
+const struct component_ops rocket_core_ops = {
+       .bind = rocket_core_bind,
+       .unbind = rocket_core_unbind,
+};
+
+static int rocket_probe(struct platform_device *pdev)
+{
+       struct component_match *match = NULL;
+       struct device_node *core_node;
+
+       if (fwnode_device_is_compatible(pdev->dev.fwnode, 
"rockchip,rk3588-rknn-core"))
+               return component_add(&pdev->dev, &rocket_core_ops);
+
+       for_each_compatible_node(core_node, NULL, "rockchip,rk3588-rknn-core") {
+               if (!of_device_is_available(core_node))
+                       continue;
+
+               drm_of_component_match_add(&pdev->dev, &match,
+                                          component_compare_of, core_node);
+       }
+
+       return component_master_add_with_match(&pdev->dev, &rocket_drm_ops, 
match);
+}
+
+static void rocket_remove(struct platform_device *pdev)
+{
+       if (fwnode_device_is_compatible(pdev->dev.fwnode, 
"rockchip,rk3588-rknn-core-top"))
+               component_master_del(&pdev->dev, &rocket_drm_ops);
+       else if (fwnode_device_is_compatible(pdev->dev.fwnode, 
"rockchip,rk3588-rknn-core"))
+               component_del(&pdev->dev, &rocket_core_ops);
+}
+
+static const struct of_device_id dt_match[] = {
+       { .compatible = "rockchip,rk3588-rknn-core-top" },
+       { .compatible = "rockchip,rk3588-rknn-core" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, dt_match);
+
+static int find_core_for_dev(struct device *dev)
+{
+       struct rocket_device *rdev = dev_get_drvdata(dev);
+
+       for (unsigned int core = 0; core < rdev->num_cores; core++) {
+               if (dev == rdev->cores[core].dev)
+                       return core;
+       }
+
+       return -1;
+}
+
+static int rocket_device_runtime_resume(struct device *dev)
+{
+       struct rocket_device *rdev = dev_get_drvdata(dev);
+       int core = find_core_for_dev(dev);
+       int err = 0;
+
+       if (core < 0)
+               return -ENODEV;
+
+       if (core == 0) {
+               err = clk_prepare_enable(rdev->clk_npu);
+               if (err) {
+                       dev_err(dev, "clk_prepare_enable failed %d for clock 
npu\n", err);
+                       return err;
+               }
+
+               err = clk_prepare_enable(rdev->pclk);
+               if (err) {
+                       dev_err(dev, "clk_prepare_enable failed %d for clock 
pclk\n", err);
+                       goto error_clk_npu;
+               }
+       }
+
+       err = clk_prepare_enable(rdev->cores[core].a_clk);
+       if (err) {
+               dev_err(dev, "clk_prepare_enable failed %d for a_clk in core 
%d\n", err, core);
+               goto error_pclk;
+       }
+
+       err = clk_prepare_enable(rdev->cores[core].h_clk);
+       if (err) {
+               dev_err(dev, "clk_prepare_enable failed %d for h_clk in core 
%d\n", err, core);
+               goto error_a_clk;
+       }
+
+       return 0;
+
+error_a_clk:
+       clk_disable_unprepare(rdev->cores[core].a_clk);
+
+error_pclk:
+       if (core == 0)
+               clk_disable_unprepare(rdev->pclk);
+
+error_clk_npu:
+       if (core == 0)
+               clk_disable_unprepare(rdev->clk_npu);
+
+       return err;
+}
+
+static int rocket_device_runtime_suspend(struct device *dev)
+{
+       struct rocket_device *rdev = dev_get_drvdata(dev);
+       int core = find_core_for_dev(dev);
+
+       if (core < 0)
+               return -ENODEV;
+
+       clk_disable_unprepare(rdev->cores[core].a_clk);
+       clk_disable_unprepare(rdev->cores[core].h_clk);
+
+       if (core == 0) {
+               clk_disable_unprepare(rdev->pclk);
+               clk_disable_unprepare(rdev->clk_npu);
+       }
+
+       return 0;
+}
+
+EXPORT_GPL_DEV_PM_OPS(rocket_pm_ops) = {
+       RUNTIME_PM_OPS(rocket_device_runtime_suspend, 
rocket_device_runtime_resume, NULL)
+       SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static struct platform_driver rocket_driver = {
+       .probe = rocket_probe,
+       .remove = rocket_remove,
+       .driver  = {
+               .name = "rocket",
+               .pm = pm_ptr(&rocket_pm_ops),
+               .of_match_table = dt_match,
+       },
+};
+module_platform_driver(rocket_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DRM driver for the Rockchip NPU IP");
+MODULE_AUTHOR("Tomeu Vizoso");
diff --git a/drivers/accel/rocket/rocket_drv.h 
b/drivers/accel/rocket/rocket_drv.h
new file mode 100644
index 
0000000000000000000000000000000000000000..bd3a697ab7c8e378967ce638b04d7d86845b53c7
--- /dev/null
+++ b/drivers/accel/rocket/rocket_drv.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2024-2025 Tomeu Vizoso <to...@tomeuvizoso.net> */
+
+#ifndef __ROCKET_DRV_H__
+#define __ROCKET_DRV_H__
+
+#include "rocket_device.h"
+
+struct rocket_file_priv {
+       struct rocket_device *rdev;
+};
+
+#endif

-- 
2.49.0

Reply via email to