From: Ekansh Gupta <[email protected]>

Implement DRM_IOCTL_QDA_REMOTE_MAP, which maps a DMA buffer into the
DSP's virtual address space and returns the DSP virtual address to
user-space. Two mapping modes are supported:

QDA_MAP_REQUEST_LEGACY (FASTRPC_RMID_INIT_MMAP)
  Legacy three-argument mapping: sends a fastrpc_map_req_msg to the
  DSP containing the session ID, mapping flags, and virtual address
  hint, together with the physical page descriptor resolved from the
  DMA-BUF fd. The DSP returns the assigned virtual address in
  fastrpc_map_rsp_msg.vaddrout.

QDA_MAP_REQUEST_ATTR (FASTRPC_RMID_INIT_MEM_MAP)
  Attribute-based four-argument mapping: sends a
  fastrpc_mem_map_req_msg which additionally carries the DMA-BUF fd,
  byte offset, and SMMU attribute flags. The DSP uses these to apply
  custom cache and permission attributes to the mapping.

In both cases qda_fastrpc_return_result() writes the DSP virtual
address back into the drm_qda_mem_map.vaddrout field so the DRM
framework copies it to user-space on IOCTL return.

The DMA-BUF fd is resolved to a fastrpc_phy_page descriptor via
setup_mmap_pages(), which imports the fd as a GEM object to obtain
the IOMMU-mapped dma_addr and then releases the extra reference.

Assisted-by: Claude:claude-4-6-sonnet
Signed-off-by: Ekansh Gupta <[email protected]>
---
 drivers/accel/qda/qda_drv.c     |   1 +
 drivers/accel/qda/qda_fastrpc.c | 237 ++++++++++++++++++++++++++++++++++++++++
 drivers/accel/qda/qda_fastrpc.h |  56 ++++++++++
 drivers/accel/qda/qda_ioctl.c   |  36 ++++++
 drivers/accel/qda/qda_ioctl.h   |   1 +
 include/uapi/drm/qda_accel.h    |  45 +++++++-
 6 files changed, 375 insertions(+), 1 deletion(-)

diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c
index 4eaba9b050c0..3640e4a41605 100644
--- a/drivers/accel/qda/qda_drv.c
+++ b/drivers/accel/qda/qda_drv.c
@@ -67,6 +67,7 @@ static const struct drm_ioctl_desc qda_ioctls[] = {
        DRM_IOCTL_DEF_DRV(QDA_GEM_CREATE, qda_ioctl_gem_create, 0),
        DRM_IOCTL_DEF_DRV(QDA_GEM_MMAP_OFFSET, qda_ioctl_gem_mmap_offset, 0),
        DRM_IOCTL_DEF_DRV(QDA_REMOTE_SESSION_CREATE, qda_ioctl_init_create, 0),
+       DRM_IOCTL_DEF_DRV(QDA_REMOTE_MAP, qda_ioctl_mmap, 0),
        DRM_IOCTL_DEF_DRV(QDA_REMOTE_INVOKE, qda_ioctl_invoke, 0),
 };
 
