[PATCH RFC 16/18] accel/qda: Add FastRPC-based DSP memory mapping support

From: Ekansh Gupta

Date: Mon Feb 23 2026 - 14:16:59 EST


Add a DRM_QDA_MAP ioctl and supporting FastRPC plumbing to map GEM
backed buffers into the DSP virtual address space. The new
qda_mem_map UAPI structure allows userspace to request legacy MMAP
style mappings or handle-based MEM_MAP mappings with attributes, and
encodes flags, offsets and optional virtual address hints that are
forwarded to the DSP.

On the FastRPC side new method identifiers FASTRPC_RMID_INIT_MMAP
and FASTRPC_RMID_INIT_MEM_MAP are introduced together with message
structures for map requests and responses. The fastrpc_prepare_args
path is extended to build the appropriate request headers, serialize
the physical page information derived from a GEM object into a
fastrpc_phy_page array and pack the arguments into the shared message
buffer used by the existing invoke infrastructure.

The qda_ioctl_mmap() handler dispatches mapping requests based on the
qda_mem_map request type, reusing the generic fastrpc_invoke()
machinery and the RPMsg transport to communicate with the DSP. This
provides the foundation for explicit buffer mapping into the DSP
address space for subsequent FastRPC calls, aligned with the
traditional FastRPC user space model.

Signed-off-by: Ekansh Gupta <ekansh.gupta@xxxxxxxxxxxxxxxx>
---
arch/arm64/configs/defconfig | 2 +
drivers/accel/qda/qda_drv.c | 1 +
drivers/accel/qda/qda_fastrpc.c | 217 ++++++++++++++++++++++++++++++++++++++++
drivers/accel/qda/qda_fastrpc.h | 64 ++++++++++++
drivers/accel/qda/qda_ioctl.c | 24 +++++
drivers/accel/qda/qda_ioctl.h | 13 +++
include/uapi/drm/qda_accel.h | 44 +++++++-
7 files changed, 364 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index b67d5b1fc45b..e53a7984c9be 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -1046,6 +1046,8 @@ CONFIG_DRM_TIDSS=m
CONFIG_DRM_ZYNQMP_DPSUB=m
CONFIG_DRM_ZYNQMP_DPSUB_AUDIO=y
CONFIG_DRM_POWERVR=m
+CONFIG_DRM_ACCEL=y
+CONFIG_DRM_ACCEL_QDA=m
CONFIG_FB=y
CONFIG_FB_EFI=y
CONFIG_FB_MODE_HELPERS=y
diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c
index 2b080d5d51c5..5f43c97ebc25 100644
--- a/drivers/accel/qda/qda_drv.c
+++ b/drivers/accel/qda/qda_drv.c
@@ -163,6 +163,7 @@ static const struct drm_ioctl_desc qda_ioctls[] = {
DRM_IOCTL_DEF_DRV(QDA_GEM_MMAP_OFFSET, qda_ioctl_gem_mmap_offset, 0),
DRM_IOCTL_DEF_DRV(QDA_INIT_ATTACH, qda_ioctl_attach, 0),
DRM_IOCTL_DEF_DRV(QDA_INIT_CREATE, qda_ioctl_create, 0),
+ DRM_IOCTL_DEF_DRV(QDA_MAP, qda_ioctl_mmap, 0),
DRM_IOCTL_DEF_DRV(QDA_INVOKE, qda_ioctl_invoke, 0),
};

diff --git a/drivers/accel/qda/qda_fastrpc.c b/drivers/accel/qda/qda_fastrpc.c
index f03dcf7e21e4..25b5d53ba2d6 100644
--- a/drivers/accel/qda/qda_fastrpc.c
+++ b/drivers/accel/qda/qda_fastrpc.c
@@ -487,6 +487,40 @@ int fastrpc_internal_invoke_unpack(struct fastrpc_invoke_context *ctx,
return err;
}

