[PATCH v4 03/25] accel/qda: Add compute context bank devices

From: Ekansh Gupta

Date: Sun Nov 01 2026 - 17:41:32 EST


The DSP does not access memory through a single IOMMU domain. Its
device tree node describes a set of context banks, one
"qcom,fastrpc-compute-cb" child node each, and every context bank has
its own SMMU address space. Later patches give each session a context
bank of its own so that sessions cannot reach each other's buffers, but
that needs a struct device per context bank to allocate from.

Create a platform device for each context bank node when the rpmsg
channel probes, and destroy them again when the channel goes away or
probe fails. Registering the device is what gives it an IOMMU group
and domain: the IOMMU core probes it from the platform bus notifier
when it is added, so no driver needs to bind to it. The devices are
created before the DRM device is registered, so user space can never
reach a context bank that does not exist yet. They are destroyed after
drm_dev_unplug(), which makes sure no ioctl is still running against
them.

The width of the address space the DSP can reach differs between SoCs
and DSPs: the CDSP on Kaanapali and the platforms that use it as their
fallback compatible can address 34 bits, everything else 32. Carry this
in struct qda_soc_data, selected through the OF match data of the two
compatibles the binding defines, and apply it to each context bank with
dma_set_mask(). All CDSP instances (cdsp, cdsp1, ...) share the CDSP
width, which is why the DSP name is matched by prefix. The remaining
per-SoC parameters are added together with the code that uses them.

Signed-off-by: Ekansh Gupta <ekansh.gupta@xxxxxxxxxxxxxxxx>
---
drivers/accel/qda/Makefile | 4 ++-
drivers/accel/qda/qda_cb.c | 76 +++++++++++++++++++++++++++++++++++++++++++
drivers/accel/qda/qda_cb.h | 29 +++++++++++++++++
drivers/accel/qda/qda_drv.c | 2 ++
drivers/accel/qda/qda_drv.h | 6 ++++
drivers/accel/qda/qda_rpmsg.c | 25 ++++++++++----
drivers/accel/qda/qda_soc.c | 31 ++++++++++++++++++
drivers/accel/qda/qda_soc.h | 30 +++++++++++++++++
8 files changed, 196 insertions(+), 7 deletions(-)

diff --git a/drivers/accel/qda/Makefile b/drivers/accel/qda/Makefile
index dbe809067a8b..4f2f236086a4 100644
--- a/drivers/accel/qda/Makefile
+++ b/drivers/accel/qda/Makefile
@@ -6,5 +6,7 @@
obj-$(CONFIG_DRM_ACCEL_QDA) := qda.o

