From: Ekansh Gupta ekansh.gupta@oss.qualcomm.com
Introduce the CB (compute context bank) device management layer for the QDA driver. Each DSP domain node in the device tree may contain child nodes with compatible "qcom,fastrpc-compute-cb", each representing one IOMMU context bank. The driver enumerates those child nodes during RPMsg probe and creates a corresponding device on the qda-compute-cb bus for each one.
The CB devices are created via create_qda_cb_device(), which registers them on the qda-compute-cb bus so that the IOMMU subsystem assigns each device its own IOMMU domain, enabling per-session address space isolation for DSP buffer mapping.
The new qda_cb.c file provides two functions:
qda_create_cb_device() Reads the "reg" property from the DT child node to obtain the stream ID, constructs a unique device name of the form "qda-cb-<dsp>-<sid>", and registers the device on the compute bus. A qda_cb_dev entry is allocated and appended to qdev->cb_devs so that the list can be walked during teardown.
qda_destroy_cb_device() Removes the device from its IOMMU group before calling device_unregister(), ensuring the IOMMU domain is released cleanly.
CB devices are populated before the DRM device is registered and destroyed before it is unplugged, so no DRM operation can race with CB teardown. On probe failure after population, qda_cb_unpopulate() is called to clean up any CBs that were successfully created before the error.
Assisted-by: Claude:claude-4-6-sonnet Signed-off-by: Ekansh Gupta ekansh.gupta@oss.qualcomm.com --- drivers/accel/qda/Makefile | 1 + drivers/accel/qda/qda_cb.c | 99 +++++++++++++++++++++++++++++++++++++++++++ drivers/accel/qda/qda_cb.h | 32 ++++++++++++++ drivers/accel/qda/qda_drv.c | 1 + drivers/accel/qda/qda_drv.h | 3 ++ drivers/accel/qda/qda_rpmsg.c | 12 +++++- 6 files changed, 147 insertions(+), 1 deletion(-)
diff --git a/drivers/accel/qda/Makefile b/drivers/accel/qda/Makefile index 424176f652a5..143c9e4e789e 100644 --- a/drivers/accel/qda/Makefile +++ b/drivers/accel/qda/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_ACCEL_QDA) := qda.o
qda-y := \ + qda_cb.o \ qda_drv.o \ qda_rpmsg.o
diff --git a/drivers/accel/qda/qda_cb.c b/drivers/accel/qda/qda_cb.c new file mode 100644 index 000000000000..77caf8438c67 --- /dev/null +++ b/drivers/accel/qda/qda_cb.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/iommu.h> +#include <linux/qda_compute_bus.h> +#include <linux/slab.h> +#include <drm/drm_print.h> +#include "qda_drv.h" +#include "qda_cb.h" + +int qda_create_cb_device(struct qda_dev *qdev, struct device_node *cb_node) +{ + struct device *cb_dev; + u32 sid = 0; + char name[64]; + struct qda_cb_dev *entry; + + drm_dbg_driver(&qdev->drm_dev, "Creating CB device for node: %s\n", cb_node->name); + + of_property_read_u32(cb_node, "reg", &sid); + + snprintf(name, sizeof(name), "qda-cb-%s-%u", qdev->dsp_name, sid); + + cb_dev = create_qda_cb_device(qdev->dev, name, DMA_BIT_MASK(32), cb_node); + if (IS_ERR(cb_dev)) { + drm_err(&qdev->drm_dev, "Failed to create CB device for SID %u: %ld\n", + sid, PTR_ERR(cb_dev)); + return PTR_ERR(cb_dev); + } + + entry = kzalloc_obj(*entry); + if (!entry) { + device_unregister(cb_dev); + return -ENOMEM; + } + + entry->dev = cb_dev; + list_add_tail(&entry->node, &qdev->cb_devs); + + drm_dbg_driver(&qdev->drm_dev, "Successfully created CB device for SID %u\n", sid); + 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); + qda_destroy_cb_device(entry->dev); + kfree(entry); + } +} + +int qda_cb_populate(struct qda_dev *qdev, struct device_node *parent_node) +{ + struct device_node *child; + int count = 0, success = 0; + + for_each_child_of_node(parent_node, child) { + if (of_device_is_compatible(child, "qcom,fastrpc-compute-cb")) { + count++; + if (qda_create_cb_device(qdev, child) == 0) { + success++; + dev_dbg(qdev->dev, "Created CB device for node: %s\n", + child->name); + } else { + dev_err(qdev->dev, "Failed to create CB device for: %s\n", + child->name); + } + } + } + if (count == 0) + return 0; + return success > 0 ? 0 : -ENODEV; +} + +void qda_destroy_cb_device(struct device *cb_dev) +{ + struct iommu_group *group; + + if (!cb_dev) { + pr_debug("qda: NULL CB device passed to destroy\n"); + return; + } + + dev_dbg(cb_dev, "Destroying CB device %s\n", dev_name(cb_dev)); + + group = iommu_group_get(cb_dev); + if (group) { + dev_dbg(cb_dev, "Removing %s from IOMMU group\n", dev_name(cb_dev)); + iommu_group_remove_device(cb_dev); + iommu_group_put(group); + } + + device_unregister(cb_dev); +} diff --git a/drivers/accel/qda/qda_cb.h b/drivers/accel/qda/qda_cb.h new file mode 100644 index 000000000000..bd83d64fa425 --- /dev/null +++ b/drivers/accel/qda/qda_cb.h @@ -0,0 +1,32 @@ +/* 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/device.h> +#include <linux/list.h> +#include <linux/of.h> +#include "qda_drv.h" + +struct qda_cb_dev { + struct list_head node; + struct device *dev; +}; + +/* + * Compute bus (CB) device management + */ +int qda_create_cb_device(struct qda_dev *qdev, struct device_node *cb_node); +void qda_destroy_cb_device(struct device *cb_dev); + +/* + * Transport-agnostic CB device population/teardown. + * Called by any transport layer (RPMsg, etc.) during probe/remove. + */ +int qda_cb_populate(struct qda_dev *qdev, struct device_node *parent_node); +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 1c1bab68d445..6c20d6a2fc47 100644 --- a/drivers/accel/qda/qda_drv.c +++ b/drivers/accel/qda/qda_drv.c @@ -53,6 +53,7 @@ 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 7ba2ef19a411..2715f378775d 100644 --- a/drivers/accel/qda/qda_drv.h +++ b/drivers/accel/qda/qda_drv.h @@ -7,6 +7,7 @@ #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> @@ -37,6 +38,8 @@ 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; /** @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 6eaf1b145f8a..afd9e851d00e 100644 --- a/drivers/accel/qda/qda_rpmsg.c +++ b/drivers/accel/qda/qda_rpmsg.c @@ -5,6 +5,7 @@ #include <linux/rpmsg.h> #include <drm/drm_print.h>
+#include "qda_cb.h" #include "qda_drv.h" #include "qda_rpmsg.h"
@@ -34,6 +35,7 @@ static void qda_rpmsg_remove(struct rpmsg_device *rpdev) { struct qda_dev *qdev = dev_get_drvdata(&rpdev->dev);
+ qda_cb_unpopulate(qdev); drm_dev_unplug(&qdev->drm_dev); qdev->rpdev = NULL; qda_unregister_device(qdev); @@ -59,9 +61,17 @@ static int qda_rpmsg_probe(struct rpmsg_device *rpdev) } qdev->dsp_name = label;
+ ret = qda_cb_populate(qdev, rpdev->dev.of_node); + if (ret) { + dev_err(qdev->dev, "Failed to populate child devices: %d\n", ret); + return ret; + } + ret = qda_register_device(qdev); - if (ret) + if (ret) { + qda_cb_unpopulate(qdev); return ret; + }
drm_info(&qdev->drm_dev, "QDA RPMsg probe complete for %s\n", qdev->dsp_name); return 0;