[PATCH 12/15] accel/qda: Add FastRPC invocation support
From: Ekansh Gupta via B4 Relay
Date: Tue May 19 2026 - 02:28:59 EST
From: Ekansh Gupta <ekansh.gupta@xxxxxxxxxxxxxxxx>
Implement the FastRPC remote procedure call path, allowing user-space
to invoke methods on the DSP via DRM_IOCTL_QDA_REMOTE_INVOKE.
qda_fastrpc.c / qda_fastrpc.h
Implements the FastRPC protocol layer: argument marshalling
(qda_fastrpc_invoke_pack), response unmarshalling
(qda_fastrpc_invoke_unpack), and invocation context lifecycle
management. Each invocation allocates a fastrpc_invoke_context
which tracks buffer descriptors, GEM objects, and the completion
used to synchronise with the DSP response.
Buffer arguments are handled in three ways:
- DMA-BUF fd: imported via PRIME, IOMMU-mapped dma_addr used
- Direct (inline): copied into the GEM-backed message buffer
- DMA handle: fd forwarded to DSP, physical page descriptor computed
qda_rpmsg.c
Implements qda_rpmsg_send_msg() which sends the wire-format
fastrpc_msg (embedded as the first member of qda_msg) directly
via rpmsg_send(), and qda_rpmsg_wait_for_rsp() which blocks on
the context completion. The RPMsg callback dispatches responses
to waiting contexts via the ctx_xa XArray.
qda_ioctl.c
qda_ioctl_invoke() drives the full invocation lifecycle:
allocate context → assign XArray ID → prepare args → allocate
GEM message buffer → pack → send → wait → unpack → free.
qda_drv.h / qda_drv.c
qda_dev gains ctx_xa (XArray for in-flight context lookup) and
remote_session_id_counter (atomic counter for session IDs).
qda_file_priv gains remote_session_id for per-session tracking.
include/uapi/drm/qda_accel.h
Adds DRM_IOCTL_QDA_REMOTE_INVOKE (command 0x07; command numbers
0x03–0x06 are reserved) and the associated drm_qda_invoke_args
and drm_qda_fastrpc_invoke_args structures.
Assisted-by: Claude:claude-4-6-sonnet
Signed-off-by: Ekansh Gupta <ekansh.gupta@xxxxxxxxxxxxxxxx>
---
drivers/accel/qda/Makefile | 1 +
drivers/accel/qda/qda_drv.c | 17 ++
drivers/accel/qda/qda_drv.h | 8 +
drivers/accel/qda/qda_fastrpc.c | 597 ++++++++++++++++++++++++++++++++++++++++
drivers/accel/qda/qda_fastrpc.h | 271 ++++++++++++++++++
drivers/accel/qda/qda_ioctl.c | 104 +++++++
drivers/accel/qda/qda_ioctl.h | 1 +
drivers/accel/qda/qda_rpmsg.c | 136 ++++++++-
drivers/accel/qda/qda_rpmsg.h | 17 ++
include/uapi/drm/qda_accel.h | 39 +++
10 files changed, 1189 insertions(+), 2 deletions(-)
diff --git a/drivers/accel/qda/Makefile b/drivers/accel/qda/Makefile
index fb092e56d7f3..2d10420cd1ec 100644
--- a/drivers/accel/qda/Makefile
+++ b/drivers/accel/qda/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_ACCEL_QDA) := qda.o
qda-y := \
qda_cb.o \
qda_drv.o \
+ qda_fastrpc.o \
qda_gem.o \
qda_ioctl.o \
qda_memory_dma.o \
diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c
index ef8bd573b836..704c7d3127d2 100644
--- a/drivers/accel/qda/qda_drv.c
+++ b/drivers/accel/qda/qda_drv.c
@@ -26,6 +26,8 @@ static int qda_open(struct drm_device *dev, struct drm_file *file)
qda_file_priv->pid = current->pid;
qda_file_priv->qda_dev = qda_dev_from_drm(dev);
+ qda_file_priv->remote_session_id =
+ atomic_inc_return(&qda_file_priv->qda_dev->remote_session_id_counter);
file->driver_priv = qda_file_priv;
return 0;
@@ -57,6 +59,7 @@ static const struct drm_ioctl_desc qda_ioctls[] = {
DRM_IOCTL_DEF_DRV(QDA_QUERY, qda_ioctl_query, 0),
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_INVOKE, qda_ioctl_invoke, 0),
};
static const struct drm_driver qda_drm_driver = {
@@ -93,6 +96,17 @@ static void cleanup_memory_manager(struct qda_dev *qdev)
}
}
+static void cleanup_device_resources(struct qda_dev *qdev)
+{
+ xa_destroy(&qdev->ctx_xa);
+}
+
+static void init_device_resources(struct qda_dev *qdev)
+{
+ atomic_set(&qdev->remote_session_id_counter, 0);
+ xa_init_flags(&qdev->ctx_xa, XA_FLAGS_ALLOC1);
+}
+
static int init_memory_manager(struct qda_dev *qdev)
{
qdev->iommu_mgr = kzalloc_obj(*qdev->iommu_mgr);
@@ -106,6 +120,7 @@ void qda_deinit_device(struct qda_dev *qdev)
{
mutex_destroy(&qdev->import_lock);
cleanup_memory_manager(qdev);
+ cleanup_device_resources(qdev);
}
int qda_init_device(struct qda_dev *qdev)
@@ -114,10 +129,12 @@ int qda_init_device(struct qda_dev *qdev)
mutex_init(&qdev->import_lock);
qdev->current_import_file_priv = NULL;
+ init_device_resources(qdev);
ret = init_memory_manager(qdev);
if (ret) {
drm_err(&qdev->drm_dev, "Failed to initialize memory manager: %d\n", ret);
+ cleanup_device_resources(qdev);
mutex_destroy(&qdev->import_lock);
}
diff --git a/drivers/accel/qda/qda_drv.h b/drivers/accel/qda/qda_drv.h
index 96ce4135e2d9..420cccff42bf 100644
--- a/drivers/accel/qda/qda_drv.h
+++ b/drivers/accel/qda/qda_drv.h
@@ -6,10 +6,12 @@
#ifndef __QDA_DRV_H__
#define __QDA_DRV_H__
+#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/rpmsg.h>
#include <linux/types.h>
+#include <linux/xarray.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
@@ -28,6 +30,8 @@ struct qda_file_priv {
struct qda_iommu_device *assigned_iommu_dev;
/** @pid: Process ID for tracking */
pid_t pid;
+ /** @remote_session_id: Unique session identifier */
+ u32 remote_session_id;
};
/**
@@ -51,8 +55,12 @@ struct qda_dev {
struct mutex import_lock;
/** @current_import_file_priv: Current file_priv during prime import */
struct drm_file *current_import_file_priv;
+ /** @ctx_xa: XArray for FastRPC context management */
+ struct xarray ctx_xa;
/** @dsp_name: Name of the DSP domain (e.g. "cdsp", "adsp") */
const char *dsp_name;
+ /** @remote_session_id_counter: Atomic counter for unique session IDs */
+ atomic_t remote_session_id_counter;
};
/**
diff --git a/drivers/accel/qda/qda_fastrpc.c b/drivers/accel/qda/qda_fastrpc.c
new file mode 100644
index 000000000000..0ec37175a098
--- /dev/null
+++ b/drivers/accel/qda/qda_fastrpc.c
@@ -0,0 +1,597 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/sort.h>
+#include <linux/completion.h>
+#include <linux/dma-buf.h>
+#include <drm/drm_gem.h>
+#include "qda_fastrpc.h"
+#include "qda_drv.h"
+#include "qda_gem.h"
+#include "qda_memory_manager.h"
+#include "qda_prime.h"
+
+/**
+ * get_gem_obj_from_dmabuf_fd() - Import a DMA-BUF fd and return the GEM object
+ * @ctx: FastRPC invocation context
+ * @dmabuf_fd: DMA-BUF file descriptor supplied by user space
+ * @gem_obj: Output GEM object (caller must call drm_gem_object_put() when done)
+ *
+ * Imports the DMA-BUF fd into the QDA device via qda_prime_fd_to_handle()
+ * (which performs IOMMU device assignment for newly imported buffers) and
+ * then looks up the resulting GEM object. The caller is responsible for
+ * calling drm_gem_object_put() on the returned object.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int get_gem_obj_from_dmabuf_fd(struct fastrpc_invoke_context *ctx,
+ int dmabuf_fd,
+ struct drm_gem_object **gem_obj)
+{
+ struct drm_device *dev = ctx->file_priv->minor->dev;
+ u32 handle;
+ int ret;
+
+ ret = qda_prime_fd_to_handle(dev, ctx->file_priv, dmabuf_fd, &handle);
+ if (ret)
+ return ret;
+
+ *gem_obj = drm_gem_object_lookup(ctx->file_priv, handle);
+ if (!*gem_obj)
+ return -ENOENT;
+
+ return 0;
+}
+
+static void setup_pages_from_gem_obj(struct qda_gem_obj *qda_gem_obj,
+ struct fastrpc_phy_page *pages)
+{
+ pages->addr = qda_gem_obj->dma_addr;
+ pages->size = qda_gem_obj->size;
+}
+
+static u64 calculate_vma_offset(u64 user_ptr)
+{
+ struct vm_area_struct *vma;
+ u64 user_ptr_page_mask = user_ptr & PAGE_MASK;
+ u64 vma_offset = 0;
+
+ mmap_read_lock(current->mm);
+ vma = find_vma(current->mm, user_ptr);
+ if (vma)
+ vma_offset = user_ptr_page_mask - vma->vm_start;
+ mmap_read_unlock(current->mm);
+
+ return vma_offset;
+}
+
+static u64 calculate_page_aligned_size(u64 ptr, u64 len)
+{
+ u64 pg_start = (ptr & PAGE_MASK) >> PAGE_SHIFT;
+ u64 pg_end = ((ptr + len - 1) & PAGE_MASK) >> PAGE_SHIFT;
+ u64 aligned_size = (pg_end - pg_start + 1) * PAGE_SIZE;
+
+ return aligned_size;
+}
+
+static struct fastrpc_invoke_buf *fastrpc_invoke_buf_start(union fastrpc_remote_arg *pra, int len)
+{
+ return (struct fastrpc_invoke_buf *)(&pra[len]);
+}
+
+static struct fastrpc_phy_page *fastrpc_phy_page_start(struct fastrpc_invoke_buf *buf, int len)
+{
+ return (struct fastrpc_phy_page *)(&buf[len]);
+}
+
+static int fastrpc_get_meta_size(struct fastrpc_invoke_context *ctx)
+{
+ int size = 0;
+
+ size = (sizeof(struct fastrpc_remote_buf) +
+ sizeof(struct fastrpc_invoke_buf) +
+ sizeof(struct fastrpc_phy_page)) * ctx->nscalars +
+ sizeof(u64) * FASTRPC_MAX_FDLIST +
+ sizeof(u32) * FASTRPC_MAX_CRCLIST;
+
+ return size;
+}
+
+static u64 fastrpc_get_payload_size(struct fastrpc_invoke_context *ctx, int metalen)
+{
+ u64 size = 0;
+ int oix;
+
+ size = ALIGN(metalen, FASTRPC_ALIGN);
+
+ for (oix = 0; oix < ctx->nbufs; oix++) {
+ int i = ctx->olaps[oix].raix;
+
+ if (ctx->args[i].fd == 0 || ctx->args[i].fd == -1) {
+ if (ctx->olaps[oix].offset == 0)
+ size = ALIGN(size, FASTRPC_ALIGN);
+
+ size += (ctx->olaps[oix].mend - ctx->olaps[oix].mstart);
+ }
+ }
+
+ return size;
+}
+
+/**
+ * qda_fastrpc_context_free() - Free an invocation context
+ * @ref: Reference counter embedded in the context
+ *
+ * Called when the reference count reaches zero; releases all resources
+ * associated with the invocation context.
+ */
+void qda_fastrpc_context_free(struct kref *ref)
+{
+ struct fastrpc_invoke_context *ctx;
+ int i;
+
+ ctx = container_of(ref, struct fastrpc_invoke_context, refcount);
+ if (ctx->gem_objs) {
+ for (i = 0; i < ctx->nscalars; ++i) {
+ if (ctx->gem_objs[i])
+ drm_gem_object_put(ctx->gem_objs[i]);
+ }
+ kfree(ctx->gem_objs);
+ }
+
+ if (ctx->msg_gem_obj)
+ drm_gem_object_put(&ctx->msg_gem_obj->base);
+
+ kfree(ctx->olaps);
+
+ kfree(ctx->args);
+ kfree(ctx->req);
+ kfree(ctx->rsp);
+ kfree(ctx->input_pages);
+ kfree(ctx->inbuf);
+
+ kfree(ctx);
+}
+
+#define CMP(aa, bb) ((aa) == (bb) ? 0 : (aa) < (bb) ? -1 : 1)
+
+static int olaps_cmp(const void *a, const void *b)
+{
+ struct fastrpc_buf_overlap *pa = (struct fastrpc_buf_overlap *)a;
+ struct fastrpc_buf_overlap *pb = (struct fastrpc_buf_overlap *)b;
+ /* sort with lowest starting buffer first */
+ int st = CMP(pa->start, pb->start);
+ /* sort with highest ending buffer first */
+ int ed = CMP(pb->end, pa->end);
+
+ return st == 0 ? ed : st;
+}
+
+static void fastrpc_get_buff_overlaps(struct fastrpc_invoke_context *ctx)
+{
+ u64 max_end = 0;
+ int i;
+
+ for (i = 0; i < ctx->nbufs; ++i) {
+ ctx->olaps[i].start = ctx->args[i].ptr;
+ ctx->olaps[i].end = ctx->olaps[i].start + ctx->args[i].length;
+ ctx->olaps[i].raix = i;
+ }
+
+ sort(ctx->olaps, ctx->nbufs, sizeof(*ctx->olaps), olaps_cmp, NULL);
+
+ for (i = 0; i < ctx->nbufs; ++i) {
+ if (ctx->olaps[i].start < max_end) {
+ ctx->olaps[i].mstart = max_end;
+ ctx->olaps[i].mend = ctx->olaps[i].end;
+ ctx->olaps[i].offset = max_end - ctx->olaps[i].start;
+
+ if (ctx->olaps[i].end > max_end) {
+ max_end = ctx->olaps[i].end;
+ } else {
+ ctx->olaps[i].mend = 0;
+ ctx->olaps[i].mstart = 0;
+ }
+ } else {
+ ctx->olaps[i].mend = ctx->olaps[i].end;
+ ctx->olaps[i].mstart = ctx->olaps[i].start;
+ ctx->olaps[i].offset = 0;
+ max_end = ctx->olaps[i].end;
+ }
+ }
+}
+
+/**
+ * qda_fastrpc_context_alloc() - Allocate a new FastRPC invocation context
+ *
+ * Return: Pointer to allocated context, or ERR_PTR on failure
+ */
+struct fastrpc_invoke_context *qda_fastrpc_context_alloc(void)
+{
+ struct fastrpc_invoke_context *ctx = NULL;
+
+ ctx = kzalloc_obj(*ctx);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&ctx->node);
+
+ ctx->retval = -1;
+ ctx->pid = current->pid;
+ init_completion(&ctx->work);
+ ctx->msg_gem_obj = NULL;
+ kref_init(&ctx->refcount);
+
+ return ctx;
+}
+
+/*
+ * process_fd_buffer() - Handle an in/out buffer argument backed by a DMA-BUF fd
+ *
+ * args[i].fd is a DMA-BUF fd. We import it to obtain the GEM object and its
+ * IOMMU-mapped dma_addr for the physical page descriptor. The DSP uses the
+ * physical address directly for this buffer type; the fd is not forwarded.
+ */
+static int process_fd_buffer(struct fastrpc_invoke_context *ctx, int i,
+ union fastrpc_remote_arg *rpra, struct fastrpc_phy_page *pages)
+{
+ struct drm_gem_object *gem_obj;
+ struct qda_gem_obj *qda_gem_obj;
+ int err;
+ u64 len = ctx->args[i].length;
+ u64 vma_offset;
+
+ err = get_gem_obj_from_dmabuf_fd(ctx, ctx->args[i].fd, &gem_obj);
+ if (err)
+ return err;
+
+ ctx->gem_objs[i] = gem_obj;
+ qda_gem_obj = to_qda_gem_obj(gem_obj);
+
+ rpra[i].buf.pv = (u64)ctx->args[i].ptr;
+
+ pages[i].addr = qda_gem_obj->dma_addr;
+
+ vma_offset = calculate_vma_offset(ctx->args[i].ptr);
+ pages[i].addr += vma_offset;
+ pages[i].size = calculate_page_aligned_size(ctx->args[i].ptr, len);
+
+ return 0;
+}
+
+static int process_direct_buffer(struct fastrpc_invoke_context *ctx, int i, int oix,
+ union fastrpc_remote_arg *rpra, struct fastrpc_phy_page *pages,
+ uintptr_t *args, u64 *rlen, u64 pkt_size)
+{
+ int mlen;
+ u64 len = ctx->args[i].length;
+ int inbufs = ctx->inbufs;
+
+ if (ctx->olaps[oix].offset == 0) {
+ *rlen -= ALIGN(*args, FASTRPC_ALIGN) - *args;
+ *args = ALIGN(*args, FASTRPC_ALIGN);
+ }
+
+ mlen = ctx->olaps[oix].mend - ctx->olaps[oix].mstart;
+
+ if (*rlen < mlen)
+ return -ENOSPC;
+
+ rpra[i].buf.pv = *args - ctx->olaps[oix].offset;
+
+ pages[i].addr = ctx->msg->phys - ctx->olaps[oix].offset + (pkt_size - *rlen);
+ pages[i].addr = pages[i].addr & PAGE_MASK;
+ pages[i].size = calculate_page_aligned_size(rpra[i].buf.pv, len);
+
+ *args = *args + mlen;
+ *rlen -= mlen;
+
+ if (i < inbufs) {
+ void *dst = (void *)(uintptr_t)rpra[i].buf.pv;
+ void *src = (void *)(uintptr_t)ctx->args[i].ptr;
+
+ /*
+ * For user-space invocations (INVOKE_DYNAMIC), ptr is a user
+ * virtual address and must be copied safely. For all other
+ * (kernel-internal) invocations, ptr is a kernel address set
+ * by the driver itself and can be copied directly.
+ */
+ if (ctx->type == FASTRPC_RMID_INVOKE_DYNAMIC) {
+ if (copy_from_user(dst, (void __user *)src, len))
+ return -EFAULT;
+ } else {
+ memcpy(dst, src, len);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * process_dma_handle() - Handle a DMA-handle scalar argument
+ *
+ * args[i].fd is a DMA-BUF fd. We import it to get the physical page
+ * descriptor for the kernel, but forward the original DMA-BUF fd to the
+ * DSP in rpra[i].dma.fd so the DSP can identify the buffer by its fd.
+ */
+static int process_dma_handle(struct fastrpc_invoke_context *ctx, int i,
+ union fastrpc_remote_arg *rpra, struct fastrpc_phy_page *pages)
+{
+ if (ctx->args[i].fd > 0) {
+ struct drm_gem_object *gem_obj;
+ struct qda_gem_obj *qda_gem_obj;
+ int err;
+
+ err = get_gem_obj_from_dmabuf_fd(ctx, ctx->args[i].fd, &gem_obj);
+ if (err)
+ return err;
+
+ ctx->gem_objs[i] = gem_obj;
+ qda_gem_obj = to_qda_gem_obj(gem_obj);
+
+ setup_pages_from_gem_obj(qda_gem_obj, &pages[i]);
+
+ /* Forward the original DMA-BUF fd to the DSP */
+ rpra[i].dma.fd = ctx->args[i].fd;
+ rpra[i].dma.len = ctx->args[i].length;
+ rpra[i].dma.offset = (u64)ctx->args[i].ptr;
+ } else {
+ rpra[i].buf.pv = ctx->args[i].ptr;
+ rpra[i].buf.len = ctx->args[i].length;
+ }
+
+ return 0;
+}
+
+/**
+ * qda_fastrpc_get_header_size() - Compute the FastRPC message header size
+ * @ctx: FastRPC invocation context
+ * @out_size: Pointer to store the aligned packet size in bytes
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_fastrpc_get_header_size(struct fastrpc_invoke_context *ctx, size_t *out_size)
+{
+ ctx->inbufs = REMOTE_SCALARS_INBUFS(ctx->sc);
+ ctx->metalen = fastrpc_get_meta_size(ctx);
+ ctx->pkt_size = fastrpc_get_payload_size(ctx, ctx->metalen);
+
+ ctx->aligned_pkt_size = PAGE_ALIGN(ctx->pkt_size);
+ if (ctx->aligned_pkt_size == 0)
+ return -EINVAL;
+
+ *out_size = ctx->aligned_pkt_size;
+ return 0;
+}
+
+static int fastrpc_get_args(struct fastrpc_invoke_context *ctx)
+{
+ union fastrpc_remote_arg *rpra;
+ struct fastrpc_invoke_buf *list;
+ struct fastrpc_phy_page *pages;
+ int i, oix, err = 0;
+ u64 rlen;
+ uintptr_t args;
+ size_t hdr_size;
+
+ ctx->inbufs = REMOTE_SCALARS_INBUFS(ctx->sc);
+ err = qda_fastrpc_get_header_size(ctx, &hdr_size);
+ if (err)
+ return err;
+
+ ctx->msg->buf = ctx->msg_gem_obj->virt;
+ ctx->msg->phys = ctx->msg_gem_obj->dma_addr;
+
+ memset(ctx->msg->buf, 0, ctx->aligned_pkt_size);
+
+ rpra = (union fastrpc_remote_arg *)ctx->msg->buf;
+ ctx->list = fastrpc_invoke_buf_start(rpra, ctx->nscalars);
+ ctx->pages = fastrpc_phy_page_start(ctx->list, ctx->nscalars);
+ list = ctx->list;
+ pages = ctx->pages;
+ args = (uintptr_t)ctx->msg->buf + ctx->metalen;
+ rlen = ctx->pkt_size - ctx->metalen;
+ ctx->rpra = rpra;
+
+ for (oix = 0; oix < ctx->nbufs; ++oix) {
+ i = ctx->olaps[oix].raix;
+
+ rpra[i].buf.pv = 0;
+ rpra[i].buf.len = ctx->args[i].length;
+ list[i].num = ctx->args[i].length ? 1 : 0;
+ list[i].pgidx = i;
+
+ if (!ctx->args[i].length)
+ continue;
+
+ if (ctx->args[i].fd > 0)
+ err = process_fd_buffer(ctx, i, rpra, pages);
+ else
+ err = process_direct_buffer(ctx, i, oix, rpra, pages, &args, &rlen,
+ ctx->pkt_size);
+
+ if (err)
+ goto bail_gem;
+ }
+
+ for (i = ctx->nbufs; i < ctx->nscalars; ++i) {
+ list[i].num = ctx->args[i].length ? 1 : 0;
+ list[i].pgidx = i;
+
+ err = process_dma_handle(ctx, i, rpra, pages);
+ if (err)
+ goto bail_gem;
+ }
+
+ return 0;
+
+bail_gem:
+ if (ctx->msg_gem_obj) {
+ drm_gem_object_put(&ctx->msg_gem_obj->base);
+ ctx->msg_gem_obj = NULL;
+ }
+
+ return err;
+}
+
+static int fastrpc_put_args(struct fastrpc_invoke_context *ctx, struct qda_msg *msg)
+{
+ union fastrpc_remote_arg *rpra;
+ int i, err = 0;
+
+ if (!ctx)
+ return -EINVAL;
+
+ rpra = ctx->rpra;
+ if (!rpra)
+ return -EINVAL;
+
+ for (i = ctx->inbufs; i < ctx->nbufs; ++i) {
+ if (ctx->args[i].fd <= 0) {
+ void *src = (void *)(uintptr_t)rpra[i].buf.pv;
+ void *dst = (void *)(uintptr_t)ctx->args[i].ptr;
+ u64 len = rpra[i].buf.len;
+
+ if (ctx->type == FASTRPC_RMID_INVOKE_DYNAMIC)
+ err = copy_to_user((void __user *)dst, src, len) ? -EFAULT : 0;
+ else
+ memcpy(dst, src, len);
+ if (err)
+ break;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * qda_fastrpc_invoke_pack() - Pack an invocation context into a QDA message
+ * @ctx: FastRPC invocation context
+ * @msg: QDA message structure to pack into
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_fastrpc_invoke_pack(struct fastrpc_invoke_context *ctx,
+ struct qda_msg *msg)
+{
+ int err = 0;
+
+ if (ctx->handle == FASTRPC_INIT_HANDLE)
+ msg->fastrpc.remote_session_id = 0;
+ else
+ msg->fastrpc.remote_session_id = ctx->remote_session_id;
+
+ ctx->msg = msg;
+
+ err = fastrpc_get_args(ctx);
+ if (err)
+ return err;
+
+ dma_wmb();
+
+ msg->fastrpc.tid = ctx->pid;
+ msg->fastrpc.ctx = ctx->ctxid | ctx->pd;
+ msg->fastrpc.handle = ctx->handle;
+ msg->fastrpc.sc = ctx->sc;
+ msg->fastrpc.addr = ctx->msg->phys;
+ msg->fastrpc.size = roundup(ctx->pkt_size, PAGE_SIZE);
+ msg->fastrpc_ctx = ctx;
+ msg->file_priv = ctx->file_priv;
+
+ return 0;
+}
+
+/**
+ * qda_fastrpc_invoke_unpack() - Unpack a response message into an invocation context
+ * @ctx: FastRPC invocation context
+ * @msg: QDA message structure to unpack from
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_fastrpc_invoke_unpack(struct fastrpc_invoke_context *ctx,
+ struct qda_msg *msg)
+{
+ int err;
+
+ dma_rmb();
+
+ err = fastrpc_put_args(ctx, msg);
+ if (err)
+ return err;
+
+ err = ctx->retval;
+ return err;
+}
+
+static int fastrpc_prepare_args_invoke(struct fastrpc_invoke_context *ctx, char __user *argp)
+{
+ struct drm_qda_invoke_args invoke_args;
+ struct drm_qda_fastrpc_invoke_args *args = NULL;
+ u32 nscalars;
+
+ /* argp is DRM ioctl data (kernel pointer); args pointer within it is user-space */
+ memcpy(&invoke_args, argp, sizeof(invoke_args));
+
+ ctx->handle = invoke_args.handle;
+ ctx->sc = invoke_args.sc;
+
+ nscalars = REMOTE_SCALARS_LENGTH(ctx->sc);
+ if (!nscalars) {
+ ctx->args = NULL;
+ return 0;
+ }
+
+ args = kcalloc(nscalars, sizeof(*args), GFP_KERNEL);
+ if (!args)
+ return -ENOMEM;
+
+ if (copy_from_user(args, u64_to_user_ptr(invoke_args.args),
+ nscalars * sizeof(*args))) {
+ kfree(args);
+ return -EFAULT;
+ }
+
+ ctx->args = args;
+ return 0;
+}
+
+/**
+ * qda_fastrpc_prepare_args() - Prepare arguments for a FastRPC invocation
+ * @ctx: FastRPC invocation context
+ * @argp: User-space pointer to invocation arguments
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_fastrpc_prepare_args(struct fastrpc_invoke_context *ctx, char __user *argp)
+{
+ int err;
+
+ switch (ctx->type) {
+ case FASTRPC_RMID_INVOKE_DYNAMIC:
+ err = fastrpc_prepare_args_invoke(ctx, argp);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (err)
+ return err;
+
+ ctx->nscalars = REMOTE_SCALARS_LENGTH(ctx->sc);
+ ctx->nbufs = REMOTE_SCALARS_INBUFS(ctx->sc) + REMOTE_SCALARS_OUTBUFS(ctx->sc);
+
+ if (ctx->nscalars) {
+ ctx->gem_objs = kcalloc(ctx->nscalars, sizeof(*ctx->gem_objs), GFP_KERNEL);
+ if (!ctx->gem_objs)
+ return -ENOMEM;
+ ctx->olaps = kcalloc(ctx->nscalars, sizeof(*ctx->olaps), GFP_KERNEL);
+ if (!ctx->olaps) {
+ kfree(ctx->gem_objs);
+ ctx->gem_objs = NULL;
+ return -ENOMEM;
+ }
+ fastrpc_get_buff_overlaps(ctx);
+ }
+
+ return err;
+}
diff --git a/drivers/accel/qda/qda_fastrpc.h b/drivers/accel/qda/qda_fastrpc.h
new file mode 100644
index 000000000000..ce77baeccfba
--- /dev/null
+++ b/drivers/accel/qda/qda_fastrpc.h
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __QDA_FASTRPC_H__
+#define __QDA_FASTRPC_H__
+
+#include <linux/completion.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/qda_accel.h>
+
+/* Forward declarations */
+struct qda_gem_obj;
+
+/*
+ * FastRPC scalar extraction macros
+ *
+ * These macros extract different fields from the scalar value that describes
+ * the arguments passed in a FastRPC invocation.
+ */
+#define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff)
+#define REMOTE_SCALARS_OUTBUFS(sc) (((sc) >> 8) & 0x0ff)
+#define REMOTE_SCALARS_INHANDLES(sc) (((sc) >> 4) & 0x0f)
+#define REMOTE_SCALARS_OUTHANDLES(sc) ((sc) & 0x0f)
+#define REMOTE_SCALARS_LENGTH(sc) (REMOTE_SCALARS_INBUFS(sc) + \
+ REMOTE_SCALARS_OUTBUFS(sc) + \
+ REMOTE_SCALARS_INHANDLES(sc) + \
+ REMOTE_SCALARS_OUTHANDLES(sc))
+
+/* FastRPC configuration constants */
+#define FASTRPC_ALIGN 128 /* Alignment requirement */
+#define FASTRPC_MAX_FDLIST 16 /* Maximum file descriptors */
+#define FASTRPC_MAX_CRCLIST 64 /* Maximum CRC list entries */
+
+/*
+ * FastRPC scalar construction macros
+ *
+ * These macros build the scalar value that describes the arguments
+ * for a FastRPC invocation.
+ */
+#define FASTRPC_BUILD_SCALARS(attr, method, in, out, oin, oout) \
+ (((attr & 0x07) << 29) | \
+ ((method & 0x1f) << 24) | \
+ ((in & 0xff) << 16) | \
+ ((out & 0xff) << 8) | \
+ ((oin & 0x0f) << 4) | \
+ (oout & 0x0f))
+
+#define FASTRPC_SCALARS(method, in, out) \
+ FASTRPC_BUILD_SCALARS(0, method, in, out, 0, 0)
+
+/**
+ * struct fastrpc_buf_overlap - Buffer overlap tracking structure
+ *
+ * Tracks overlapping buffer regions to optimise memory mapping and avoid
+ * redundant mappings of the same physical memory.
+ */
+struct fastrpc_buf_overlap {
+ /** @start: Start address of the buffer in user virtual address space */
+ u64 start;
+ /** @end: End address of the buffer in user virtual address space */
+ u64 end;
+ /** @raix: Remote argument index associated with this overlap */
+ int raix;
+ /** @mstart: Start address of the mapped region */
+ u64 mstart;
+ /** @mend: End address of the mapped region */
+ u64 mend;
+ /** @offset: Offset within the mapped region */
+ u64 offset;
+};
+
+/**
+ * struct fastrpc_remote_dmahandle - Remote DMA handle descriptor
+ */
+struct fastrpc_remote_dmahandle {
+ /** @fd: DMA-BUF file descriptor */
+ s32 fd;
+ /** @offset: Byte offset within the DMA-BUF */
+ u32 offset;
+ /** @len: Length of the region in bytes */
+ u32 len;
+};
+
+/**
+ * struct fastrpc_remote_buf - Remote buffer descriptor
+ */
+struct fastrpc_remote_buf {
+ /** @pv: Buffer pointer (user virtual address) */
+ u64 pv;
+ /** @len: Length of the buffer in bytes */
+ u64 len;
+};
+
+/**
+ * union fastrpc_remote_arg - Remote argument (buffer or DMA handle)
+ */
+union fastrpc_remote_arg {
+ /** @buf: Inline buffer descriptor */
+ struct fastrpc_remote_buf buf;
+ /** @dma: DMA-BUF handle descriptor */
+ struct fastrpc_remote_dmahandle dma;
+};
+
+/**
+ * struct fastrpc_phy_page - Physical page descriptor
+ */
+struct fastrpc_phy_page {
+ /** @addr: Physical (IOMMU) address of the page */
+ u64 addr;
+ /** @size: Size of the contiguous region in bytes */
+ u64 size;
+};
+
+/**
+ * struct fastrpc_invoke_buf - Invoke buffer descriptor
+ */
+struct fastrpc_invoke_buf {
+ /** @num: Number of contiguous physical regions */
+ u32 num;
+ /** @pgidx: Index into the physical page array */
+ u32 pgidx;
+};
+
+/**
+ * struct fastrpc_msg - FastRPC wire message for remote invocations
+ *
+ * Sent to the remote processor via RPMsg. This is the exact layout
+ * the DSP expects; do not reorder or add fields without DSP firmware
+ * coordination.
+ */
+struct fastrpc_msg {
+ /** @remote_session_id: Session identifier on the remote processor */
+ int remote_session_id;
+ /** @tid: Thread ID of the invoking thread */
+ int tid;
+ /** @ctx: Context identifier for matching request/response */
+ u64 ctx;
+ /** @handle: Handle of the remote method to invoke */
+ u32 handle;
+ /** @sc: Scalars value encoding in/out buffer counts */
+ u32 sc;
+ /** @addr: Physical address of the message payload buffer */
+ u64 addr;
+ /** @size: Size of the message payload in bytes */
+ u64 size;
+};
+
+/**
+ * struct qda_msg - FastRPC message with kernel-internal bookkeeping
+ *
+ * The wire-format portion is kept in the embedded @fastrpc member (must
+ * be first) so that &qda_msg->fastrpc can be passed directly to
+ * rpmsg_send() without a copy.
+ */
+struct qda_msg {
+ /**
+ * @fastrpc: Wire-format message sent to the DSP via RPMsg.
+ * Must be the first member.
+ */
+ struct fastrpc_msg fastrpc;
+ /** @buf: Kernel virtual address of the payload buffer */
+ void *buf;
+ /** @phys: Physical/DMA address of the payload buffer */
+ u64 phys;
+ /** @ret: Return value from the remote processor */
+ int ret;
+ /** @fastrpc_ctx: Back-pointer to the owning invocation context */
+ struct fastrpc_invoke_context *fastrpc_ctx;
+ /** @file_priv: DRM file private data for GEM object lookup */
+ struct drm_file *file_priv;
+};
+
+/**
+ * struct fastrpc_invoke_context - Remote procedure call invocation context
+ *
+ * Maintains all state for a single remote procedure call, including buffer
+ * management, synchronisation, and result handling.
+ */
+struct fastrpc_invoke_context {
+ /** @node: List node for linking contexts in a queue */
+ struct list_head node;
+ /** @ctxid: Unique context identifier (XArray key shifted left by 4) */
+ u64 ctxid;
+ /** @inbufs: Number of input buffers */
+ int inbufs;
+ /** @outbufs: Number of output buffers */
+ int outbufs;
+ /** @handles: Number of DMA-BUF handle arguments */
+ int handles;
+ /** @nscalars: Total number of scalar arguments */
+ int nscalars;
+ /** @nbufs: Total number of buffer arguments (inbufs + outbufs) */
+ int nbufs;
+ /** @pid: Process ID of the calling process */
+ int pid;
+ /** @retval: Return value from the remote invocation */
+ int retval;
+ /** @metalen: Length of the FastRPC metadata header in bytes */
+ int metalen;
+ /** @remote_session_id: Session identifier on the remote processor */
+ int remote_session_id;
+ /** @pd: Protection domain identifier encoded into the context ID */
+ int pd;
+ /** @type: Invocation type (e.g. FASTRPC_RMID_INVOKE_DYNAMIC) */
+ int type;
+ /** @sc: Scalars value encoding in/out buffer counts */
+ u32 sc;
+ /** @handle: Handle of the remote method being invoked */
+ u32 handle;
+ /** @crc: Pointer to CRC values for data integrity checking */
+ u32 *crc;
+ /** @fdlist: Pointer to array of DMA-BUF file descriptors */
+ u64 *fdlist;
+ /** @pkt_size: Total payload size in bytes */
+ u64 pkt_size;
+ /** @aligned_pkt_size: Page-aligned payload size for GEM allocation */
+ u64 aligned_pkt_size;
+ /** @list: Array of invoke buffer descriptors */
+ struct fastrpc_invoke_buf *list;
+ /** @pages: Array of physical page descriptors for all arguments */
+ struct fastrpc_phy_page *pages;
+ /** @input_pages: Array of physical page descriptors for input buffers */
+ struct fastrpc_phy_page *input_pages;
+ /** @work: Completion used to synchronise with the DSP response */
+ struct completion work;
+ /** @msg: Pointer to the QDA message structure for this invocation */
+ struct qda_msg *msg;
+ /** @rpra: Array of remote procedure arguments */
+ union fastrpc_remote_arg *rpra;
+ /** @gem_objs: Array of GEM objects imported for argument buffers */
+ struct drm_gem_object **gem_objs;
+ /** @args: User-space invoke argument descriptors */
+ struct drm_qda_fastrpc_invoke_args *args;
+ /** @olaps: Array of buffer overlap descriptors for deduplication */
+ struct fastrpc_buf_overlap *olaps;
+ /** @refcount: Reference counter for context lifetime management */
+ struct kref refcount;
+ /** @msg_gem_obj: GEM object backing the message payload buffer */
+ struct qda_gem_obj *msg_gem_obj;
+ /** @file_priv: DRM file private data */
+ struct drm_file *file_priv;
+ /** @init_mem_gem_obj: GEM object for protection domain init memory */
+ struct qda_gem_obj *init_mem_gem_obj;
+ /** @req: Pointer to kernel-internal request buffer */
+ void *req;
+ /** @rsp: Pointer to kernel-internal response buffer */
+ void *rsp;
+ /** @inbuf: Pointer to kernel-internal input buffer */
+ void *inbuf;
+};
+
+/* Remote Method ID table - identifies initialization and control operations */
+#define FASTRPC_RMID_INVOKE_DYNAMIC 0xFFFFFFFF /* Dynamic method invocation */
+
+/* Common handle for initialization operations */
+#define FASTRPC_INIT_HANDLE 0x1
+
+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);
+
+#endif /* __QDA_FASTRPC_H__ */
diff --git a/drivers/accel/qda/qda_ioctl.c b/drivers/accel/qda/qda_ioctl.c
index 1769c85a3e98..c81268c20b04 100644
--- a/drivers/accel/qda/qda_ioctl.c
+++ b/drivers/accel/qda/qda_ioctl.c
@@ -3,8 +3,10 @@
#include <drm/drm_ioctl.h>
#include <drm/qda_accel.h>
#include "qda_drv.h"
+#include "qda_fastrpc.h"
#include "qda_gem.h"
#include "qda_ioctl.h"
+#include "qda_rpmsg.h"
/**
* qda_ioctl_query() - Query DSP device information
@@ -74,3 +76,105 @@ int qda_ioctl_gem_mmap_offset(struct drm_device *dev, void *data, struct drm_fil
return drm_gem_dumb_map_offset(file_priv, dev, args->handle, &args->offset);
}
+
+static int fastrpc_context_get_id(struct fastrpc_invoke_context *ctx, struct qda_dev *qdev)
+{
+ int ret;
+ u32 id;
+
+ if (!qdev)
+ return -EINVAL;
+
+ ret = xa_alloc(&qdev->ctx_xa, &id, ctx, xa_limit_32b, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ ctx->ctxid = id << 4;
+ return 0;
+}
+
+static void fastrpc_context_put_id(struct fastrpc_invoke_context *ctx, struct qda_dev *qdev)
+{
+ if (qdev)
+ xa_erase(&qdev->ctx_xa, ctx->ctxid >> 4);
+}
+
+static int fastrpc_invoke(int type, struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct qda_file_priv *qda_file_priv = file_priv->driver_priv;
+ struct qda_dev *qdev = qda_file_priv->qda_dev;
+ struct qda_msg msg;
+ struct fastrpc_invoke_context *ctx;
+ struct drm_gem_object *gem_obj;
+ int err;
+ size_t hdr_size;
+
+ ctx = qda_fastrpc_context_alloc();
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ err = fastrpc_context_get_id(ctx, qdev);
+ if (err) {
+ kref_put(&ctx->refcount, qda_fastrpc_context_free);
+ return err;
+ }
+
+ ctx->type = type;
+ ctx->file_priv = file_priv;
+ ctx->remote_session_id = qda_file_priv->remote_session_id;
+
+ err = qda_fastrpc_prepare_args(ctx, (char __user *)data);
+ if (err)
+ goto err_context_free;
+
+ err = qda_fastrpc_get_header_size(ctx, &hdr_size);
+ if (err)
+ goto err_context_free;
+
+ gem_obj = qda_gem_create_object(dev, qdev->iommu_mgr, hdr_size, file_priv);
+ if (IS_ERR(gem_obj)) {
+ err = PTR_ERR(gem_obj);
+ goto err_context_free;
+ }
+
+ ctx->msg_gem_obj = to_qda_gem_obj(gem_obj);
+
+ err = qda_fastrpc_invoke_pack(ctx, &msg);
+ if (err)
+ goto err_context_free;
+
+ err = qda_rpmsg_send_msg(qdev, &msg);
+ if (err)
+ goto err_context_free;
+
+ err = qda_rpmsg_wait_for_rsp(ctx);
+ if (err)
+ goto err_context_free;
+
+ err = qda_fastrpc_invoke_unpack(ctx, &msg);
+ if (err)
+ goto err_context_free;
+
+ fastrpc_context_put_id(ctx, qdev);
+ kref_put(&ctx->refcount, qda_fastrpc_context_free);
+ return 0;
+
+err_context_free:
+ fastrpc_context_put_id(ctx, qdev);
+ kref_put(&ctx->refcount, qda_fastrpc_context_free);
+ return err;
+}
+
+/**
+ * qda_ioctl_invoke() - Perform a dynamic FastRPC method invocation
+ * @dev: DRM device structure
+ * @data: User-space data (struct qda_invoke_args)
+ * @file_priv: DRM file private data
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_ioctl_invoke(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ return fastrpc_invoke(FASTRPC_RMID_INVOKE_DYNAMIC, dev, data, file_priv);
+}
diff --git a/drivers/accel/qda/qda_ioctl.h b/drivers/accel/qda/qda_ioctl.h
index d1cbbfb6d965..3bb9cfd98370 100644
--- a/drivers/accel/qda/qda_ioctl.h
+++ b/drivers/accel/qda/qda_ioctl.h
@@ -11,5 +11,6 @@
int qda_ioctl_query(struct drm_device *dev, void *data, struct drm_file *file_priv);
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);
#endif /* __QDA_IOCTL_H__ */
diff --git a/drivers/accel/qda/qda_rpmsg.c b/drivers/accel/qda/qda_rpmsg.c
index 719dabb028c5..44b12a9f2808 100644
--- a/drivers/accel/qda/qda_rpmsg.c
+++ b/drivers/accel/qda/qda_rpmsg.c
@@ -1,14 +1,81 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+#include <linux/completion.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/rpmsg.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
#include <drm/drm_print.h>
#include "qda_cb.h"
#include "qda_drv.h"
+#include "qda_fastrpc.h"
#include "qda_rpmsg.h"
+static int validate_device_availability(struct qda_dev *qdev)
+{
+ if (!qdev)
+ return -ENODEV;
+
+ if (!qdev->rpdev) {
+ drm_dbg_driver(&qdev->drm_dev, "RPMsg device unavailable: rpdev is NULL\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static struct fastrpc_invoke_context *get_and_validate_context(struct qda_msg *msg,
+ struct qda_dev *qdev)
+{
+ struct fastrpc_invoke_context *ctx = msg->fastrpc_ctx;
+
+ if (!ctx) {
+ drm_dbg_driver(&qdev->drm_dev, "FastRPC context not found in message\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ kref_get(&ctx->refcount);
+ return ctx;
+}
+
+static int validate_callback_params(struct qda_dev *qdev, void *data, int len)
+{
+ if (!qdev)
+ return -ENODEV;
+
+ if (len < sizeof(struct qda_invoke_rsp)) {
+ drm_dbg_driver(&qdev->drm_dev, "Invalid message size from remote: %d\n", len);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static unsigned long extract_context_id(struct qda_invoke_rsp *resp_msg)
+{
+ return resp_msg->ctx >> 4;
+}
+
+static struct fastrpc_invoke_context *find_context_by_id(struct qda_dev *qdev,
+ unsigned long ctxid)
+{
+ struct fastrpc_invoke_context *ctx;
+
+ ctx = xa_load(&qdev->ctx_xa, ctxid);
+ if (!ctx) {
+ drm_dbg_driver(&qdev->drm_dev, "FastRPC context not found for ctxid: %lu\n", ctxid);
+ return ERR_PTR(-ENOENT);
+ }
+ return ctx;
+}
+
+static void complete_context_processing(struct fastrpc_invoke_context *ctx, int retval)
+{
+ ctx->retval = retval;
+ complete(&ctx->work);
+ kref_put(&ctx->refcount, qda_fastrpc_context_free);
+}
+
static struct qda_dev *alloc_and_init_qdev(struct rpmsg_device *rpdev)
{
struct qda_dev *qdev;
@@ -24,11 +91,76 @@ static struct qda_dev *alloc_and_init_qdev(struct rpmsg_device *rpdev)
return qdev;
}
+int qda_rpmsg_send_msg(struct qda_dev *qdev, struct qda_msg *msg)
+{
+ int ret, idx;
+ struct fastrpc_invoke_context *ctx;
+
+ if (!qdev)
+ return -ENODEV;
+
+ if (!drm_dev_enter(&qdev->drm_dev, &idx))
+ return -ENODEV;
+
+ ret = validate_device_availability(qdev);
+ if (ret)
+ goto out_exit;
+
+ ctx = get_and_validate_context(msg, qdev);
+ if (IS_ERR(ctx)) {
+ ret = PTR_ERR(ctx);
+ goto out_exit;
+ }
+
+ ret = rpmsg_send(qdev->rpdev->ept, &msg->fastrpc, sizeof(msg->fastrpc));
+ if (ret) {
+ drm_err(&qdev->drm_dev, "rpmsg_send failed: %d\n", ret);
+ kref_put(&ctx->refcount, qda_fastrpc_context_free);
+ }
+
+out_exit:
+ drm_dev_exit(idx);
+ return ret;
+}
+
+int qda_rpmsg_wait_for_rsp(struct fastrpc_invoke_context *ctx)
+{
+ return wait_for_completion_interruptible(&ctx->work);
+}
+
static int qda_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
void *priv, u32 src)
{
- /* Placeholder: responses will be dispatched here */
- return 0;
+ struct qda_dev *qdev = dev_get_drvdata(&rpdev->dev);
+ struct qda_invoke_rsp *resp_msg = (struct qda_invoke_rsp *)data;
+ struct fastrpc_invoke_context *ctx;
+ unsigned long ctxid;
+ int ret, idx;
+
+ if (!qdev)
+ return -ENODEV;
+
+ if (!drm_dev_enter(&qdev->drm_dev, &idx))
+ return -ENODEV;
+
+ ret = validate_callback_params(qdev, data, len);
+ if (ret)
+ goto out_exit;
+
+ ctxid = extract_context_id(resp_msg);
+
+ ctx = find_context_by_id(qdev, ctxid);
+ if (IS_ERR(ctx)) {
+ ret = PTR_ERR(ctx);
+ goto out_exit;
+ }
+
+ complete_context_processing(ctx, resp_msg->retval);
+ ret = 0;
+
+out_exit:
+ drm_dev_exit(idx);
+ return ret;
}
static void qda_rpmsg_remove(struct rpmsg_device *rpdev)
diff --git a/drivers/accel/qda/qda_rpmsg.h b/drivers/accel/qda/qda_rpmsg.h
index 5229d834b34b..bf601e915017 100644
--- a/drivers/accel/qda/qda_rpmsg.h
+++ b/drivers/accel/qda/qda_rpmsg.h
@@ -6,6 +6,23 @@
#ifndef __QDA_RPMSG_H__
#define __QDA_RPMSG_H__
+#include "qda_drv.h"
+#include "qda_fastrpc.h"
+
+/**
+ * struct qda_invoke_rsp - Response structure for FastRPC invocations
+ */
+struct qda_invoke_rsp {
+ /** @ctx: Invoke caller context for matching request/response */
+ u64 ctx;
+ /** @retval: Return value from the remote invocation */
+ int retval;
+};
+
+/* RPMsg transport layer functions */
+int qda_rpmsg_send_msg(struct qda_dev *qdev, struct qda_msg *msg);
+int qda_rpmsg_wait_for_rsp(struct fastrpc_invoke_context *ctx);
+
/* RPMsg transport layer registration */
int qda_rpmsg_register(void);
void qda_rpmsg_unregister(void);
diff --git a/include/uapi/drm/qda_accel.h b/include/uapi/drm/qda_accel.h
index 319e21aae0d6..72512213741f 100644
--- a/include/uapi/drm/qda_accel.h
+++ b/include/uapi/drm/qda_accel.h
@@ -21,6 +21,8 @@ extern "C" {
#define DRM_QDA_QUERY 0x00
#define DRM_QDA_GEM_CREATE 0x01
#define DRM_QDA_GEM_MMAP_OFFSET 0x02
+/* Command numbers 0x03-0x06 reserved for INIT_ATTACH, INIT_CREATE, MAP, MUNMAP */
+#define DRM_QDA_REMOTE_INVOKE 0x07
/*
* QDA IOCTL definitions
@@ -35,6 +37,8 @@ extern "C" {
struct drm_qda_gem_create)
#define DRM_IOCTL_QDA_GEM_MMAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_GEM_MMAP_OFFSET, \
struct drm_qda_gem_mmap_offset)
+#define DRM_IOCTL_QDA_REMOTE_INVOKE DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_REMOTE_INVOKE, \
+ struct drm_qda_invoke_args)
/**
* struct drm_qda_query - Device information query structure
@@ -78,6 +82,41 @@ struct drm_qda_gem_mmap_offset {
__u32 pad;
};
+/**
+ * struct drm_qda_fastrpc_invoke_args - FastRPC invocation argument descriptor
+ * @ptr: Pointer to argument data (user virtual address)
+ * @length: Length of the argument data in bytes
+ * @fd: DMA-BUF file descriptor for buffer arguments, -1/0 for scalar arguments
+ * @attr: Argument attributes and flags
+ *
+ * This structure describes a single argument passed to a FastRPC invocation.
+ * Arguments can be either scalar values or buffer references (via DMA-BUF fd).
+ */
+struct drm_qda_fastrpc_invoke_args {
+ __u64 ptr;
+ __u64 length;
+ __s32 fd;
+ __u32 attr;
+};
+
+/**
+ * struct drm_qda_invoke_args - Dynamic FastRPC invocation parameters
+ * @handle: Remote handle to invoke on the DSP
+ * @sc: FastRPC scalars value encoding the number of in/out buffers
+ * @args: User-space pointer to array of drm_qda_fastrpc_invoke_args descriptors;
+ * the fd field in each entry must be a DMA-BUF fd (or -1/0 for
+ * inline scalar buffers)
+ *
+ * This structure is used with DRM_IOCTL_QDA_REMOTE_INVOKE to perform a
+ * dynamic remote procedure call on the DSP. The args pointer must reference
+ * an array of REMOTE_SCALARS_LENGTH(sc) drm_qda_fastrpc_invoke_args entries.
+ */
+struct drm_qda_invoke_args {
+ __u32 handle;
+ __u32 sc;
+ __u64 args;
+};
+
#if defined(__cplusplus)
}
#endif
--
2.34.1