[PATCH v4 23/25] accel/qda: Add DSP capability query
From: Ekansh Gupta
Date: Sun Nov 01 2026 - 17:35:35 EST
From: Ekansh Gupta <ekangupt@xxxxxxxxxxxxxxxx>
Add a DRM_IOCTL_QDA_QUERY query type that returns a DSP capability
attribute, such as async support, unsigned PD offload or VTCM size, so
user space can ask the DSP what it supports without a separate ioctl.
The attributes are fetched from the DSP through its stateless utilities
handle, which needs no process context, and cached on the device the
first time any session queries them. Later queries from any session are
served from the cache. A DSP without capability support makes the query
fail with -EOPNOTSUPP.
Signed-off-by: Ekansh Gupta <ekangupt@xxxxxxxxxxxxxxxx>
---
drivers/accel/qda/qda_drv.c | 2 ++
drivers/accel/qda/qda_drv.h | 9 ++++++++
drivers/accel/qda/qda_fastrpc.c | 47 +++++++++++++++++++++++++++++++++++++++++
drivers/accel/qda/qda_fastrpc.h | 12 +++++++++++
drivers/accel/qda/qda_ioctl.c | 42 ++++++++++++++++++++++++++++++++++++
drivers/accel/qda/qda_ioctl.h | 2 ++
include/uapi/drm/qda_accel.h | 15 +++++++++++--
7 files changed, 127 insertions(+), 2 deletions(-)
diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c
index 1e72dce769b9..51d8ef10c6c1 100644
--- a/drivers/accel/qda/qda_drv.c
+++ b/drivers/accel/qda/qda_drv.c
@@ -154,6 +154,7 @@ struct qda_dev *qda_alloc_device(struct device *dev)
INIT_LIST_HEAD(&qdev->cb_devs);
mutex_init(&qdev->import_lock);
+ mutex_init(&qdev->attr_lock);
xa_init_flags(&qdev->ctx_xa, XA_FLAGS_ALLOC1);
atomic64_set(&qdev->ctx_seq, 0);
atomic_set(&qdev->session_id_seq, 0);
@@ -181,6 +182,7 @@ void qda_deinit_device(struct qda_dev *qdev)
qda_memory_manager_exit(qdev->iommu_mgr);
xa_destroy(&qdev->ctx_xa);
mutex_destroy(&qdev->import_lock);
+ mutex_destroy(&qdev->attr_lock);
}
int qda_register_device(struct qda_dev *qdev)
diff --git a/drivers/accel/qda/qda_drv.h b/drivers/accel/qda/qda_drv.h
index 1d083ea9fdd7..cbb580d03e0b 100644
--- a/drivers/accel/qda/qda_drv.h
+++ b/drivers/accel/qda/qda_drv.h
@@ -35,6 +35,9 @@
/* Upper bound on the "qcom,vmids" DT property */
#define QDA_MAX_VMIDS 16
+/* Size of the cached DSP attribute array; index 0 is reserved */
+#define QDA_MAX_DSP_ATTRIBUTES 256
+
/**
* struct qda_session - One user of a DSP
*
@@ -122,6 +125,12 @@ struct qda_dev {
* to, or NULL if it could not be created
*/
struct auxiliary_device *legacy_auxdev;
+ /** @attr_lock: Serialises the DSP capability query and protects the cache below */
+ struct mutex attr_lock;
+ /** @valid_attributes: True once @dsp_attributes has been queried from the DSP */
+ bool valid_attributes;
+ /** @dsp_attributes: Cached DSP capability attributes */
+ u32 dsp_attributes[QDA_MAX_DSP_ATTRIBUTES];
/** @vmperms: VMs the remote heap is shared with, from qcom,vmids */
struct qcom_scm_vmperm vmperms[QDA_MAX_VMIDS];
/** @vmcount: Number of valid entries in @vmperms */
diff --git a/drivers/accel/qda/qda_fastrpc.c b/drivers/accel/qda/qda_fastrpc.c
index e9688e3ba404..6044cfad53e9 100644
--- a/drivers/accel/qda/qda_fastrpc.c
+++ b/drivers/accel/qda/qda_fastrpc.c
@@ -638,6 +638,13 @@ void qda_fastrpc_return_result(struct qda_fastrpc_invoke_ctx *ctx, void *argp)
int idx;
switch (ctx->type) {
+ case QDA_FASTRPC_TYPE_GET_DSP_INFO:
+ /* @argp is the attribute array; entry 0 is reserved */
+ ((u32 *)argp)[0] = 0;
+ memcpy((u32 *)argp + 1,
+ (u8 *)to_qda_gem_obj(ctx->gem_objs[1])->virt + ctx->args[1].offset,
+ ctx->args[1].length);
+ return;
case QDA_FASTRPC_RMID_INIT_MMAP:
idx = 2;
break;
@@ -980,6 +987,43 @@ static int qda_fastrpc_prepare_args_create_static(struct qda_fastrpc_invoke_ctx
return 0;
}
+/*
+ * DSP capability query packed layout:
+ * [0] count sizeof(u32), the capacity of the array below IN
+ * [1] attrs (QDA_MAX_DSP_ATTRIBUTES - 1) * sizeof(u32) OUT
+ *
+ * Index 0 of the cached array is reserved, so only the rest is exchanged.
+ */
+static int qda_fastrpc_prepare_args_get_dsp_info(struct qda_fastrpc_invoke_ctx *ctx)
+{
+ const u32 attr_count = QDA_MAX_DSP_ATTRIBUTES - 1;
+ struct qda_gem_obj *kernel_gem;
+ int err;
+
+ const size_t off_attrs = sizeof(u32);
+ const size_t total_size = off_attrs + attr_count * sizeof(u32);
+
+ ctx->sc = QDA_FASTRPC_SCALARS(0, 1, 1);
+ ctx->handle = QDA_FASTRPC_DSP_UTILITIES_HANDLE;
+
+ err = alloc_kernel_args(ctx, 2);
+ if (err)
+ return err;
+
+ kernel_gem = alloc_kernel_gem(ctx, total_size);
+ if (IS_ERR(kernel_gem))
+ return PTR_ERR(kernel_gem);
+
+ *(u32 *)kernel_gem->virt = attr_count;
+
+ assign_kernel_gem_arg(ctx, 0, kernel_gem, kernel_gem->virt, sizeof(u32));
+ assign_kernel_gem_arg(ctx, 1, kernel_gem, (u8 *)kernel_gem->virt + off_attrs,
+ attr_count * sizeof(u32));
+ drm_gem_object_put(&kernel_gem->base);
+
+ return 0;
+}
+
/*
* Find the buffer a mapping request names and describe it to the DSP. The
* buffer is held by the context until the request is done, so that closing its
@@ -1277,6 +1321,9 @@ int qda_fastrpc_prepare_args(struct qda_fastrpc_invoke_ctx *ctx,
ctx->pd = QDA_USER_PD;
err = qda_fastrpc_prepare_args_create_static(ctx, argp);
break;
+ case QDA_FASTRPC_TYPE_GET_DSP_INFO:
+ err = qda_fastrpc_prepare_args_get_dsp_info(ctx);
+ break;
case QDA_FASTRPC_RMID_INIT_MMAP:
err = qda_fastrpc_prepare_args_map(ctx, argp);
break;
diff --git a/drivers/accel/qda/qda_fastrpc.h b/drivers/accel/qda/qda_fastrpc.h
index de04cadcaeb0..7d5cd04943a3 100644
--- a/drivers/accel/qda/qda_fastrpc.h
+++ b/drivers/accel/qda/qda_fastrpc.h
@@ -55,9 +55,21 @@ struct qda_session;
#define QDA_FASTRPC_RMID_INIT_MEM_UNMAP 11
#define QDA_FASTRPC_RMID_INVOKE_DYNAMIC 0xFFFFFFFFU
+/*
+ * ctx->type tag for the DSP capability query. Not a wire method ID: the query
+ * is addressed by QDA_FASTRPC_DSP_UTILITIES_HANDLE, method 0.
+ */
+#define QDA_FASTRPC_TYPE_GET_DSP_INFO 0xFFFFFFFEU
+
/* Handle the initialisation methods are addressed to */
#define QDA_FASTRPC_INIT_HANDLE 0x1
+/* Handle of the DSP's utility calls, which include the capability query */
+#define QDA_FASTRPC_DSP_UTILITIES_HANDLE 0x2
+
+/* Status the DSP returns when it does not support the capability query */
+#define QDA_FASTRPC_DSP_UNSUPPORTED_API 0x80000414U
+
/* Protection domains a request can be directed at */
#define QDA_ROOT_PD 0
#define QDA_USER_PD 1
diff --git a/drivers/accel/qda/qda_ioctl.c b/drivers/accel/qda/qda_ioctl.c
index aaf3e5e6b5b1..c784defd0f70 100644
--- a/drivers/accel/qda/qda_ioctl.c
+++ b/drivers/accel/qda/qda_ioctl.c
@@ -27,6 +27,9 @@ int qda_ioctl_query(struct drm_device *dev, void *data, struct drm_file *file_pr
case QDA_QUERY_DSP_NAME:
strscpy(args->dsp_name, qdev->dsp_name, sizeof(args->dsp_name));
return 0;
+ case QDA_QUERY_DSP_CAPABILITIES:
+ return qda_get_dsp_capability(qdev, file_priv->driver_priv,
+ args->cap.attribute_id, &args->cap.capability);
default:
return -EINVAL;
}
@@ -165,6 +168,45 @@ int qda_session_init_create(struct qda_session *session, struct qda_init_create_
return qda_fastrpc_invoke(QDA_FASTRPC_RMID_INIT_CREATE, create, session);
}
+/**
+ * qda_get_dsp_capability() - Read one DSP capability attribute
+ * @qdev: Device whose DSP to query
+ * @session: Session the query is issued on if the cache is still empty
+ * @attribute_id: Attribute to read
+ * @capability: Receives the value of the attribute
+ *
+ * The attributes are fetched from the DSP once per device and served from the
+ * cache afterwards, to every session. The cache is only written by a query
+ * that succeeded.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_get_dsp_capability(struct qda_dev *qdev, struct qda_session *session,
+ u32 attribute_id, u32 *capability)
+{
+ int err;
+
+ if (attribute_id >= QDA_MAX_DSP_ATTRIBUTES)
+ return -EOVERFLOW;
+
+ guard(mutex)(&qdev->attr_lock);
+
+ if (!qdev->valid_attributes) {
+ err = qda_fastrpc_invoke(QDA_FASTRPC_TYPE_GET_DSP_INFO, qdev->dsp_attributes,
+ session);
+ if (err == (int)QDA_FASTRPC_DSP_UNSUPPORTED_API)
+ return -EOPNOTSUPP;
+ if (err)
+ return err;
+
+ qdev->valid_attributes = true;
+ }
+
+ *capability = qdev->dsp_attributes[attribute_id];
+
+ return 0;
+}
+
/**
* qda_release_dsp_process() - Tear down a session's DSP process
* @qdev: QDA device structure
diff --git a/drivers/accel/qda/qda_ioctl.h b/drivers/accel/qda/qda_ioctl.h
index 94adba549989..329f3809c8ce 100644
--- a/drivers/accel/qda/qda_ioctl.h
+++ b/drivers/accel/qda/qda_ioctl.h
@@ -23,6 +23,8 @@ int qda_ioctl_attach(struct drm_device *dev, void *data, struct drm_file *file_p
int qda_session_init_create(struct qda_session *session, struct qda_init_create_args *create);
int qda_session_attach(struct qda_session *session, u32 pd_type, const char __user *name,
u32 namelen);
+int qda_get_dsp_capability(struct qda_dev *qdev, struct qda_session *session,
+ u32 attribute_id, u32 *capability);
int qda_session_mem_map(struct qda_session *session, struct qda_mem_map_args *args);
int qda_session_mem_unmap(struct qda_session *session, struct drm_qda_mem_unmap *unmap);
diff --git a/include/uapi/drm/qda_accel.h b/include/uapi/drm/qda_accel.h
index 7c439058876e..07b943a3c822 100644
--- a/include/uapi/drm/qda_accel.h
+++ b/include/uapi/drm/qda_accel.h
@@ -50,19 +50,30 @@ extern "C" {
/* Query type definitions for drm_qda_query */
#define QDA_QUERY_DSP_NAME 1
+#define QDA_QUERY_DSP_CAPABILITIES 2
/**
* struct drm_qda_query - Device information query structure
* @query_type: Type of query (input)
* @dsp_name: Null-terminated name of the DSP (returned when query_type is
* QDA_QUERY_DSP_NAME)
+ * @cap: DSP capability query (used when query_type is
+ * QDA_QUERY_DSP_CAPABILITIES): @cap.attribute_id selects the attribute
+ * (input) and @cap.capability returns its value (output)
*
* This structure is used with DRM_IOCTL_QDA_QUERY to query device attributes
- * based on @query_type.
+ * based on @query_type. @dsp_name and @cap share storage, as only one query
+ * is made per call.
*/
struct drm_qda_query {
__u32 query_type;
- __u8 dsp_name[16];
+ union {
+ __u8 dsp_name[16];
+ struct {
+ __u32 attribute_id;
+ __u32 capability;
+ } cap;
+ };
};
/**
--
2.34.1