[PATCH 5/9] accel/neutron: Add GEM buffer object support

From: Ioana Ciocoi-Radulescu

Date: Thu Feb 26 2026 - 08:54:17 EST


Add the following IOCTLs:
- CREATE_BO - for creating a new buffer object and passing BO info
back to user
- SYNC_BO - for explicit DMA sync operations on the BO memory, since
Neutron isn't guaranteed to be cache coherent. User controls which
portions of the buffer memory to sync and the direction.

The Neutron device requires contiguous DMA buffers, so use the GEM DMA
helpers for creating and managing the BOs. Depending on the platform
it is integrated on, Neutron device may or may not be cache coherent. On
i.MX95, the first platform for which we add Neutron support, it is not.

Signed-off-by: Ioana Ciocoi-Radulescu <ruxandra.radulescu@xxxxxxx>
---
drivers/accel/neutron/Makefile | 3 +-
drivers/accel/neutron/neutron_driver.c | 13 +++-
drivers/accel/neutron/neutron_gem.c | 115 +++++++++++++++++++++++++++++++++
drivers/accel/neutron/neutron_gem.h | 14 ++++
include/uapi/drm/neutron_accel.h | 79 ++++++++++++++++++++++
5 files changed, 222 insertions(+), 2 deletions(-)

diff --git a/drivers/accel/neutron/Makefile b/drivers/accel/neutron/Makefile
index 7592e318dd83..d4298c7a8535 100644
--- a/drivers/accel/neutron/Makefile
+++ b/drivers/accel/neutron/Makefile
@@ -4,4 +4,5 @@ obj-$(CONFIG_DRM_ACCEL_NXP_NEUTRON) := neutron.o

neutron-y := \
neutron_driver.o \
- neutron_device.o
+ neutron_device.o \
+ neutron_gem.o
diff --git a/drivers/accel/neutron/neutron_driver.c b/drivers/accel/neutron/neutron_driver.c
index 7f34785216cf..c9a18bf52037 100644
--- a/drivers/accel/neutron/neutron_driver.c
+++ b/drivers/accel/neutron/neutron_driver.c
@@ -14,12 +14,19 @@
#include <drm/drm_drv.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_gem.h>
+#include <drm/neutron_accel.h>

#include "neutron_device.h"
#include "neutron_driver.h"
+#include "neutron_gem.h"

#define NEUTRON_SUSPEND_DELAY_MS 1000