diff --git a/drivers/accel/qda/qda_fastrpc.c b/drivers/accel/qda/qda_fastrpc.c
index 305915022b91..cab3a560ceb5 100644
--- a/drivers/accel/qda/qda_fastrpc.c
+++ b/drivers/accel/qda/qda_fastrpc.c
@@ -524,6 +524,44 @@ int qda_fastrpc_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 drm_qda_mem_map margs;
+       struct fastrpc_map_rsp_msg *rsp_msg;
+
+       rsp_msg = ctx->rsp;
+
+       memcpy(&margs, argp, sizeof(margs));
+
+       margs.vaddrout = rsp_msg->vaddrout;
+
+       memcpy(argp, &margs, sizeof(margs));
+       return 0;
+}
+
+/**
+ * qda_fastrpc_return_result() - Return invocation result to user-space
+ * @ctx: FastRPC invocation context
+ * @argp: User-space pointer to write result into
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_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 drm_qda_fastrpc_invoke_args *args,
                                      struct fastrpc_create_process_inbuf 
*inbuf,
                                      struct drm_qda_init_create *init,
@@ -561,6 +599,37 @@ static void setup_single_arg(struct 
drm_qda_fastrpc_invoke_args *args, const voi
        args[0].fd = -1;
 }
 
+/*
+ * setup_mmap_pages() - Resolve a DMA-BUF fd to a physical page descriptor
+ *
+ * Imports the DMA-BUF fd as a GEM object to obtain the IOMMU-mapped
+ * dma_addr, fills in the fastrpc_phy_page entry, then releases the extra
+ * GEM object reference.  The handle table keeps the object alive.
+ */
+static int setup_mmap_pages(struct fastrpc_invoke_context *ctx, int dmabuf_fd,
+                           struct fastrpc_phy_page *pages)
+{
+       struct drm_gem_object *gem_obj;
+       struct qda_gem_obj *qda_gem_obj;
+       int err;
+
+       if (dmabuf_fd <= 0) {
+               pages->addr = 0;
+               pages->size = 0;
+               return 0;
+       }
+
+       err = get_gem_obj_from_dmabuf_fd(ctx, dmabuf_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_release_process(struct fastrpc_invoke_context 
*ctx)
 {
        struct drm_qda_fastrpc_invoke_args *args;
@@ -656,6 +725,168 @@ 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 drm_qda_mem_map margs;
+       struct drm_qda_fastrpc_invoke_args *args;
+       void *req, *rsp;
+       struct fastrpc_map_req_msg *req_msg;
+       struct fastrpc_map_rsp_msg *rsp_msg;
+       int err;
+
+       memcpy(&margs, argp, sizeof(margs));
+
+       args = kzalloc_objs(*args, 3);
+       if (!args)
+               return -ENOMEM;
+
+       req = kzalloc_obj(*req_msg);
+       if (!req) {
+               err = -ENOMEM;
+               goto err_free_args;
+       }
+       req_msg = (struct fastrpc_map_req_msg *)req;
+
+       rsp = kzalloc_obj(*rsp_msg);
+       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);
+       if (!ctx->input_pages) {
+               err = -ENOMEM;
+               goto err_free_rsp;
+       }
+
+       req_msg->remote_session_id = ctx->remote_session_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);
+       args[0].fd = -1;
+
+       /* Resolve DMA-BUF fd to physical page descriptor */
+       err = setup_mmap_pages(ctx, 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[1].fd = -1;
+
+       args[2].ptr = (u64)(uintptr_t)rsp;
+       args[2].length = sizeof(*rsp_msg);
+       args[2].fd = -1;
+
+       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 drm_qda_mem_map margs;
+       struct drm_qda_fastrpc_invoke_args *args;
+       void *req, *rsp;
+       struct fastrpc_mem_map_req_msg *req_msg;
+       struct fastrpc_map_rsp_msg *rsp_msg;
+       int err;
+
+       memcpy(&margs, argp, sizeof(margs));
+
+       args = kzalloc_objs(*args, 4);
+       if (!args)
+               return -ENOMEM;
+
+       req = kzalloc_obj(*req_msg);
+       if (!req) {
+               err = -ENOMEM;
+               goto err_free_args;
+       }
+       req_msg = (struct fastrpc_mem_map_req_msg *)req;
+
+       rsp = kzalloc_obj(*rsp_msg);
+       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);
+       if (!ctx->input_pages) {
+               err = -ENOMEM;
+               goto err_free_rsp;
+       }
+
+       req_msg->remote_session_id = ctx->remote_session_id;
+       req_msg->fd       = margs.fd;           /* DMA-BUF fd forwarded to DSP 
*/
+       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);
+       args[0].fd = -1;
+
+       /* Resolve DMA-BUF fd to physical page descriptor */
+       err = setup_mmap_pages(ctx, 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[1].fd = -1;
+
+       /* args[2] is a zero-length handle-only entry required by the DSP 
protocol */
+       args[2].ptr = (u64)(uintptr_t)ctx->input_pages;
+       args[2].length = 0;
+       args[2].fd = -1;
+
+       args[3].ptr = (u64)(uintptr_t)rsp;
+       args[3].length = sizeof(*rsp_msg);
+       args[3].fd = -1;
+
+       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;
+
+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_invoke(struct fastrpc_invoke_context *ctx, 
char __user *argp)
 {
        struct drm_qda_invoke_args invoke_args;
@@ -708,6 +939,12 @@ int qda_fastrpc_prepare_args(struct fastrpc_invoke_context 
*ctx, char __user *ar
                ctx->pd = QDA_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;
        case FASTRPC_RMID_INVOKE_DYNAMIC:
                err = fastrpc_prepare_args_invoke(ctx, argp);
                break;
diff --git a/drivers/accel/qda/qda_fastrpc.h b/drivers/accel/qda/qda_fastrpc.h
index 1c1236f9525e..71812eaf9a54 100644
--- a/drivers/accel/qda/qda_fastrpc.h
+++ b/drivers/accel/qda/qda_fastrpc.h
@@ -274,8 +274,10 @@ struct fastrpc_invoke_context {
 
 /* Remote Method ID table - identifies initialization and control operations */
 #define FASTRPC_RMID_INIT_RELEASE      1       /* Release DSP process */
+#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 */
@@ -290,11 +292,65 @@ struct fastrpc_invoke_context {
 /* Maximum initialization file size (4 MB) */
 #define FASTRPC_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 {
+       /** @remote_session_id: Client identifier for the session */
+       s32 remote_session_id;
+       /** @fd: DMA-BUF file descriptor of the buffer to map */
+       s32 fd;
+       /** @offset: Byte offset within the buffer */
+       s32 offset;
+       /** @flags: Mapping flags (cache attributes, permissions) */
+       u32 flags;
+       /** @vaddrin: Virtual address hint for the DSP mapping */
+       u64 vaddrin;
+       /** @num: Size of the physical page descriptor array in bytes */
+       s32 num;
+       /** @data_len: Length of additional inline 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 {
+       /** @remote_session_id: Client identifier for the session */
+       s32 remote_session_id;
+       /** @flags: Mapping flags (cache attributes, permissions) */
+       u32 flags;
+       /** @vaddr: Virtual address hint for the DSP mapping */
+       u64 vaddr;
+       /** @num: Size of the physical page descriptor array in bytes */
+       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 {
+       /** @vaddrout: DSP virtual address assigned to the mapped buffer */
+       u64 vaddrout;
+};
+
 void qda_fastrpc_context_free(struct kref *ref);
 struct fastrpc_invoke_context *qda_fastrpc_context_alloc(void);
 int qda_fastrpc_prepare_args(struct fastrpc_invoke_context *ctx, char __user 
*argp);
 int qda_fastrpc_get_header_size(struct fastrpc_invoke_context *ctx, size_t 
*out_size);
 int qda_fastrpc_invoke_pack(struct fastrpc_invoke_context *ctx, struct qda_msg 
*msg);
 int qda_fastrpc_invoke_unpack(struct fastrpc_invoke_context *ctx, struct 
qda_msg *msg);
+int qda_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 33f0a798ad13..283eb7535c45 100644
--- a/drivers/accel/qda/qda_ioctl.c
+++ b/drivers/accel/qda/qda_ioctl.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 #include <drm/drm_ioctl.h>
+#include <drm/drm_print.h>
 #include <drm/qda_accel.h>
 #include "qda_drv.h"
 #include "qda_fastrpc.h"
@@ -178,6 +179,10 @@ static int fastrpc_invoke(int type, struct drm_device 
*dev, void *data,
        if (err)
                goto err_context_free;
 
+       err = qda_fastrpc_return_result(ctx, (char __user *)data);
+       if (err)
+               goto err_context_free;
+
        fastrpc_context_put_id(ctx, qdev);
        kref_put(&ctx->refcount, qda_fastrpc_context_free);
        return 0;
@@ -218,6 +223,37 @@ int qda_release_dsp_process(struct qda_dev *qdev, struct 
drm_file *file_priv)
        return fastrpc_invoke(FASTRPC_RMID_INIT_RELEASE, &qdev->drm_dev, NULL, 
file_priv);
 }
 
+/**
+ * qda_ioctl_mmap() - Map memory to DSP address space
+ * @dev: DRM device structure
+ * @data: User-space data (struct drm_qda_mem_map)
+ * @file_priv: DRM file private data
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_ioctl_mmap(struct drm_device *dev, void *data, struct drm_file 
*file_priv)
+{
+       struct drm_qda_mem_map *map_req;
+
+       if (!data)
+               return -EINVAL;
+
+       map_req = (struct drm_qda_mem_map *)data;
+
+       if (map_req->pad)
+               return -EINVAL;
+
+       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:
+               drm_err(dev, "Invalid map request type: %u\n", 
map_req->request);
+               return -EINVAL;
+       }
+}
+
 /**
  * qda_ioctl_invoke() - Perform a dynamic FastRPC method invocation
  * @dev: DRM device structure
diff --git a/drivers/accel/qda/qda_ioctl.h b/drivers/accel/qda/qda_ioctl.h
index 192565434363..457ceccede08 100644
--- a/drivers/accel/qda/qda_ioctl.h
+++ b/drivers/accel/qda/qda_ioctl.h
@@ -13,5 +13,6 @@ int qda_ioctl_init_create(struct drm_device *dev, void *data, 
struct drm_file *f
 int qda_ioctl_gem_create(struct drm_device *dev, void *data, struct drm_file 
*file_priv);
 int qda_ioctl_gem_mmap_offset(struct drm_device *dev, void *data, struct 
drm_file *file_priv);
 int qda_ioctl_invoke(struct drm_device *dev, void *data, struct drm_file 
*file_priv);
+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 711e2523a570..173f59abd361 100644
--- a/include/uapi/drm/qda_accel.h
+++ b/include/uapi/drm/qda_accel.h
@@ -21,8 +21,9 @@ extern "C" {
 #define DRM_QDA_QUERY          0x00
 #define DRM_QDA_GEM_CREATE             0x01
 #define DRM_QDA_GEM_MMAP_OFFSET        0x02
-/* Command number 0x03 reserved for INIT_ATTACH; 0x05-0x06 reserved for MAP, 
MUNMAP */
+/* Command number 0x03 reserved for INIT_ATTACH; 0x06 reserved for MUNMAP */
 #define DRM_QDA_REMOTE_SESSION_CREATE          0x04
+#define DRM_QDA_REMOTE_MAP                     0x05
 #define DRM_QDA_REMOTE_INVOKE                  0x07
 
 /*
@@ -41,9 +42,15 @@ extern "C" {
 #define DRM_IOCTL_QDA_REMOTE_SESSION_CREATE                                    
\
        DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_REMOTE_SESSION_CREATE,              
\
                 struct drm_qda_init_create)
+#define DRM_IOCTL_QDA_REMOTE_MAP       DRM_IOWR(DRM_COMMAND_BASE + 
DRM_QDA_REMOTE_MAP, \
+                                         struct drm_qda_mem_map)
 #define DRM_IOCTL_QDA_REMOTE_INVOKE    DRM_IOWR(DRM_COMMAND_BASE + 
DRM_QDA_REMOTE_INVOKE, \
                                          struct drm_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")
@@ -145,6 +152,42 @@ struct drm_qda_invoke_args {
        __u64 args;
 };
 
+/**
+ * struct drm_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: DMA-BUF file descriptor of the buffer to map
+ * @attrs: Mapping attributes (used for ATTR request)
+ * @offset: Offset within buffer (used for ATTR request)
+ * @pad: Padding for 64-bit alignment (must be zero)
+ * @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 drm_qda_mem_map {
+       __u32 request;
+       __u32 flags;
+       __s32 fd;
+       __u32 attrs;
+       __u32 offset;
+       __u32 pad;
+       __u64 vaddrin;
+       __u64 size;
+       __u64 vaddrout;
+};
+
 #if defined(__cplusplus)
 }
 #endif

-- 
2.34.1


Reply via email to