qda-y := \
+ qda_cb.o \
qda_drv.o \
- qda_rpmsg.o
+ qda_rpmsg.o \
+ qda_soc.o
diff --git a/drivers/accel/qda/qda_cb.c b/drivers/accel/qda/qda_cb.c
new file mode 100644
index 000000000000..afb0bb71a1f4
--- /dev/null
+++ b/drivers/accel/qda/qda_cb.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <drm/drm_print.h>
+#include "qda_cb.h"
+#include "qda_drv.h"
+#include "qda_soc.h"
+
+static int qda_create_cb_device(struct qda_dev *qdev, struct device_node *cb_node)
+{
+ struct qda_cb_dev *entry;
+ struct platform_device *pdev;
+ u32 dma_bits;
+ int ret;
+
+ pdev = of_platform_device_create(cb_node, NULL, qdev->dev);
+ if (!pdev) {
+ drm_err(&qdev->drm_dev, "Failed to create CB device for %pOFn\n", cb_node);
+ return -ENODEV;
+ }
+
+ dma_bits = qda_soc_dma_addr_bits(qdev);
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(dma_bits));
+ if (ret) {
+ drm_err(&qdev->drm_dev, "%u-bit DMA enable failed for %pOFn: %d\n",
+ dma_bits, cb_node, ret);
+ goto err_destroy;
+ }
+
+ entry = kzalloc_obj(*entry);
+ if (!entry) {
+ ret = -ENOMEM;
+ goto err_destroy;
+ }
+
+ entry->pdev = pdev;
+ list_add_tail(&entry->node, &qdev->cb_devs);
+
+ return 0;
+
+err_destroy:
+ of_platform_device_destroy(&pdev->dev, NULL);
+ return ret;
+}
+
+int qda_cb_populate(struct qda_dev *qdev, struct device_node *parent_node)
+{
+ int ret;
+
+ for_each_available_child_of_node_scoped(parent_node, child) {
+ if (!of_device_is_compatible(child, "qcom,fastrpc-compute-cb"))
+ continue;
+
+ ret = qda_create_cb_device(qdev, child);
+ if (ret) {
+ qda_cb_unpopulate(qdev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void qda_cb_unpopulate(struct qda_dev *qdev)
+{
+ struct qda_cb_dev *entry, *tmp;
+
+ list_for_each_entry_safe(entry, tmp, &qdev->cb_devs, node) {
+ list_del(&entry->node);
+ of_platform_device_destroy(&entry->pdev->dev, NULL);
+ kfree(entry);
+ }
+}
diff --git a/drivers/accel/qda/qda_cb.h b/drivers/accel/qda/qda_cb.h
new file mode 100644
index 000000000000..070dc1de67a0
--- /dev/null
+++ b/drivers/accel/qda/qda_cb.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __QDA_CB_H__
+#define __QDA_CB_H__
+
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include "qda_drv.h"
+
+/**
+ * struct qda_cb_dev - Bookkeeping for one compute context bank device
+ */
+struct qda_cb_dev {
+ /** @node: Entry in &qda_dev.cb_devs */
+ struct list_head node;
+ /** @pdev: Platform device created for the context bank node */
+ struct platform_device *pdev;
+};
+
+/* Create a device for each context bank child node of @parent_node */
+int qda_cb_populate(struct qda_dev *qdev, struct device_node *parent_node);
+/* Destroy every context bank device created by qda_cb_populate() */
+void qda_cb_unpopulate(struct qda_dev *qdev);
+
+#endif /* __QDA_CB_H__ */
diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c
index f4430332ff6e..6ecb6a966f68 100644
--- a/drivers/accel/qda/qda_drv.c
+++ b/drivers/accel/qda/qda_drv.c
@@ -65,6 +65,8 @@ struct qda_dev *qda_alloc_device(struct device *dev)
if (IS_ERR(qdev))
return ERR_CAST(qdev);

+ INIT_LIST_HEAD(&qdev->cb_devs);
+
return qdev;
}

diff --git a/drivers/accel/qda/qda_drv.h b/drivers/accel/qda/qda_drv.h
index dd3345b6a5ad..737232f7027f 100644
--- a/drivers/accel/qda/qda_drv.h
+++ b/drivers/accel/qda/qda_drv.h
@@ -7,11 +7,13 @@
#define __QDA_DRV_H__

#include <linux/device.h>
+#include <linux/list.h>
#include <linux/rpmsg.h>
#include <linux/types.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
+#include "qda_soc.h"

/* Driver identification */
#define QDA_DRIVER_NAME "qda"
@@ -41,6 +43,10 @@ struct qda_dev {
struct rpmsg_device *rpdev;
/** @dev: Underlying Linux device */
struct device *dev;
+ /** @cb_devs: Compute context-bank (CB) child devices */
+ struct list_head cb_devs;
+ /** @soc_data: Per-SoC DSP integration parameters */
+ const struct qda_soc_data *soc_data;
/** @dsp_name: Name of the DSP domain (e.g. "cdsp", "adsp") */
const char *dsp_name;
};
diff --git a/drivers/accel/qda/qda_rpmsg.c b/drivers/accel/qda/qda_rpmsg.c
index 6bd977e8d68a..78b5f31974c1 100644
--- a/drivers/accel/qda/qda_rpmsg.c
+++ b/drivers/accel/qda/qda_rpmsg.c
@@ -5,18 +5,18 @@
#include <linux/rpmsg.h>
#include <drm/drm_print.h>

+#include "qda_cb.h"
#include "qda_drv.h"
+#include "qda_soc.h"

/*
* Both compatibles the binding defines are matched here. The glymur, hawi,
* maili and nord nodes name "qcom,kaanapali-fastrpc" as their fallback
- * compatible and so are matched by that entry. Per-SoC DSP parameters are
- * attached as match data by a later patch, once there is something that
- * consumes them.
+ * compatible and so are matched by that entry.
*/
static const struct of_device_id qda_rpmsg_of_match[] = {
- { .compatible = "qcom,kaanapali-fastrpc" },
- { .compatible = "qcom,fastrpc" },
+ { .compatible = "qcom,kaanapali-fastrpc", .data = &kaanapali_soc_data },
+ { .compatible = "qcom,fastrpc", .data = &default_soc_data },
{ }
};
MODULE_DEVICE_TABLE(of, qda_rpmsg_of_match);
@@ -58,6 +58,7 @@ static void qda_rpmsg_remove(struct rpmsg_device *rpdev)
*/
drm_dev_unplug(&qdev->drm_dev);
qdev->rpdev = NULL;
+ qda_cb_unpopulate(qdev);
}

static int qda_rpmsg_probe(struct rpmsg_device *rpdev)
@@ -69,13 +70,25 @@ static int qda_rpmsg_probe(struct rpmsg_device *rpdev)
if (IS_ERR(qdev))
return PTR_ERR(qdev);

+ qdev->soc_data = of_device_get_match_data(&rpdev->dev);
+ if (!qdev->soc_data)
+ return -ENODEV;
+
ret = of_property_read_string(rpdev->dev.of_node, "label", &qdev->dsp_name);
if (ret) {
dev_err(&rpdev->dev, "Missing 'label' property in DT node: %d\n", ret);
return ret;
}

- return qda_register_device(qdev);
+ ret = qda_cb_populate(qdev, rpdev->dev.of_node);
+ if (ret)
+ return ret;
+
+ ret = qda_register_device(qdev);
+ if (ret)
+ qda_cb_unpopulate(qdev);
+
+ return ret;
}

static struct rpmsg_driver qda_rpmsg_driver = {
diff --git a/drivers/accel/qda/qda_soc.c b/drivers/accel/qda/qda_soc.c
new file mode 100644
index 000000000000..323b620b8adb
--- /dev/null
+++ b/drivers/accel/qda/qda_soc.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+#include <linux/string.h>
+#include "qda_drv.h"
+#include "qda_soc.h"
+
+const struct qda_soc_data kaanapali_soc_data = {
+ .dma_addr_bits_cdsp = 34,
+ .dma_addr_bits_default = 32,
+};
+
+const struct qda_soc_data default_soc_data = {
+ .dma_addr_bits_cdsp = 32,
+ .dma_addr_bits_default = 32,
+};
+
+/**
+ * qda_soc_dma_addr_bits() - DMA address width of a device's context banks
+ * @qdev: QDA device
+ *
+ * All CDSP instances (cdsp, cdsp1, ...) share the CDSP width.
+ *
+ * Return: Number of DMA address bits the DSP can address on @qdev
+ */
+u32 qda_soc_dma_addr_bits(const struct qda_dev *qdev)
+{
+ if (!strncmp(qdev->dsp_name, "cdsp", 4))
+ return qdev->soc_data->dma_addr_bits_cdsp;
+
+ return qdev->soc_data->dma_addr_bits_default;
+}
diff --git a/drivers/accel/qda/qda_soc.h b/drivers/accel/qda/qda_soc.h
new file mode 100644
index 000000000000..b052964f11ae
--- /dev/null
+++ b/drivers/accel/qda/qda_soc.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __QDA_SOC_H__
+#define __QDA_SOC_H__
+
+#include <linux/types.h>
+
+struct qda_dev;
+
+/**
+ * struct qda_soc_data - Per-SoC DSP integration parameters
+ *
+ * Selected by OF match on the fastrpc node; see qda_rpmsg.c.
+ */
+struct qda_soc_data {
+ /** @dma_addr_bits_cdsp: DMA address width of CDSP context banks */
+ u32 dma_addr_bits_cdsp;
+ /** @dma_addr_bits_default: DMA address width of all other context banks */
+ u32 dma_addr_bits_default;
+};
+
+extern const struct qda_soc_data kaanapali_soc_data;
+extern const struct qda_soc_data default_soc_data;
+
+u32 qda_soc_dma_addr_bits(const struct qda_dev *qdev);
+
+#endif /* __QDA_SOC_H__ */

--
2.34.1