+static const struct drm_ioctl_desc neutron_drm_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(NEUTRON_CREATE_BO, neutron_ioctl_create_bo, 0),
+ DRM_IOCTL_DEF_DRV(NEUTRON_SYNC_BO, neutron_ioctl_sync_bo, 0),
+};
+
static int neutron_open(struct drm_device *drm, struct drm_file *file)
{
struct neutron_device *ndev = to_neutron_device(drm);
@@ -45,7 +52,7 @@ static void neutron_postclose(struct drm_device *drm, struct drm_file *file)
DEFINE_DRM_ACCEL_FOPS(neutron_drm_driver_fops);

static const struct drm_driver neutron_drm_driver = {
- .driver_features = DRIVER_COMPUTE_ACCEL,
+ .driver_features = DRIVER_COMPUTE_ACCEL | DRIVER_GEM,
.name = "neutron",
.desc = "NXP Neutron driver",
.major = 1,
@@ -54,6 +61,10 @@ static const struct drm_driver neutron_drm_driver = {
.fops = &neutron_drm_driver_fops,
.open = neutron_open,
.postclose = neutron_postclose,
+ .ioctls = neutron_drm_ioctls,
+ .num_ioctls = ARRAY_SIZE(neutron_drm_ioctls),
+
+ .gem_create_object = neutron_gem_create_object,
};

static irqreturn_t neutron_irq_handler_thread(int irq, void *data)
diff --git a/drivers/accel/neutron/neutron_gem.c b/drivers/accel/neutron/neutron_gem.c
new file mode 100644
index 000000000000..9b5460d9bcc6
--- /dev/null
+++ b/drivers/accel/neutron/neutron_gem.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright 2025-2026 NXP */
+
+#include <linux/sizes.h>
+#include <linux/align.h>
+#include <linux/dma-map-ops.h>
+#include <drm/drm_device.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
+#include <drm/neutron_accel.h>
+
+#include "neutron_device.h"
+#include "neutron_gem.h"
+
+#define NEUTRON_BO_ALIGN SZ_1M
+
+struct drm_gem_object *neutron_gem_create_object(struct drm_device *drm, size_t size)
+{
+ struct neutron_device *ndev = to_neutron_device(drm);
+ struct drm_gem_dma_object *dma_obj;
+ struct drm_gem_object *gem_obj;
+
+ dma_obj = kzalloc_obj(*dma_obj);
+ if (!dma_obj)
+ return ERR_PTR(-ENOMEM);
+
+ dma_obj->map_noncoherent = !dev_is_dma_coherent(ndev->dev);
+ dma_obj->map_bidirectional = true;
+ gem_obj = &dma_obj->base;
+
+ return gem_obj;
+}
+
+int neutron_ioctl_create_bo(struct drm_device *drm, void *data, struct drm_file *filp)
+{
+ struct drm_neutron_create_bo *args = data;
+ struct drm_gem_dma_object *dma_obj;
+ struct drm_gem_object *gem_obj;
+ size_t size;
+ int ret;
+
+ if (!args->size || args->pad)
+ return -EINVAL;
+
+ size = ALIGN(args->size, NEUTRON_BO_ALIGN);
+
+ dma_obj = drm_gem_dma_create(drm, size);
+ if (IS_ERR(dma_obj))
+ return PTR_ERR(dma_obj);
+
+ /* We expect correctly aligned buffers, but double-check */
+ if (drm_WARN_ON(drm, !IS_ALIGNED(dma_obj->dma_addr, NEUTRON_BO_ALIGN))) {
+ ret = -EFAULT;
+ goto out_put;
+ }
+
+ gem_obj = &dma_obj->base;
+ ret = drm_gem_handle_create(filp, gem_obj, &args->handle);
+ if (ret)
+ goto out_put;
+
+ args->map_offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
+ args->size = gem_obj->size;
+
+out_put:
+ /* No need to keep a reference of the GEM object. Freeing is handled by user */
+ drm_gem_object_put(gem_obj);
+
+ return ret;
+}
+
+int neutron_ioctl_sync_bo(struct drm_device *drm, void *data, struct drm_file *filp)
+{
+ struct drm_neutron_sync_bo *args = data;
+ struct drm_gem_dma_object *dma_obj;
+ struct drm_gem_object *gem_obj;
+ dma_addr_t start_addr;
+ int ret = 0;
+
+ gem_obj = drm_gem_object_lookup(filp, args->handle);
+ if (!gem_obj) {
+ dev_dbg(drm->dev, "Invalid BO handle %u\n", args->handle);
+ return -ENOENT;
+ }
+
+ dma_obj = to_drm_gem_dma_obj(gem_obj);
+
+ if (!args->size || args->offset >= gem_obj->size ||
+ args->size > gem_obj->size - args->offset) {
+ dev_dbg(drm->dev, "Invalid offset/size for BO sync\n");
+ ret = -EINVAL;
+ goto out_put;
+ }
+
+ start_addr = dma_obj->dma_addr + args->offset;
+
+ switch (args->direction) {
+ case DRM_NEUTRON_SYNC_TO_DEVICE:
+ dma_sync_single_for_device(drm->dev, start_addr, args->size,
+ DMA_BIDIRECTIONAL);
+ break;
+ case DRM_NEUTRON_SYNC_FROM_DEVICE:
+ dma_sync_single_for_cpu(drm->dev, start_addr, args->size,
+ DMA_BIDIRECTIONAL);
+ break;
+ default:
+ dev_dbg(drm->dev, "Invalid direction for BO sync\n");
+ ret = -EINVAL;
+ }
+
+out_put:
+ drm_gem_object_put(gem_obj);
+
+ return ret;
+}
diff --git a/drivers/accel/neutron/neutron_gem.h b/drivers/accel/neutron/neutron_gem.h
new file mode 100644
index 000000000000..95ba2fe96617
--- /dev/null
+++ b/drivers/accel/neutron/neutron_gem.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright 2025-2026 NXP */
+
+#ifndef __NEUTRON_GEM_H__
+#define __NEUTRON_GEM_H__
+
+#include <drm/drm_gem.h>
+
+struct drm_gem_object *neutron_gem_create_object(struct drm_device *drm, size_t size);
+
+int neutron_ioctl_create_bo(struct drm_device *drm, void *data, struct drm_file *filp);
+int neutron_ioctl_sync_bo(struct drm_device *drm, void *data, struct drm_file *filp);
+
+#endif /* __NEUTRON_GEM_H__ */
diff --git a/include/uapi/drm/neutron_accel.h b/include/uapi/drm/neutron_accel.h
new file mode 100644
index 000000000000..2f5639f2e0e8
--- /dev/null
+++ b/include/uapi/drm/neutron_accel.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/* Copyright 2025-2026 NXP */
+
+#ifndef __NEUTRON_ACCEL_H__
+#define __NEUTRON_ACCEL_H__
+
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/**
+ * enum drm_neutron_ioctl - Neutron IOCTL IDs
+ *
+ * @DRM_NEUTRON_CREATE_BO: Create a buffer object
+ * @DRM_NEUTRON_SYNC_BO: Sync (parts of) the buffer object memory
+ */
+enum drm_neutron_ioctl {
+ DRM_NEUTRON_CREATE_BO = 0,
+ DRM_NEUTRON_SYNC_BO,
+};
+
+/**
+ * struct drm_neutron_create_bo - Create a buffer object and return buffer
+ * info to user
+ *
+ * @size: Size in bytes of requested buffer. May be updated by driver
+ * if allocated size different than requested
+ * @handle: Returned handle for the new buffer object
+ * @pad: MBZ
+ * @map_offset: Returned offset for mmap() calls
+ */
+struct drm_neutron_create_bo {
+ __u64 size;
+ __u32 handle;
+ __u32 pad;
+ __u64 map_offset;
+};
+
+/**
+ * enum drm_neutron_sync_dir - Direction of buffer object synchronization
+ *
+ * @DRM_NEUTRON_SYNC_TO_DEVICE: Sync from CPU to device
+ * @DRM_NEUTRON_SYNC_FROM_DEVICE: Sync from device to CPU
+ */
+enum drm_neutron_sync_dir {
+ DRM_NEUTRON_SYNC_TO_DEVICE = 0,
+ DRM_NEUTRON_SYNC_FROM_DEVICE,
+};
+
+/**
+ * struct drm_neutron_sync_bo - Sync buffer object memory
+ *
+ * @handle: Handle of buffer object to sync
+ * @direction: Direction of sync, can be one of enum drm_neutron_sync_dir
+ * @size: Size of the memory to sync, in bytes
+ * @offset: Offset inside the buffer, in bytes
+ */
+struct drm_neutron_sync_bo {
+ __u32 handle;
+ __u32 direction;
+ __u64 size;
+ __u64 offset;
+};
+
+#define DRM_IOCTL_NEUTRON_CREATE_BO \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_NEUTRON_CREATE_BO, \
+ struct drm_neutron_create_bo)
+
+#define DRM_IOCTL_NEUTRON_SYNC_BO \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_NEUTRON_SYNC_BO, \
+ struct drm_neutron_sync_bo)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __NEUTRON_ACCEL_H__ */

--
2.34.1