[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20260113-thames-v1-2-99390026937c@tomeuvizoso.net>
Date: Tue, 13 Jan 2026 18:44:36 +0100
From: Tomeu Vizoso <tomeu@...euvizoso.net>
To: Nishanth Menon <nm@...com>, "Andrew F. Davis" <afd@...com>,
Randolph Sapp <rs@...com>, Jonathan Humphreys <j-humphreys@...com>,
Andrei Aldea <a-aldea@...com>, Chirag Shilwant <c-shilwant@...com>,
Vignesh Raghavendra <vigneshr@...com>, Tero Kristo <kristo@...nel.org>,
Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>, Oded Gabbay <ogabbay@...nel.org>,
Jonathan Corbet <corbet@....net>, Sumit Semwal <sumit.semwal@...aro.org>,
Christian König <christian.koenig@....com>
Cc: linux-arm-kernel@...ts.infradead.org, devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org, dri-devel@...ts.freedesktop.org,
linux-doc@...r.kernel.org, linux-media@...r.kernel.org,
linaro-mm-sig@...ts.linaro.org, Tomeu Vizoso <tomeu@...euvizoso.net>
Subject: [PATCH 2/5] accel/thames: Add driver for the C7x DSPs in TI SoCs
Some SoCs from Texas Instruments contain DSPs that can be used for
general compute tasks.
This driver provides a drm/accel UABI to userspace for submitting jobs
to the DSP cores and managing the input, output and intermediate memory.
Signed-off-by: Tomeu Vizoso <tomeu@...euvizoso.net>
---
Documentation/accel/thames/index.rst | 28 +++++
MAINTAINERS | 9 ++
drivers/accel/Kconfig | 1 +
drivers/accel/Makefile | 3 +-
drivers/accel/thames/Kconfig | 26 +++++
drivers/accel/thames/Makefile | 9 ++
drivers/accel/thames/thames_core.c | 155 ++++++++++++++++++++++++++
drivers/accel/thames/thames_core.h | 53 +++++++++
drivers/accel/thames/thames_device.c | 93 ++++++++++++++++
drivers/accel/thames/thames_device.h | 46 ++++++++
drivers/accel/thames/thames_drv.c | 156 +++++++++++++++++++++++++++
drivers/accel/thames/thames_drv.h | 21 ++++
drivers/accel/thames/thames_ipc.h | 204 +++++++++++++++++++++++++++++++++++
drivers/accel/thames/thames_rpmsg.c | 155 ++++++++++++++++++++++++++
drivers/accel/thames/thames_rpmsg.h | 27 +++++
15 files changed, 985 insertions(+), 1 deletion(-)
diff --git a/Documentation/accel/thames/index.rst b/Documentation/accel/thames/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ca8391031f226f7ef1dc210a356c86acbe126c6f
--- /dev/null
+++ b/Documentation/accel/thames/index.rst
@@ -0,0 +1,28 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+============================================================
+ accel/thames Driver for the C7x DSPs from Texas Instruments
+============================================================
+
+The accel/thames driver supports the C7x DSPs inside some Texas Instruments SoCs
+such as the J722S. These can be used as accelerators for various workloads,
+including machine learning inference.
+
+This driver controls the power state of the hardware via :doc:`remoteproc </staging/remoteproc>`
+and communicates with the firmware running on the DSP via :doc:`rpmsg_virtio </staging/rpmsg_virtio>`.
+The kernel driver itself allocates buffers, manages contexts, and submits jobs
+to the DSP firmware. Buffers are mapped by the DSP itself using its MMU,
+providing memory isolation among different clients.
+
+The source code for the firmware running on the DSP is available at:
+https://gitlab.freedesktop.org/tomeu/thames_firmware/.
+
+Everything else is done in userspace, as a Gallium driver (also called thames)
+that is part of the Mesa3D project: https://docs.mesa3d.org/teflon.html
+
+If there is more than one core that advertises the same rpmsg_virtio service
+name, the driver will load balance jobs between them with drm-gpu-scheduler.
+
+Hardware currently supported:
+
+* J722S
diff --git a/MAINTAINERS b/MAINTAINERS
index dc731d37c8feeff25613c59fe9c929927dadaa7e..a3fc809c797269d0792dfe5202cc1b49f6ff57e9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7731,6 +7731,15 @@ F: Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
F: drivers/accel/rocket/
F: include/uapi/drm/rocket_accel.h
+DRM ACCEL DRIVER FOR TI C7x DSPS
+M: Tomeu Vizoso <tomeu@...euvizoso.net>
+L: dri-devel@...ts.freedesktop.org
+S: Supported
+T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F: Documentation/accel/thames/
+F: drivers/accel/thames/
+F: include/uapi/drm/thames_accel.h
+
DRM COMPUTE ACCELERATORS DRIVERS AND FRAMEWORK
M: Oded Gabbay <ogabbay@...nel.org>
L: dri-devel@...ts.freedesktop.org
diff --git a/drivers/accel/Kconfig b/drivers/accel/Kconfig
index bdf48ccafcf21b2fd685ec963e39e256196e6e17..cb49c71cd4e4a4220624f7041a75ba950a1a2ee1 100644
--- a/drivers/accel/Kconfig
+++ b/drivers/accel/Kconfig
@@ -30,5 +30,6 @@ source "drivers/accel/habanalabs/Kconfig"
source "drivers/accel/ivpu/Kconfig"
source "drivers/accel/qaic/Kconfig"
source "drivers/accel/rocket/Kconfig"
+source "drivers/accel/thames/Kconfig"
endif
diff --git a/drivers/accel/Makefile b/drivers/accel/Makefile
index 1d3a7251b950f39e2ae600a2fc07a3ef7e41831e..8472989cbe22746f1e7292d2401fa0f7424a6c15 100644
--- a/drivers/accel/Makefile
+++ b/drivers/accel/Makefile
@@ -5,4 +5,5 @@ obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) += ethosu/
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
+obj-$(CONFIG_DRM_ACCEL_ROCKET) += rocket/
+obj-$(CONFIG_DRM_ACCEL_THAMES) += thames/
\ No newline at end of file
diff --git a/drivers/accel/thames/Kconfig b/drivers/accel/thames/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..50e0b6ac2a16a942ba8463333991f5b0161b99ac
--- /dev/null
+++ b/drivers/accel/thames/Kconfig
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_ACCEL_THAMES
+ tristate "Thames (support for TI C7x DSP accelerators)"
+ depends on DRM_ACCEL
+ depends on TI_K3_R5_REMOTEPROC || COMPILE_TEST
+ depends on RPMSG
+ depends on MMU
+ select DRM_SCHED
+ select DRM_GEM_SHMEM_HELPER
+ help
+ Choose this option if you have a Texas Instruments SoC that contains
+ C7x DSP cores that can be used as compute accelerators. This includes
+ SoCs such as the AM62A, J721E, J721S2, and J784S4.
+
+ The C7x DSP cores can be used for general-purpose compute acceleration
+ and are exposed through the DRM accel subsystem.
+
+ The interface exposed to userspace is described in
+ include/uapi/drm/thames_accel.h and is used by the Thames userspace
+ driver in Mesa3D.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called thames.
diff --git a/drivers/accel/thames/Makefile b/drivers/accel/thames/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..7ccd8204f0f5ea800f30e84b319f355be948109d
--- /dev/null
+++ b/drivers/accel/thames/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_DRM_ACCEL_THAMES) := thames.o
+
+thames-y := \
+ thames_core.o \
+ thames_device.o \
+ thames_drv.o \
+ thames_rpmsg.o
diff --git a/drivers/accel/thames/thames_core.c b/drivers/accel/thames/thames_core.c
new file mode 100644
index 0000000000000000000000000000000000000000..92af1d68063116bcfa28a33960cbe829029fc1bf
--- /dev/null
+++ b/drivers/accel/thames/thames_core.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+
+#include "linux/remoteproc.h"
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/jiffies.h>
+#include <linux/rpmsg.h>
+
+#include "thames_core.h"
+#include "thames_device.h"
+#include "thames_rpmsg.h"
+
+/* Shift to convert bytes to megabytes (divide by 1048576) */
+#define THAMES_BYTES_TO_MB_SHIFT 20
+
+int thames_core_get_iova_range(struct rpmsg_device *rpdev, u64 *iova_start, u64 *iova_size)
+{
+ struct rproc *rproc;
+ struct device_node *of_node;
+ struct device_node *mem_node;
+ struct resource mem_res;
+ int err;
+
+ if (!iova_start || !iova_size)
+ return -EINVAL;
+
+ rproc = rproc_get_by_child(&rpdev->dev);
+ if (!rproc) {
+ dev_err(&rpdev->dev, "Failed to get rproc device\n");
+ return -ENODEV;
+ }
+
+ of_node = rproc->dev.parent->of_node;
+ put_device(&rproc->dev);
+
+ if (!of_node) {
+ dev_err(&rpdev->dev, "No device tree node found on rproc parent\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Read the IOVA pool range from the device tree node.
+ * The third memory-region (index 2) defines the virtual address range.
+ * The first two regions are typically:
+ * [0] = DMA memory region for remoteproc (physically contiguous)
+ * [1] = Code/data memory region for remoteproc (physically contiguous)
+ * [2] = Virtual address pool for BO mappings (firmware-managed MMU)
+ */
+ mem_node = of_parse_phandle(of_node, "memory-region", 2);
+ if (!mem_node) {
+ dev_err(&rpdev->dev, "Missing third memory-region (DSP VA pool) in device tree\n");
+ return -EINVAL;
+ }
+
+ err = of_address_to_resource(mem_node, 0, &mem_res);
+ of_node_put(mem_node);
+ if (err) {
+ dev_err(&rpdev->dev, "Failed to get DSP VA pool range from memory-region[2]: %d\n",
+ err);
+ return err;
+ }
+
+ *iova_start = mem_res.start;
+ *iova_size = resource_size(&mem_res);
+
+ if (!*iova_size) {
+ dev_err(&rpdev->dev, "Invalid DSP VA pool size: 0\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int thames_core_validate_iova_range(struct thames_core *core)
+{
+ struct thames_device *tdev = core->tdev;
+ u64 iova_start, iova_size;
+ int err;
+
+ err = thames_core_get_iova_range(core->rpdev, &iova_start, &iova_size);
+ if (err)
+ return err;
+
+ if (iova_start != tdev->iova_start || iova_size != tdev->iova_size) {
+ dev_err(core->dev,
+ "Core %d IOVA range mismatch! Expected 0x%llx-0x%llx, got 0x%llx-0x%llx\n",
+ core->index, tdev->iova_start, tdev->iova_start + tdev->iova_size - 1,
+ iova_start, iova_start + iova_size - 1);
+ dev_err(core->dev,
+ "All cores must have the same memory-region[2] (IOVA pool) in device tree\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int thames_core_init(struct thames_core *core)
+{
+ int err = 0;
+
+ err = thames_core_validate_iova_range(core);
+ if (err)
+ return err;
+
+ err = thames_rpmsg_init(core);
+ if (err)
+ return err;
+
+ err = thames_rpmsg_ping_test(core);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+void thames_core_fini(struct thames_core *core)
+{
+ thames_rpmsg_fini(core);
+}
+
+void thames_core_reset(struct thames_core *core)
+{
+ struct rpmsg_device *rpdev = core->rpdev;
+ struct rproc *rproc;
+ int ret;
+
+ dev_warn(core->dev, "Resetting DSP core %d", core->index);
+
+ if (!atomic_read(&core->reset.pending))
+ dev_warn(core->dev, "Reset called without reset.pending set\n");
+
+ rproc = rproc_get_by_child(&rpdev->dev);
+ if (!rproc) {
+ dev_err(core->dev, "Failed to get rproc for reset\n");
+ return;
+ }
+
+ ret = rproc_shutdown(rproc);
+ if (ret) {
+ dev_err(&rproc->dev, "Failed to shut down DSP: %d\n", ret);
+ goto put_rproc;
+ }
+
+ ret = rproc_boot(rproc);
+ if (ret)
+ dev_err(&rproc->dev, "Failed to boot DSP: %d\n", ret);
+
+put_rproc:
+ put_device(&rproc->dev);
+}
diff --git a/drivers/accel/thames/thames_core.h b/drivers/accel/thames/thames_core.h
new file mode 100644
index 0000000000000000000000000000000000000000..72c3d3d6c575f56cc1d8731d1c9dc958486dbf7f
--- /dev/null
+++ b/drivers/accel/thames/thames_core.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+
+#ifndef __THAMES_CORE_H__
+#define __THAMES_CORE_H__
+
+#include <linux/rpmsg.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/mutex_types.h>
+#include <linux/completion.h>
+
+struct thames_msg_buffer_op;
+
+struct thames_core {
+ struct rpmsg_device *rpdev;
+ struct device *dev;
+ struct thames_device *tdev;
+ unsigned int index;
+
+ /* RPMSG communication context */
+ struct {
+ struct rpmsg_endpoint *endpoint;
+
+ struct {
+ u32 sequence;
+ u32 expected_data;
+ bool success;
+ struct completion completion;
+ } ping_test;
+ } rpmsg_ctx;
+
+ struct mutex job_lock;
+ struct thames_job *in_flight_job;
+
+ spinlock_t fence_lock;
+
+ struct {
+ struct workqueue_struct *wq;
+ struct work_struct work;
+ atomic_t pending;
+ } reset;
+
+ struct drm_gpu_scheduler sched;
+ u64 fence_context;
+ u64 emit_seqno;
+};
+
+int thames_core_init(struct thames_core *core);
+void thames_core_fini(struct thames_core *core);
+void thames_core_reset(struct thames_core *core);
+int thames_core_get_iova_range(struct rpmsg_device *rpdev, u64 *iova_start, u64 *iova_size);
+
+#endif
diff --git a/drivers/accel/thames/thames_device.c b/drivers/accel/thames/thames_device.c
new file mode 100644
index 0000000000000000000000000000000000000000..2b2aa32b07ee361ea388ab5ec781a13ff4359e5f
--- /dev/null
+++ b/drivers/accel/thames/thames_device.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+
+#include <drm/drm_drv.h>
+#include <linux/array_size.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/idr.h>
+#include <linux/platform_device.h>
+
+#include "thames_device.h"
+
+/* Shift to convert bytes to megabytes (divide by 1048576) */
+#define THAMES_BYTES_TO_MB_SHIFT 20
+
+struct thames_device *thames_device_init(struct platform_device *pdev,
+
+ const struct drm_driver *thames_drm_driver, u64 iova_start,
+ u64 iova_size)
+{
+ struct device *dev = &pdev->dev;
+ struct thames_device *tdev;
+ struct drm_device *ddev;
+ int err;
+
+ tdev = devm_drm_dev_alloc(dev, thames_drm_driver, struct thames_device, ddev);
+ if (IS_ERR(tdev))
+ return tdev;
+
+ tdev->num_cores = 0;
+ ddev = &tdev->ddev;
+ dev_set_drvdata(dev, tdev);
+
+ dma_set_max_seg_size(dev, UINT_MAX);
+
+ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+ if (err)
+ return ERR_PTR(err);
+
+ err = devm_mutex_init(dev, &tdev->sched_lock);
+ if (err)
+ return ERR_PTR(-ENOMEM);
+
+ ida_init(&tdev->bo_ida);
+ ida_init(&tdev->ctx_ida);
+ ida_init(&tdev->job_ida);
+ ida_init(&tdev->ipc_seq_ida);
+
+ /*
+ * Initialize shared virtual address space for all DSP cores.
+ *
+ * IMPORTANT: This driver does NOT use Linux IOMMU. The TI C7x DSP cores
+ * have their own MMUs that are managed entirely by the DSP firmware.
+ * The VA space is shared across all cores - userspace receives VAs that
+ * work on all cores. Each core's firmware programs its own MMU to map
+ * the same VA to the same PA.
+ *
+ * The Linux driver's role is only to:
+ * 1. Allocate non-overlapping virtual addresses from a safe range
+ * 2. Provide physical addresses to each DSP firmware via IPC
+ * 3. Let each firmware program its own MMU to map VA -> PA
+ */
+ if (!iova_size) {
+ dev_err(dev, "Invalid DSP VA pool size: 0\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ tdev->iova_start = iova_start;
+ tdev->iova_size = iova_size;
+
+ drm_mm_init(&tdev->mm, iova_start, iova_size);
+ err = devm_mutex_init(dev, &tdev->mm_lock);
+ if (err)
+ return ERR_PTR(-ENOMEM);
+
+ err = drm_dev_register(ddev, 0);
+ if (err)
+ return ERR_PTR(err);
+
+ return tdev;
+}
+
+void thames_device_fini(struct thames_device *tdev)
+{
+ WARN_ON(tdev->num_cores > 0);
+
+ ida_destroy(&tdev->bo_ida);
+ ida_destroy(&tdev->ctx_ida);
+ ida_destroy(&tdev->job_ida);
+ ida_destroy(&tdev->ipc_seq_ida);
+ drm_mm_takedown(&tdev->mm);
+ drm_dev_unregister(&tdev->ddev);
+}
diff --git a/drivers/accel/thames/thames_device.h b/drivers/accel/thames/thames_device.h
new file mode 100644
index 0000000000000000000000000000000000000000..c7d8e521d4323122134e8c8e8d256d957c89ae5f
--- /dev/null
+++ b/drivers/accel/thames/thames_device.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+
+#ifndef __THAMES_DEVICE_H__
+#define __THAMES_DEVICE_H__
+
+#include <drm/drm_device.h>
+#include <drm/drm_mm.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/idr.h>
+#include <linux/platform_device.h>
+
+#include "thames_core.h"
+
+#define MAX_CORES 8
+
+struct thames_device {
+ struct drm_device ddev;
+
+ struct mutex sched_lock;
+
+ struct thames_core cores[MAX_CORES];
+ unsigned int num_cores;
+
+ struct ida bo_ida;
+ struct ida ctx_ida;
+ struct ida job_ida;
+ struct ida ipc_seq_ida;
+
+ struct drm_mm mm;
+ struct mutex mm_lock;
+
+ u64 iova_start;
+ u64 iova_size;
+};
+
+struct thames_device *thames_device_init(struct platform_device *pdev,
+ const struct drm_driver *thames_drm_driver, u64 iova_start,
+ u64 iova_size);
+void thames_device_fini(struct thames_device *rdev);
+
+#define to_thames_device(drm_dev) \
+ ((struct thames_device *)(container_of((drm_dev), struct thames_device, ddev)))
+
+#endif /* __THAMES_DEVICE_H__ */
diff --git a/drivers/accel/thames/thames_drv.c b/drivers/accel/thames/thames_drv.c
new file mode 100644
index 0000000000000000000000000000000000000000..a288e6ef05d7b1a21741ac0ca9cc8981f33969e4
--- /dev/null
+++ b/drivers/accel/thames/thames_drv.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+
+#include <drm/drm_accel.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_ioctl.h>
+#include <drm/thames_accel.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+#include <linux/rpmsg.h>
+
+#include "thames_drv.h"
+#include "thames_core.h"
+#include "thames_ipc.h"
+
+static struct platform_device *drm_dev;
+static struct thames_device *tdev;
+
+static int thames_open(struct drm_device *dev, struct drm_file *file)
+{
+ struct thames_device *tdev = to_thames_device(dev);
+ struct thames_file_priv *thames_priv;
+ int ret;
+
+ if (!try_module_get(THIS_MODULE))
+ return -EINVAL;
+
+ thames_priv = kzalloc(sizeof(*thames_priv), GFP_KERNEL);
+ if (!thames_priv) {
+ ret = -ENOMEM;
+ goto err_put_mod;
+ }
+
+ thames_priv->tdev = tdev;
+
+ file->driver_priv = thames_priv;
+
+ return 0;
+
+err_put_mod:
+ module_put(THIS_MODULE);
+ return ret;
+}
+
+static void thames_postclose(struct drm_device *dev, struct drm_file *file)
+{
+ struct thames_file_priv *thames_priv = file->driver_priv;
+
+ kfree(thames_priv);
+ module_put(THIS_MODULE);
+}
+
+static const struct drm_ioctl_desc thames_drm_driver_ioctls[] = {
+#define THAMES_IOCTL(n, func) DRM_IOCTL_DEF_DRV(THAMES_##n, thames_ioctl_##func, 0)
+
+};
+
+DEFINE_DRM_ACCEL_FOPS(thames_accel_driver_fops);
+
+static const struct drm_driver thames_drm_driver = {
+ .driver_features = DRIVER_COMPUTE_ACCEL | DRIVER_GEM,
+ .open = thames_open,
+ .postclose = thames_postclose,
+ .ioctls = thames_drm_driver_ioctls,
+ .num_ioctls = ARRAY_SIZE(thames_drm_driver_ioctls),
+ .fops = &thames_accel_driver_fops,
+ .name = "thames",
+ .desc = "thames DRM",
+};
+
+static int thames_probe(struct rpmsg_device *rpdev)
+{
+ u64 iova_start, iova_size;
+ unsigned int core;
+ int err;
+
+ if (!tdev) {
+ err = thames_core_get_iova_range(rpdev, &iova_start, &iova_size);
+ if (err)
+ return err;
+
+ tdev = thames_device_init(drm_dev, &thames_drm_driver, iova_start, iova_size);
+ if (IS_ERR(tdev)) {
+ dev_err(&rpdev->dev, "failed to initialize thames device\n");
+ return PTR_ERR(tdev);
+ }
+ }
+
+ core = tdev->num_cores;
+
+ tdev->cores[core].tdev = tdev;
+ tdev->cores[core].rpdev = rpdev;
+ tdev->cores[core].dev = &rpdev->dev;
+ tdev->cores[core].index = core;
+
+ tdev->num_cores++;
+
+ return thames_core_init(&tdev->cores[core]);
+}
+
+static void thames_remove(struct rpmsg_device *rpdev)
+{
+ unsigned int core;
+
+ for (core = 0; core < tdev->num_cores; core++) {
+ if (tdev->cores[core].rpdev == rpdev) {
+ thames_core_fini(&tdev->cores[core]);
+ tdev->num_cores--;
+ break;
+ }
+ }
+
+ if (!tdev->num_cores) {
+ thames_device_fini(tdev);
+ tdev = NULL;
+ }
+}
+
+static const struct rpmsg_device_id thames_rpmsg_id_table[] = { { .name = THAMES_SERVICE_NAME },
+ {} };
+
+static struct rpmsg_driver thames_rpmsg_driver = {
+ .drv = {
+ .name = "thames",
+ .owner = THIS_MODULE,
+ },
+ .id_table = thames_rpmsg_id_table,
+ .probe = thames_probe,
+ .remove = thames_remove,
+};
+
+static int __init thames_register(void)
+{
+ drm_dev = platform_device_register_simple("thames", -1, NULL, 0);
+ if (IS_ERR(drm_dev))
+ return PTR_ERR(drm_dev);
+
+ return register_rpmsg_driver(&thames_rpmsg_driver);
+}
+
+static void __exit thames_unregister(void)
+{
+ unregister_rpmsg_driver(&thames_rpmsg_driver);
+
+ platform_device_unregister(drm_dev);
+}
+
+module_init(thames_register);
+module_exit(thames_unregister);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DRM driver for Texas Instrument's C7x accelerator cores");
+MODULE_AUTHOR("Tomeu Vizoso");
+MODULE_ALIAS("rpmsg:" THAMES_SERVICE_NAME);
diff --git a/drivers/accel/thames/thames_drv.h b/drivers/accel/thames/thames_drv.h
new file mode 100644
index 0000000000000000000000000000000000000000..e03203eab8b88686ca91c10b45e55df1ea3d2e77
--- /dev/null
+++ b/drivers/accel/thames/thames_drv.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+
+#ifndef __THAMES_DRV_H__
+#define __THAMES_DRV_H__
+
+#include <drm/drm_mm.h>
+#include <drm/gpu_scheduler.h>
+
+#include "thames_device.h"
+
+struct thames_file_priv {
+ struct thames_device *tdev;
+
+ struct drm_sched_entity sched_entity;
+
+ u32 context_id;
+ bool context_valid;
+};
+
+#endif
diff --git a/drivers/accel/thames/thames_ipc.h b/drivers/accel/thames/thames_ipc.h
new file mode 100644
index 0000000000000000000000000000000000000000..60297b4bc2ffd990315cb735a96a23429d390f43
--- /dev/null
+++ b/drivers/accel/thames/thames_ipc.h
@@ -0,0 +1,204 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * This header defines the RPMSG message structures exchanged between
+ * the Linux kernel (host) and the C7x DSP (remote) firmware for the
+ * Thames DRM/accel driver.
+ */
+
+#ifndef _THAMES_IPC_H
+#define _THAMES_IPC_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <stdint.h>
+typedef uint8_t __u8;
+typedef uint16_t __u16;
+typedef uint32_t __u32;
+typedef uint64_t __u64;
+#endif
+
+#define THAMES_SERVICE_NAME "thames-service"
+
+/**
+ * @THAMES_MSG_TYPE: Simplified message type enumeration
+ */
+enum thames_msg_type {
+ /* --- Host (Kernel) -> Remote (DSP) --- */
+ THAMES_MSG_PING = 0x100, /* Ping message to test communication */
+ THAMES_MSG_CONTEXT_OP, /* Create/destroy context */
+ THAMES_MSG_BO_OP, /* Map/unmap buffer objects */
+ THAMES_MSG_SUBMIT_JOB, /* Submit job for execution */
+
+ /* --- Remote (DSP) -> Host (Kernel) --- */
+ THAMES_MSG_PING_RESPONSE = 0x200,
+ THAMES_MSG_CONTEXT_OP_RESPONSE,
+ THAMES_MSG_BO_OP_RESPONSE,
+ THAMES_MSG_SUBMIT_JOB_RESPONSE,
+};
+
+/**
+ * @THAMES_CONTEXT_OP: Context operation types
+ */
+enum thames_context_op {
+ THAMES_CONTEXT_CREATE = 0,
+ THAMES_CONTEXT_DESTROY,
+};
+
+/**
+ * @THAMES_BO_OP: Buffer Object operation types
+ */
+enum thames_bo_op {
+ THAMES_BO_MAP = 0,
+ THAMES_BO_UNMAP,
+};
+
+/**
+ * @THAMES_RESP_STATUS: Response status codes
+ */
+enum thames_resp_status {
+ THAMES_RESP_SUCCESS = 0,
+ THAMES_RESP_ERR_GENERIC = 1,
+ THAMES_RESP_ERR_NOMEM = 2,
+ THAMES_RESP_ERR_INVAL = 3,
+ THAMES_RESP_ERR_NO_CTX = 4,
+ THAMES_RESP_ERR_MMU = 5,
+ THAMES_RESP_ERR_JOB_TIMEOUT = 6,
+};
+
+/**
+ * struct thames_msg_hdr - Common header for all RPMSG messages
+ * @type: Message type from enum thames_msg_type
+ * @seq: Sequence number for request/response matching
+ * @len: Total message length including header
+ */
+struct thames_msg_hdr {
+ __u32 type;
+ __u32 seq;
+ __u32 len;
+ __u32 reserved;
+};
+
+/*
+ * ===================================================================
+ * Host (Kernel) -> Remote (DSP) Messages
+ * ===================================================================
+ */
+
+/**
+ * struct thames_msg_ping - Ping message to test communication
+ * @hdr: Common message header
+ * @ping_data: Optional ping data (timestamp, sequence, etc.)
+ */
+struct thames_msg_ping {
+ struct thames_msg_hdr hdr;
+ __u32 ping_data;
+};
+
+/**
+ * struct thames_msg_context_op - Context create/destroy operations
+ * @hdr: Common message header
+ * @op: Operation type (CREATE/DESTROY)
+ * @context_id: Context ID
+ */
+struct thames_msg_context_op {
+ struct thames_msg_hdr hdr;
+ uint32_t op; /* enum thames_context_op */
+ uint32_t context_id;
+};
+
+/**
+ * struct thames_msg_bo_op - Buffer Object map/unmap operations
+ * @hdr: Common message header
+ * @op: Operation type (MAP/UNMAP)
+ * @context_id: Context ID that this BO belongs to
+ * @bo_id: Buffer Object ID for tracking
+ * @vaddr: Virtual address where BO should be mapped on DSP
+ * @paddr: Physical address of the BO
+ * @size: Size of the BO in bytes
+ */
+struct thames_msg_bo_op {
+ struct thames_msg_hdr hdr;
+ uint32_t op; /* enum thames_bo_op */
+ uint32_t context_id;
+ uint32_t bo_id;
+ uint64_t vaddr;
+ uint64_t paddr;
+ uint64_t size;
+};
+
+/**
+ * struct thames_msg_submit_job - Submit job for execution
+ * @hdr: Common message header
+ * @context_id: Context to run job in
+ * @job_id: Host-generated job tracking ID
+ * @kernel_iova: IOVA of kernel code BO (first byte = first instruction)
+ * @kernel_size: Size of kernel code in bytes
+ * @args_iova: IOVA of arguments BO (array of uint64_t values)
+ * @args_size: Size of arguments BO in bytes
+ */
+struct thames_msg_submit_job {
+ struct thames_msg_hdr hdr;
+ uint32_t context_id;
+ uint32_t job_id;
+ uint64_t kernel_iova;
+ uint64_t kernel_size;
+ uint64_t args_iova;
+ uint64_t args_size;
+};
+
+/*
+ * ===================================================================
+ * Remote (DSP) -> Host (Kernel) Messages
+ * ===================================================================
+ */
+
+/**
+ * struct thames_msg_response - Generic response to commands
+ * @hdr: Common message header (seq matches request)
+ * @status: Status code from enum thames_resp_status
+ * @data: Optional response data (context-dependent)
+ */
+struct thames_msg_response {
+ struct thames_msg_hdr hdr;
+ uint32_t status;
+ uint32_t data;
+};
+
+/*
+ * ===================================================================
+ * Buffer Size Calculations
+ * ===================================================================
+ */
+
+/* Calculate the maximum message size by finding the largest structure */
+#define THAMES_MSG_SIZE_PING sizeof(struct thames_msg_ping)
+#define THAMES_MSG_SIZE_CONTEXT_OP sizeof(struct thames_msg_context_op)
+#define THAMES_MSG_SIZE_BO_OP sizeof(struct thames_msg_bo_op)
+#define THAMES_MSG_SIZE_SUBMIT_JOB sizeof(struct thames_msg_submit_job)
+#define THAMES_MSG_SIZE_RESPONSE sizeof(struct thames_msg_response)
+
+/* Helper macros to find maximum of multiple values */
+#define THAMES_MAX2(a, b) ((a) > (b) ? (a) : (b))
+#define THAMES_MAX3(a, b, c) THAMES_MAX2(THAMES_MAX2(a, b), c)
+#define THAMES_MAX5(a, b, c, d, e) THAMES_MAX2(THAMES_MAX3(a, b, c), THAMES_MAX2(d, e))
+
+/* Maximum size of any Thames IPC message */
+#define THAMES_IPC_MAX_MSG_SIZE \
+ THAMES_MAX5(THAMES_MSG_SIZE_PING, THAMES_MSG_SIZE_CONTEXT_OP, THAMES_MSG_SIZE_BO_OP, \
+ THAMES_MSG_SIZE_SUBMIT_JOB, THAMES_MSG_SIZE_RESPONSE)
+
+/* RPMSG buffer size - should accommodate largest message + some padding */
+#define THAMES_RPMSG_BUFFER_SIZE ((THAMES_IPC_MAX_MSG_SIZE + 15) & ~15) /* 16-byte aligned */
+
+/* Compile-time size checks - use BUILD_BUG_ON in kernel code */
+#ifdef __KERNEL__
+#define THAMES_ASSERT_MSG_SIZE(msg_type) BUILD_BUG_ON(sizeof(struct msg_type) > 64)
+#else
+#define THAMES_ASSERT_MSG_SIZE(msg_type) \
+ _Static_assert(sizeof(struct msg_type) <= 64, #msg_type " too large")
+#endif
+
+#endif /* _THAMES_IPC_H */
diff --git a/drivers/accel/thames/thames_rpmsg.c b/drivers/accel/thames/thames_rpmsg.c
new file mode 100644
index 0000000000000000000000000000000000000000..ebc34f49353e5e7959734da8e8a935573c130e79
--- /dev/null
+++ b/drivers/accel/thames/thames_rpmsg.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/jiffies.h>
+#include <linux/rpmsg.h>
+
+#include "thames_rpmsg.h"
+#include "thames_core.h"
+#include "thames_device.h"
+#include "thames_ipc.h"
+
+#define THAMES_PING_TEST_PATTERN 0xDEADBEEF
+#define THAMES_PING_TIMEOUT_MS 5000
+
+static int thames_rpmsg_callback(struct rpmsg_device *rpdev, void *data, int len, void *priv,
+ u32 src)
+{
+ struct thames_msg_hdr *hdr = (struct thames_msg_hdr *)data;
+ struct thames_core *core = priv;
+
+ dev_dbg(&rpdev->dev, "Received response on core %d with length %d\n", core->index, len);
+
+ if (len < sizeof(struct thames_msg_hdr)) {
+ dev_err(&rpdev->dev, "Received message too short: %d bytes", len);
+ return -EINVAL;
+ }
+
+ switch (hdr->type) {
+ case THAMES_MSG_PING_RESPONSE: {
+ struct thames_msg_response *response = (struct thames_msg_response *)data;
+
+ dev_dbg(&rpdev->dev,
+ "Received PING response: status=%u, data=0x%x, expected_data=0x%x, seq=%u, expected_seq=%u\n",
+ response->status, response->data, core->rpmsg_ctx.ping_test.expected_data,
+ hdr->seq, core->rpmsg_ctx.ping_test.sequence);
+
+ if (hdr->seq != core->rpmsg_ctx.ping_test.sequence) {
+ dev_err(&rpdev->dev,
+ "PING response sequence mismatch: got %u, expected %u\n", hdr->seq,
+ core->rpmsg_ctx.ping_test.sequence);
+ ida_free(&core->tdev->ipc_seq_ida, hdr->seq);
+ return -EINVAL;
+ }
+
+ if (response->data != core->rpmsg_ctx.ping_test.expected_data) {
+ dev_err(&rpdev->dev,
+ "PING response data mismatch: got 0x%x, expected 0x%x\n",
+ response->data, core->rpmsg_ctx.ping_test.expected_data);
+ core->rpmsg_ctx.ping_test.success = false;
+ complete(&core->rpmsg_ctx.ping_test.completion);
+ ida_free(&core->tdev->ipc_seq_ida, hdr->seq);
+ return -EINVAL;
+ }
+
+ core->rpmsg_ctx.ping_test.success = (response->status == THAMES_RESP_SUCCESS);
+ complete(&core->rpmsg_ctx.ping_test.completion);
+
+ ida_free(&core->tdev->ipc_seq_ida, hdr->seq);
+
+ break;
+ }
+
+ default:
+ dev_warn(&rpdev->dev, "Unknown message type: %u\n", hdr->type);
+ break;
+ }
+
+ return 0;
+}
+
+static int thames_rpmsg_send_raw(struct thames_core *core, const void *data, size_t len)
+{
+ if (!core->rpmsg_ctx.endpoint) {
+ dev_err(core->dev, "RPMSG endpoint not available");
+ return -ENODEV;
+ }
+
+ return rpmsg_send(core->rpmsg_ctx.endpoint, (void *)data, len);
+}
+
+int thames_rpmsg_init(struct thames_core *core)
+{
+ struct rpmsg_device *rpdev = core->rpdev;
+ struct rpmsg_channel_info chinfo = {};
+
+ strscpy(chinfo.name, rpdev->id.name, sizeof(chinfo.name));
+ chinfo.src = RPMSG_ADDR_ANY; /* Let rpmsg assign an address */
+ chinfo.dst = RPMSG_ADDR_ANY;
+
+ core->rpmsg_ctx.endpoint = rpmsg_create_ept(rpdev, thames_rpmsg_callback, core, chinfo);
+ if (!core->rpmsg_ctx.endpoint) {
+ dev_err(core->dev, "Failed to create RPMSG endpoint for core %d", core->index);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+void thames_rpmsg_fini(struct thames_core *core)
+{
+ if (core->rpmsg_ctx.endpoint) {
+ rpmsg_destroy_ept(core->rpmsg_ctx.endpoint);
+ core->rpmsg_ctx.endpoint = NULL;
+ }
+}
+
+int thames_rpmsg_send_ping(struct thames_core *core, u32 ping_data, u32 *sequence)
+{
+ struct thames_msg_ping ping_msg = {};
+
+ ping_msg.hdr.type = THAMES_MSG_PING;
+ ping_msg.hdr.seq = ida_alloc(&core->tdev->ipc_seq_ida, GFP_KERNEL);
+ ping_msg.hdr.len = sizeof(ping_msg);
+ ping_msg.hdr.reserved = 0;
+ ping_msg.ping_data = ping_data;
+
+ *sequence = ping_msg.hdr.seq;
+
+ return thames_rpmsg_send_raw(core, &ping_msg, sizeof(ping_msg));
+}
+
+int thames_rpmsg_ping_test(struct thames_core *core)
+{
+ const u32 test_data = THAMES_PING_TEST_PATTERN;
+ int ret;
+ unsigned long timeout;
+
+ core->rpmsg_ctx.ping_test.expected_data = test_data;
+ core->rpmsg_ctx.ping_test.success = false;
+ init_completion(&core->rpmsg_ctx.ping_test.completion);
+
+ ret = thames_rpmsg_send_ping(core, test_data, &core->rpmsg_ctx.ping_test.sequence);
+ if (ret) {
+ dev_err(core->dev, "Failed to send PING message to core %d: %d", core->index, ret);
+ return ret;
+ }
+
+ timeout = msecs_to_jiffies(THAMES_PING_TIMEOUT_MS);
+ ret = wait_for_completion_timeout(&core->rpmsg_ctx.ping_test.completion, timeout);
+ if (ret == 0) {
+ dev_err(core->dev, "PING test timed out - DSP core %d not responding", core->index);
+ return -ETIMEDOUT;
+ }
+
+ if (!core->rpmsg_ctx.ping_test.success) {
+ dev_err(core->dev, "PING test failed - incorrect PONG response from DSP core %d",
+ core->index);
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/drivers/accel/thames/thames_rpmsg.h b/drivers/accel/thames/thames_rpmsg.h
new file mode 100644
index 0000000000000000000000000000000000000000..6d5195453b8d3eac2c333b7ac03e469b2744fb78
--- /dev/null
+++ b/drivers/accel/thames/thames_rpmsg.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+
+#ifndef __THAMES_RPMSG_H__
+#define __THAMES_RPMSG_H__
+
+#include <linux/completion.h>
+#include <linux/rpmsg.h>
+
+struct thames_core;
+
+int thames_rpmsg_init(struct thames_core *core);
+void thames_rpmsg_fini(struct thames_core *core);
+
+int thames_rpmsg_send_ping(struct thames_core *core, u32 ping_data, u32 *sequence);
+int thames_rpmsg_send_create_context(struct thames_core *core, u32 context_id);
+int thames_rpmsg_send_destroy_context(struct thames_core *core, u32 context_id);
+int thames_rpmsg_send_map_bo(struct thames_core *core, u32 context_id, u32 bo_id, u64 vaddr,
+ u64 paddr, u64 size);
+int thames_rpmsg_send_unmap_bo(struct thames_core *core, u32 context_id, u32 bo_id);
+int thames_rpmsg_send_submit_job(struct thames_core *core, u32 context_id, u32 job_id,
+ u64 kernel_iova, u64 kernel_size, u64 args_iova, u64 args_size,
+ u32 *sequence);
+
+int thames_rpmsg_ping_test(struct thames_core *core);
+
+#endif /* __THAMES_RPMSG_H__ */
--
2.52.0
Powered by blists - more mailing lists