+static int fastrpc_return_result_mem_map(struct fastrpc_invoke_context *ctx, char __user *argp)
+{
+ struct qda_mem_map margs;
+ struct fastrpc_map_rsp_msg *rsp_msg;
+ int err;
+
+ rsp_msg = ctx->rsp;
+
+ err = copy_from_user_or_kernel(&margs, argp, sizeof(margs));
+ if (err)
+ return err;
+
+ margs.vaddrout = rsp_msg->vaddrout;
+
+ err = copy_to_user_or_kernel(argp, &margs, sizeof(margs));
+ return err;
+}
+
+int fastrpc_return_result(struct fastrpc_invoke_context *ctx, char __user *argp)
+{
+ int err = 0;
+
+ switch (ctx->type) {
+ case FASTRPC_RMID_INIT_MMAP:
+ case FASTRPC_RMID_INIT_MEM_MAP:
+ err = fastrpc_return_result_mem_map(ctx, argp);
+ break;
+ default:
+ break;
+ }
+
+ return err;
+}
+
static void setup_create_process_args(struct fastrpc_invoke_args *args,
struct fastrpc_create_process_inbuf *inbuf,
struct qda_init_create *init,
@@ -517,6 +551,29 @@ static void setup_create_process_args(struct fastrpc_invoke_args *args,
args[5].fd = -1;
}

+static int setup_mmap_pages(struct drm_file *file_priv, int fd, struct fastrpc_phy_page *pages)
+{
+ struct drm_gem_object *gem_obj;
+ struct qda_gem_obj *qda_gem_obj;
+ int err;
+
+ if (fd <= 0) {
+ pages->addr = 0;
+ pages->size = 0;
+ return 0;
+ }
+
+ err = get_gem_obj_from_handle(file_priv, fd, &gem_obj);
+ if (err)
+ return err;
+
+ qda_gem_obj = to_qda_gem_obj(gem_obj);
+ setup_pages_from_gem_obj(qda_gem_obj, pages);
+
+ drm_gem_object_put(gem_obj);
+ return 0;
+}
+
static int fastrpc_prepare_args_init_attach(struct fastrpc_invoke_context *ctx)
{
struct fastrpc_invoke_args *args;
@@ -658,6 +715,160 @@ static int fastrpc_prepare_args_init_create(struct fastrpc_invoke_context *ctx,
return err;
}

+static int fastrpc_prepare_args_map(struct fastrpc_invoke_context *ctx, char __user *argp)
+{
+ struct qda_mem_map margs;
+ struct fastrpc_invoke_args *args;
+ void *req, *rsp;
+ struct fastrpc_map_req_msg *req_msg;
+ struct fastrpc_map_rsp_msg *rsp_msg;
+ int err;
+
+ err = copy_from_user_or_kernel(&margs, argp, sizeof(margs));
+ if (err)
+ return err;
+
+ args = kzalloc_objs(*args, 3, GFP_KERNEL);
+ if (!args)
+ return -ENOMEM;
+
+ req = kzalloc_obj(*req_msg, GFP_KERNEL);
+ if (!req) {
+ err = -ENOMEM;
+ goto err_free_args;
+ }
+ req_msg = (struct fastrpc_map_req_msg *)req;
+
+ rsp = kzalloc_obj(*rsp_msg, GFP_KERNEL);
+ if (!rsp) {
+ err = -ENOMEM;
+ goto err_free_req;
+ }
+ rsp_msg = (struct fastrpc_map_rsp_msg *)rsp;
+
+ ctx->input_pages = kzalloc_objs(*ctx->input_pages, 1, GFP_KERNEL);
+ if (!ctx->input_pages) {
+ err = -ENOMEM;
+ goto err_free_rsp;
+ }
+
+ req_msg->client_id = ctx->client_id;
+ req_msg->flags = margs.flags;
+ req_msg->vaddr = margs.vaddrin;
+ req_msg->num = sizeof(*ctx->input_pages);
+
+ args[0].ptr = (u64)(uintptr_t)req;
+ args[0].length = sizeof(*req_msg);
+
+ err = setup_mmap_pages(ctx->file_priv, margs.fd, ctx->input_pages);
+ if (err)
+ goto err_free_input_pages;
+
+ args[1].ptr = (u64)(uintptr_t)ctx->input_pages;
+ args[1].length = sizeof(*ctx->input_pages);
+
+ args[2].ptr = (u64)(uintptr_t)rsp;
+ args[2].length = sizeof(*rsp_msg);
+
+ ctx->sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MMAP, 2, 1);
+ ctx->args = args;
+ ctx->req = req;
+ ctx->rsp = rsp;
+ ctx->handle = FASTRPC_INIT_HANDLE;
+
+ return 0;
+
+err_free_input_pages:
+ kfree(ctx->input_pages);
+ ctx->input_pages = NULL;
+err_free_rsp:
+ kfree(rsp);
+err_free_req:
+ kfree(req);
+err_free_args:
+ kfree(args);
+ return err;
+}
+
+static int fastrpc_prepare_args_mem_map_attr(struct fastrpc_invoke_context *ctx, char __user *argp)
+{
+ struct qda_mem_map margs;
+ struct fastrpc_invoke_args *args;
+ void *req, *rsp;
+ struct fastrpc_mem_map_req_msg *req_msg;
+ struct fastrpc_map_rsp_msg *rsp_msg;
+ int err;
+
+ err = copy_from_user_or_kernel(&margs, argp, sizeof(margs));
+ if (err)
+ return err;
+
+ args = kzalloc_objs(*args, 4, GFP_KERNEL);
+ if (!args)
+ return -ENOMEM;
+
+ req = kzalloc_obj(*req_msg, GFP_KERNEL);
+ if (!req) {
+ kfree(args);
+ return -ENOMEM;
+ }
+ req_msg = (struct fastrpc_mem_map_req_msg *)req;
+
+ rsp = kzalloc_obj(*rsp_msg, GFP_KERNEL);
+ if (!rsp) {
+ kfree(args);
+ kfree(req);
+ return -ENOMEM;
+ }
+ rsp_msg = (struct fastrpc_map_rsp_msg *)rsp;
+
+ ctx->input_pages = kzalloc_objs(*ctx->input_pages, 1, GFP_KERNEL);
+ if (!ctx->input_pages) {
+ kfree(args);
+ kfree(req);
+ kfree(rsp);
+ return -ENOMEM;
+ }
+
+ req_msg->client_id = ctx->client_id;
+ req_msg->fd = margs.fd;
+ req_msg->offset = margs.offset;
+ req_msg->flags = margs.flags;
+ req_msg->vaddrin = margs.vaddrin;
+ req_msg->num = sizeof(*ctx->input_pages);
+ req_msg->data_len = 0;
+
+ args[0].ptr = (u64)(uintptr_t)req;
+ args[0].length = sizeof(*req_msg);
+
+ err = setup_mmap_pages(ctx->file_priv, margs.fd, ctx->input_pages);
+ if (err) {
+ kfree(args);
+ kfree(req);
+ kfree(rsp);
+ kfree(ctx->input_pages);
+ ctx->input_pages = NULL;
+ return err;
+ }
+
+ args[1].ptr = (u64)(uintptr_t)ctx->input_pages;
+ args[1].length = sizeof(*ctx->input_pages);
+
+ args[2].ptr = (u64)(uintptr_t)ctx->input_pages;
+ args[2].length = 0;
+
+ args[3].ptr = (u64)(uintptr_t)rsp;
+ args[3].length = sizeof(*rsp_msg);
+
+ ctx->sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MEM_MAP, 3, 1);
+ ctx->args = args;
+ ctx->req = req;
+ ctx->rsp = rsp;
+ ctx->handle = FASTRPC_INIT_HANDLE;
+
+ return 0;
+}
+
int fastrpc_prepare_args(struct fastrpc_invoke_context *ctx, char __user *argp)
{
int err;
@@ -678,6 +889,12 @@ int fastrpc_prepare_args(struct fastrpc_invoke_context *ctx, char __user *argp)
ctx->pd = USER_PD;
err = fastrpc_prepare_args_init_create(ctx, argp);
break;
+ case FASTRPC_RMID_INIT_MMAP:
+ err = fastrpc_prepare_args_map(ctx, argp);
+ break;
+ case FASTRPC_RMID_INIT_MEM_MAP:
+ err = fastrpc_prepare_args_mem_map_attr(ctx, argp);
+ break;
default:
return -EINVAL;
}
diff --git a/drivers/accel/qda/qda_fastrpc.h b/drivers/accel/qda/qda_fastrpc.h
index a8deb7efec86..b45ccc77d9d1 100644
--- a/drivers/accel/qda/qda_fastrpc.h
+++ b/drivers/accel/qda/qda_fastrpc.h
@@ -260,8 +260,10 @@ struct fastrpc_invoke_context {
/* Remote Method ID table - identifies initialization and control operations */
#define FASTRPC_RMID_INIT_ATTACH 0 /* Attach to DSP session */
#define FASTRPC_RMID_INIT_RELEASE 1 /* Release DSP session */
+#define FASTRPC_RMID_INIT_MMAP 4 /* Map memory region to DSP */
#define FASTRPC_RMID_INIT_CREATE 6 /* Create DSP process */
#define FASTRPC_RMID_INIT_CREATE_ATTR 7 /* Create DSP process with attributes */
+#define FASTRPC_RMID_INIT_MEM_MAP 10 /* Map DMA buffer with attributes to DSP */
#define FASTRPC_RMID_INVOKE_DYNAMIC 0xFFFFFFFF /* Dynamic method invocation */

/* Common handle for initialization operations */
@@ -276,6 +278,59 @@ struct fastrpc_invoke_context {
/* Maximum initialization file size (4MB) */
#define INIT_FILELEN_MAX (4 * 1024 * 1024)

+/* Message structures for internal FastRPC calls */
+
+/**
+ * struct fastrpc_mem_map_req_msg - Memory map request message with attributes
+ *
+ * This message structure is sent to the DSP to request mapping
+ * of a DMA buffer with custom attributes (ATTR request).
+ */
+struct fastrpc_mem_map_req_msg {
+ /* Client identifier for the session */
+ s32 client_id;
+ /* Handle of the buffer */
+ s32 fd;
+ /* Offset within the buffer */
+ s32 offset;
+ /* Mapping flags */
+ u32 flags;
+ /* Virtual address hint for mapping */
+ u64 vaddrin;
+ /* Pages in the mapping */
+ s32 num;
+ /* Length of additional data */
+ s32 data_len;
+};
+
+/**
+ * struct fastrpc_map_req_msg - Legacy memory map request message
+ *
+ * This message structure is sent to the DSP to request mapping
+ * of a DMA buffer into the DSP's virtual address space.
+ */
+struct fastrpc_map_req_msg {
+ /* Client identifier for the session */
+ s32 client_id;
+ /* Mapping flags */
+ u32 flags;
+ /* Virtual address hint for mapping */
+ u64 vaddr;
+ /* Pages in the mapping */
+ s32 num;
+};
+
+/**
+ * struct fastrpc_map_rsp_msg - Memory map response message
+ *
+ * This message structure is returned by the DSP after successfully
+ * mapping a buffer, providing the virtual address for future access.
+ */
+struct fastrpc_map_rsp_msg {
+ /* DSP virtual address assigned to the mapped buffer */
+ u64 vaddrout;
+};
+
/**
* fastrpc_context_free - Free an invocation context
* @ref: Reference counter for the context
@@ -332,4 +387,13 @@ int fastrpc_internal_invoke_pack(struct fastrpc_invoke_context *ctx, struct qda_
*/
int fastrpc_internal_invoke_unpack(struct fastrpc_invoke_context *ctx, struct qda_msg *msg);

+/**
+ * fastrpc_return_result - Return invocation result to user-space
+ * @ctx: FastRPC invocation context
+ * @argp: User-space pointer to return result
+ *
+ * Returns: 0 on success, negative error code on failure
+ */
+int fastrpc_return_result(struct fastrpc_invoke_context *ctx, char __user *argp);
+
#endif /* __QDA_FASTRPC_H__ */
diff --git a/drivers/accel/qda/qda_ioctl.c b/drivers/accel/qda/qda_ioctl.c
index 477112ad6664..4eb932e2c9ae 100644
--- a/drivers/accel/qda/qda_ioctl.c
+++ b/drivers/accel/qda/qda_ioctl.c
@@ -192,6 +192,10 @@ static int fastrpc_invoke(int type, struct drm_device *dev, void *data,
if (err)
goto err_context_free;

+ err = fastrpc_return_result(ctx, (char __user *)data);
+ if (err)
+ goto err_context_free;
+
err_context_free:
if (type == FASTRPC_RMID_INIT_RELEASE && qda_user->init_mem_gem_obj) {
drm_gem_object_put(&qda_user->init_mem_gem_obj->base);
@@ -223,3 +227,23 @@ int qda_ioctl_create(struct drm_device *dev, void *data, struct drm_file *file_p
{
return fastrpc_invoke(FASTRPC_RMID_INIT_CREATE, dev, data, file_priv);
}
+
+int qda_ioctl_mmap(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ struct qda_mem_map *map_req;
+
+ if (!data)
+ return -EINVAL;
+
+ map_req = (struct qda_mem_map *)data;
+
+ switch (map_req->request) {
+ case QDA_MAP_REQUEST_LEGACY:
+ return fastrpc_invoke(FASTRPC_RMID_INIT_MMAP, dev, data, file_priv);
+ case QDA_MAP_REQUEST_ATTR:
+ return fastrpc_invoke(FASTRPC_RMID_INIT_MEM_MAP, dev, data, file_priv);
+ default:
+ qda_err(NULL, "Invalid map request type: %u\n", map_req->request);
+ return -EINVAL;
+ }
+}
diff --git a/drivers/accel/qda/qda_ioctl.h b/drivers/accel/qda/qda_ioctl.h
index 181ed50b19dc..d402d6715b41 100644
--- a/drivers/accel/qda/qda_ioctl.h
+++ b/drivers/accel/qda/qda_ioctl.h
@@ -89,4 +89,17 @@ int qda_ioctl_invoke(struct drm_device *dev, void *data, struct drm_file *file_p
*/
int qda_ioctl_create(struct drm_device *dev, void *data, struct drm_file *file_priv);

+/**
+ * qda_ioctl_mmap - Map memory to DSP address space
+ * @dev: DRM device structure
+ * @data: User-space data containing memory mapping parameters
+ * @file_priv: DRM file private data
+ *
+ * This IOCTL handler maps a DMA buffer into the DSP's virtual address
+ * space, enabling the DSP to access the buffer during remote calls.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_ioctl_mmap(struct drm_device *dev, void *data, struct drm_file *file_priv);
+
#endif /* _QDA_IOCTL_H */
diff --git a/include/uapi/drm/qda_accel.h b/include/uapi/drm/qda_accel.h
index 2b7f500db52c..9151ba7adfaf 100644
--- a/include/uapi/drm/qda_accel.h
+++ b/include/uapi/drm/qda_accel.h
@@ -23,7 +23,8 @@ extern "C" {
#define DRM_QDA_GEM_MMAP_OFFSET 0x02
#define DRM_QDA_INIT_ATTACH 0x03
#define DRM_QDA_INIT_CREATE 0x04
-/* Indexes 0x05-0x06 are reserved for other requests */
+#define DRM_QDA_MAP 0x05
+/* 0x06 is reserved for other request */
#define DRM_QDA_INVOKE 0x07

/*
@@ -41,9 +42,14 @@ extern "C" {
#define DRM_IOCTL_QDA_INIT_ATTACH DRM_IO(DRM_COMMAND_BASE + DRM_QDA_INIT_ATTACH)
#define DRM_IOCTL_QDA_INIT_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_INIT_CREATE, \
struct qda_init_create)
+#define DRM_IOCTL_QDA_MAP DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_MAP, struct qda_mem_map)
#define DRM_IOCTL_QDA_INVOKE DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_INVOKE, \
struct qda_invoke_args)

+/* Request type definitions for qda_mem_map */
+#define QDA_MAP_REQUEST_LEGACY 1 /* Legacy MMAP operation */
+#define QDA_MAP_REQUEST_ATTR 2 /* Handle-based MEM_MAP operation with attributes */
+
/**
* struct drm_qda_query - Device information query structure
* @dsp_name: Name of DSP (e.g., "adsp", "cdsp", "cdsp1", "gdsp0", "gdsp1")
@@ -143,6 +149,42 @@ struct qda_init_create {
__u64 file;
};

+/**
+ * struct qda_mem_map - Memory mapping request structure
+ * @request: Request type (QDA_MAP_REQUEST_LEGACY or QDA_MAP_REQUEST_ATTR)
+ * @flags: Mapping flags for DSP (cache attributes, permissions)
+ * @fd: Handle of the buffer to map
+ * @attrs: Mapping attributes (used for ATTR request)
+ * @offset: Offset within buffer (used for ATTR request)
+ * @reserved: Reserved for alignment/future use
+ * @vaddrin: Optional virtual address hint for mapping
+ * @size: Size of the memory region to map in bytes
+ * @vaddrout: Output DSP virtual address after successful mapping
+ *
+ * This structure is used to request mapping of a DMA buffer into the
+ * DSP's virtual address space. The DSP will map the buffer according
+ * to the specified flags and return the virtual address in vaddrout.
+ *
+ * For QDA_MAP_REQUEST_LEGACY (value 1):
+ * - Uses fields: fd, flags, vaddrin, size, vaddrout
+ * - Legacy MMAP operation for backward compatibility
+ *
+ * For QDA_MAP_REQUEST_ATTR (value 2):
+ * - Uses all fields including attrs and offset
+ * - FD-based MEM_MAP operation with custom SMMU attributes
+ */
+struct qda_mem_map {
+ __u32 request;
+ __u32 flags;
+ __s32 fd;
+ __u32 attrs;
+ __u32 offset;
+ __u32 reserved;
+ __u64 vaddrin;
+ __u64 size;
+ __u64 vaddrout;
+};
+
#if defined(__cplusplus)
}
#endif

--
2.34.1