On Thursday, February 26, 2026 at 5:00 PM, Christian König wrote:
> On 2/26/26 14:40, Ioana Ciocoi-Radulescu wrote:
> > [Sie erhalten nicht häufig E-Mails von [email protected].
> > Weitere Informationen, warum dies wichtig ist, finden Sie unter
> > https://aka.ms/LearnAboutSenderIdentification ]
> >
> > Neutron can execute a single job at a time. For now, only inference
> > jobs are supported. Each job has exactly one BO associated with it.
> >
> > When submitting a job, user also provides a syncobj handle on which it
> > will wait for job completion.
> >
> > We use the DRM GPU scheduler for job management. Large part of the job
> > submission code is based on the example of the ethosu driver.
> >
> > Signed-off-by: Jiwei Fu <[email protected]>
> > Signed-off-by: Ioana Ciocoi-Radulescu <[email protected]>
> > ---
> > drivers/accel/neutron/Makefile | 1 +
> > drivers/accel/neutron/neutron_device.c | 8 +-
> > drivers/accel/neutron/neutron_device.h | 21 ++
> > drivers/accel/neutron/neutron_driver.c | 28 ++-
> > drivers/accel/neutron/neutron_driver.h | 3 +
> > drivers/accel/neutron/neutron_job.c | 367
> +++++++++++++++++++++++++++++++++
> > drivers/accel/neutron/neutron_job.h | 45 ++++
> > include/uapi/drm/neutron_accel.h | 51 +++++
> > 8 files changed, 519 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/accel/neutron/Makefile
> > b/drivers/accel/neutron/Makefile index 192ed896a9f9..ac6dd576521c
> > 100644
> > --- a/drivers/accel/neutron/Makefile
> > +++ b/drivers/accel/neutron/Makefile
> > @@ -6,4 +6,5 @@ neutron-y := \
> > neutron_driver.o \
> > neutron_device.o \
> > neutron_gem.o \
> > + neutron_job.o \
> > neutron_mailbox.o
> > diff --git a/drivers/accel/neutron/neutron_device.c
> > b/drivers/accel/neutron/neutron_device.c
> > index e5c09105be99..571ec906ad72 100644
> > --- a/drivers/accel/neutron/neutron_device.c
> > +++ b/drivers/accel/neutron/neutron_device.c
> > @@ -7,6 +7,7 @@
> > #include <linux/iopoll.h>
> >
> > #include "neutron_device.h"
> > +#include "neutron_job.h"
> > #include "neutron_mailbox.h"
> >
> > void neutron_enable_irq(struct neutron_device *ndev) @@ -32,9 +33,14
> > @@ void neutron_handle_irq(struct neutron_device *ndev)
> > /* Write 1 to clear */
> > writel_relaxed(appstatus & APPSTATUS_CLEAR_MASK,
> > NEUTRON_REG(ndev, APPSTATUS));
> >
> > - if (appstatus & APPSTATUS_FAULTCAUSE_MASK)
> > + if (appstatus & APPSTATUS_FAULTCAUSE_MASK) {
> > dev_err(ndev->dev, "Neutron halted due to fault: 0x%lx\n",
> > FIELD_GET(APPSTATUS_FAULTCAUSE_MASK,
> > appstatus));
> > + return neutron_job_err_handler(ndev);
> > + }
> > +
> > + if (appstatus & APPSTATUS_INFDONE)
> > + neutron_job_done_handler(ndev);
> > }
> >
> > #define neutron_boot_done(appctrl) \
> > diff --git a/drivers/accel/neutron/neutron_device.h
> > b/drivers/accel/neutron/neutron_device.h
> > index 8e4df7462d82..0ed72965774d 100644
> > --- a/drivers/accel/neutron/neutron_device.h
> > +++ b/drivers/accel/neutron/neutron_device.h
> > @@ -9,8 +9,10 @@
> > #include <linux/spinlock.h>
> > #include <linux/bits.h>
> > #include <drm/drm_device.h>
> > +#include <drm/gpu_scheduler.h>
> >
> > struct clk_bulk_data;
> > +struct neutron_job;
> >
> > #define NEUTRON_FIRMWARE_NAME "NeutronFirmware.elf"
> >
> > @@ -92,6 +94,13 @@ enum neutron_mem_id {
> > * @clks: Neutron clocks
> > * @num_clks: Number of clocks
> > * @flags: Software flags used by driver
> > + * @fence_lock: DMA fence lock
> > + * @sched: GPU scheduler
> > + * @sched_lock: Scheduler lock, for neutron_push_job
> > + * @fence_context: Fence context
> > + * @job_seqno: Job sequence number
> > + * @job_lock: Job lock, for active_job handling
> > + * @active_job: Currently active job
> > */
> > struct neutron_device {
> > struct drm_device base;
> > @@ -103,6 +112,18 @@ struct neutron_device {
> > struct clk_bulk_data *clks;
> > int num_clks;
> > u32 flags;
> > +
> > + /* For dma_fence */
> > + spinlock_t fence_lock;
>
> I've just pushed a patch set to drm-misc-next which makes the fence_lock
> superflous in most cases. Just provide NULL as lock when calling to
> dma_fence_init().
Thanks, I'll update for v2.
>
> > + struct drm_gpu_scheduler sched;
> > + /* For neutron_push_job */
> > + struct mutex sched_lock;
> > + u64 fence_context;
> > + u64 job_seqno;
> > +
> > + /* For active_job handling */
> > + struct mutex job_lock;
> > + struct neutron_job *active_job;
> > };
> >
> > #define to_neutron_device(drm) \
> > diff --git a/drivers/accel/neutron/neutron_driver.c
> > b/drivers/accel/neutron/neutron_driver.c
> > index c9a18bf52037..ceae1f7e8359 100644
> > --- a/drivers/accel/neutron/neutron_driver.c
> > +++ b/drivers/accel/neutron/neutron_driver.c
> > @@ -19,40 +19,53 @@
> > #include "neutron_device.h"
> > #include "neutron_driver.h"
> > #include "neutron_gem.h"
> > +#include "neutron_job.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),
> > + DRM_IOCTL_DEF_DRV(NEUTRON_SUBMIT_JOB,
> > + neutron_ioctl_submit_job, 0),
> > };
> >
> > static int neutron_open(struct drm_device *drm, struct drm_file
> > *file) {
> > struct neutron_device *ndev = to_neutron_device(drm);
> > struct neutron_file_priv *npriv;
> > + int ret;
> >
> > npriv = kzalloc_obj(*npriv);
> > if (!npriv)
> > return -ENOMEM;
> >
> > npriv->ndev = ndev;
> > - file->driver_priv = npriv;
> >
> > + ret = neutron_job_open(npriv);
> > + if (ret)
> > + goto err_free;
> > +
> > + file->driver_priv = npriv;
> > return 0;
> > +
> > +err_free:
> > + kfree(npriv);
> > + return ret;
> > }
> >
> > static void neutron_postclose(struct drm_device *drm, struct drm_file
> > *file) {
> > struct neutron_file_priv *npriv = file->driver_priv;
> >
> > + neutron_job_close(npriv);
> > kfree(npriv);
> > }
> >
> > DEFINE_DRM_ACCEL_FOPS(neutron_drm_driver_fops);
> >
> > static const struct drm_driver neutron_drm_driver = {
> > - .driver_features = DRIVER_COMPUTE_ACCEL | DRIVER_GEM,
> > + .driver_features = DRIVER_COMPUTE_ACCEL | DRIVER_GEM |
> > + DRIVER_SYNCOBJ,
> > .name = "neutron",
> > .desc = "NXP Neutron driver",
> > .major = 1,
> > @@ -151,19 +164,25 @@ static int neutron_probe(struct platform_device
> *pdev)
> > return ret;
> > }
> >
> > - ret = devm_pm_runtime_enable(dev);
> > + ret = neutron_job_init(ndev);
> > if (ret)
> > goto free_reserved;
> >
> > + ret = devm_pm_runtime_enable(dev);
> > + if (ret)
> > + goto free_job;
> > +
> > pm_runtime_set_autosuspend_delay(dev,
> NEUTRON_SUSPEND_DELAY_MS);
> > pm_runtime_use_autosuspend(dev);
> >
> > ret = drm_dev_register(&ndev->base, 0);
> > if (ret)
> > - goto free_reserved;
> > + goto free_job;
> >
> > return 0;
> >
> > +free_job:
> > + neutron_job_fini(ndev);
> > free_reserved:
> > of_reserved_mem_device_release(&pdev->dev);
> >
> > @@ -175,6 +194,7 @@ static void neutron_remove(struct platform_device
> *pdev)
> > struct neutron_device *ndev = platform_get_drvdata(pdev);
> >
> > drm_dev_unregister(&ndev->base);
> > + neutron_job_fini(ndev);
> > of_reserved_mem_device_release(&pdev->dev);
> > }
> >
> > diff --git a/drivers/accel/neutron/neutron_driver.h
> > b/drivers/accel/neutron/neutron_driver.h
> > index cd52b5eb2d27..b709de74105a 100644
> > --- a/drivers/accel/neutron/neutron_driver.h
> > +++ b/drivers/accel/neutron/neutron_driver.h
> > @@ -4,10 +4,13 @@
> > #ifndef __NEUTRON_DRIVER_H__
> > #define __NEUTRON_DRIVER_H__
> >
> > +#include <drm/gpu_scheduler.h>
> > +
> > struct neutron_device;
> >
> > struct neutron_file_priv {
> > struct neutron_device *ndev;
> > + struct drm_sched_entity sched_entity;
> > };
> >
> > #endif /* __NEUTRON_DRIVER_H__ */
> > diff --git a/drivers/accel/neutron/neutron_job.c
> > b/drivers/accel/neutron/neutron_job.c
> > new file mode 100644
> > index 000000000000..316e361166a2
> > --- /dev/null
> > +++ b/drivers/accel/neutron/neutron_job.c
> > @@ -0,0 +1,367 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/* Copyright 2025-2026 NXP */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/pm_runtime.h>
> > +#include <drm/drm_file.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_gem_dma_helper.h>
> > +#include <drm/neutron_accel.h>
> > +
> > +#include "neutron_driver.h"
> > +#include "neutron_device.h"
> > +#include "neutron_gem.h"
> > +#include "neutron_mailbox.h"
> > +#include "neutron_job.h"
> > +
> > +#define NEUTRON_JOB_TIMEOUT_MS 5000
> > +
> > +static const char *neutron_fence_get_driver_name(struct dma_fence
> > +*fence) {
> > + return "neutron";
> > +}
> > +
> > +static const char *neutron_fence_get_timeline_name(struct dma_fence
> > +*fence) {
> > + return "neutron-npu";
> > +}
> > +
> > +static const struct dma_fence_ops neutron_fence_ops = {
> > + .get_driver_name = neutron_fence_get_driver_name,
> > + .get_timeline_name = neutron_fence_get_timeline_name, };
> > +
> > +static void neutron_hw_submit(struct neutron_job *job) {
> > + struct neutron_device *ndev = job->ndev;
> > + struct neutron_mbox_cmd cmd = {0};
> > + u32 base_l, base_h;
> > + u64 base_addr;
> > + int ret;
> > +
> > + switch (job->type) {
> > + case DRM_NEUTRON_JOB_INFERENCE:
> > + cmd.id = NEUTRON_CMD_INFERENCE;
> > + cmd.args[0] = job->inference.tensor_offset;
> > + cmd.args[1] = job->inference.microcode_offset;
> > + cmd.args[2] = job->inference.tensor_count;
> > + break;
> > + default:
> > + dev_WARN(ndev->dev, "Unknown job type: %d\n", job->type);
> > + return;
> > + }
> > +
> > + base_addr = to_drm_gem_dma_obj(job->bo)->dma_addr;
> > + base_l = lower_32_bits(base_addr);
> > + base_h = upper_32_bits(base_addr);
> > +
> > + writel_relaxed(base_l, NEUTRON_REG(ndev, BASEDDRL));
> > + writel_relaxed(base_l, NEUTRON_REG(ndev, BASEINOUTL));
> > + writel_relaxed(base_l, NEUTRON_REG(ndev, BASESPILLL));
> > + writel_relaxed(base_h, NEUTRON_REG(ndev, BASEDDRH));
> > + writel_relaxed(base_h, NEUTRON_REG(ndev, BASEINOUTH));
> > + writel_relaxed(base_h, NEUTRON_REG(ndev, BASESPILLH));
> > +
> > + ret = neutron_mbox_send_cmd(ndev, &cmd);
> > + if (ret) {
> > + /* Nothing we can do here, we'll reset the device on
> > timeout */
> > + dev_err(ndev->dev, "Failed to submit job, device is
> > busy\n");
> > + }
> > +}
> > +
> > +void neutron_job_err_handler(struct neutron_device *ndev) {
> > + guard(mutex)(&ndev->job_lock);
> > +
> > + if (ndev->active_job)
> > + drm_sched_fault(&ndev->sched); }
> > +
> > +void neutron_job_done_handler(struct neutron_device *ndev) {
> > + struct neutron_mbox_state state;
> > +
> > + neutron_mbox_read_state(ndev, &state);
> > + if (state.status != NEUTRON_FW_STATUS_DONE) {
> > + dev_err(ndev->dev, "Inconsistent firmware state: status
> > 0x%x, err
> 0x%x\n",
> > + state.status, state.err_code);
> > + return neutron_job_err_handler(ndev);
> > + }
> > +
> > + if (state.err_code != 0)
> > + dev_warn(ndev->dev, "Job finished with error: 0x%x\n",
> > + state.err_code);
>
> Not mandatory but you might also want to forward that as error to your
> dma_fence, see dma_fence_set_error().
Ok, will do that.
>
> > +
> > + /* Reset Neutron internal state to prepare for next inference */
> > + neutron_mbox_reset_state(ndev);
> > +
> > + scoped_guard(mutex, &ndev->job_lock) {
> > + if (ndev->active_job) {
> > + dma_fence_signal(ndev->active_job->neutron_fence);
> > + ndev->active_job = NULL;
> > + }
> > + }
> > +}
> > +
> > +static void neutron_cleanup_job(struct kref *ref) {
> > + struct neutron_job *job = container_of(ref, struct
> > +neutron_job, refcnt);
> > +
> > + pm_runtime_put_autosuspend(job->ndev->base.dev);
> > +
> > + dma_fence_put(job->neutron_fence);
> > + dma_fence_put(job->sched_fence);
> > + drm_gem_object_put(job->bo);
> > + drm_syncobj_put(job->syncobj);
> > +
> > + kfree(job);
> > +}
> > +
> > +static void neutron_put_job(struct neutron_job *job) {
> > + kref_put(&job->refcnt, neutron_cleanup_job); }
> > +
> > +static void neutron_free_job(struct drm_sched_job *sched_job) {
> > + struct neutron_job *job = to_neutron_job(sched_job);
> > +
> > + drm_sched_job_cleanup(sched_job);
> > + neutron_put_job(job);
> > +}
> > +
> > +static struct dma_fence *neutron_run_job(struct drm_sched_job
> > +*sched_job) {
> > + struct neutron_job *job = to_neutron_job(sched_job);
> > + struct dma_fence *fence = job->neutron_fence;
> > + struct neutron_device *ndev = job->ndev;
> > +
> > + if (unlikely(job->base.s_fence->finished.error))
> > + return NULL;
> > +
> > + dma_fence_init(fence, &neutron_fence_ops, &ndev->fence_lock,
> > + ndev->fence_context, ++ndev->job_seqno);
> > + dma_fence_get(fence);
> > +
> > + scoped_guard(mutex, &ndev->job_lock) {
> > + ndev->active_job = job;
> > + neutron_hw_submit(job);
> > + }
> > +
> > + return fence;
> > +}
> > +
> > +static enum drm_gpu_sched_stat neutron_timedout_job(struct
> > +drm_sched_job *sched_job) {
> > + struct neutron_job *job = to_neutron_job(sched_job);
> > + struct neutron_device *ndev = job->ndev;
> > + struct neutron_mbox_state state;
> > +
> > + /* We assume Neutron is stuck, retrieve current state and reset */
> > + neutron_mbox_read_state(ndev, &state);
> > + dev_err(ndev->dev, "Neutron timedout, status: 0x%x, err: 0x%x\n",
> > + state.status, state.err_code);
> > +
> > + drm_sched_stop(&ndev->sched, sched_job);
> > +
> > + scoped_guard(mutex, &ndev->job_lock)
> > + ndev->active_job = NULL;
> > +
> > + pm_runtime_force_suspend(ndev->dev);
> > + pm_runtime_force_resume(ndev->dev);
> > +
> > + drm_sched_start(&ndev->sched, 0);
> > +
> > + return DRM_GPU_SCHED_STAT_RESET; }
> > +
> > +static void neutron_cancel_job(struct drm_sched_job *sched_job) {
> > + struct neutron_job *job = to_neutron_job(sched_job);
> > + struct neutron_device *ndev = job->ndev;
> > +
> > + guard(mutex)(&ndev->job_lock);
> > +
> > + if (!dma_fence_is_signaled(job->neutron_fence)) {
> > + dma_fence_set_error(job->neutron_fence, -ECANCELED);
> > + dma_fence_signal(job->neutron_fence);
> > + }
> > +}
> > +
> > +static const struct drm_sched_backend_ops neutron_sched_ops = {
> > + .run_job = neutron_run_job,
> > + .free_job = neutron_free_job,
> > + .timedout_job = neutron_timedout_job,
> > + .cancel_job = neutron_cancel_job, };
> > +
> > +int neutron_job_init(struct neutron_device *ndev) {
> > + const struct drm_sched_init_args args = {
> > + .ops = &neutron_sched_ops,
> > + .num_rqs = DRM_SCHED_PRIORITY_COUNT,
> > + .credit_limit = 1,
> > + .timeout = msecs_to_jiffies(NEUTRON_JOB_TIMEOUT_MS),
> > + .name = dev_name(ndev->dev),
> > + .dev = ndev->dev,
> > + };
> > + int ret;
> > +
> > + ret = devm_mutex_init(ndev->dev, &ndev->sched_lock);
> > + if (ret)
> > + return ret;
> > + ret = devm_mutex_init(ndev->dev, &ndev->job_lock);
> > + if (ret)
> > + return ret;
> > + spin_lock_init(&ndev->fence_lock);
> > +
> > + ndev->fence_context = dma_fence_context_alloc(1);
> > +
> > + ret = drm_sched_init(&ndev->sched, &args);
> > + if (ret)
> > + dev_err(ndev->dev, "Error creating DRM scheduler\n");
> > +
> > + return ret;
> > +}
> > +
> > +void neutron_job_fini(struct neutron_device *ndev) {
> > + drm_sched_fini(&ndev->sched);
> > +}
> > +
> > +int neutron_job_open(struct neutron_file_priv *npriv) {
> > + struct neutron_device *ndev = npriv->ndev;
> > + struct drm_gpu_scheduler *sched = &ndev->sched;
> > + int ret;
> > +
> > + ret = drm_sched_entity_init(&npriv->sched_entity,
> > + DRM_SCHED_PRIORITY_NORMAL,
> > + &sched, 1, NULL);
> > + if (ret)
> > + dev_err(ndev->dev, "Error creating scheduler
> > + entity\n");
> > +
> > + return ret;
> > +}
> > +
> > +void neutron_job_close(struct neutron_file_priv *npriv) {
> > + drm_sched_entity_destroy(&npriv->sched_entity);
> > +}
> > +
> > +static int neutron_push_job(struct neutron_job *job) {
> > + struct neutron_device *ndev = job->ndev;
> > + struct ww_acquire_ctx acquire_ctx;
> > + int ret;
> > +
> > + ret = drm_gem_lock_reservations(&job->bo, 1, &acquire_ctx);
> > + if (ret)
> > + return ret;
> > +
> > + ret = dma_resv_reserve_fences(job->bo->resv, 1);
> > + if (ret)
> > + goto out_unlock_res;
> > +
> > + ret = drm_sched_job_add_implicit_dependencies(&job->base, job->bo,
> true);
> > + if (ret)
> > + goto out_unlock_res;
> > +
> > + ret = pm_runtime_resume_and_get(ndev->base.dev);
> > + if (ret)
> > + goto out_unlock_res;
> > +
> > + scoped_guard(mutex, &ndev->sched_lock) {
> > + drm_sched_job_arm(&job->base);
> > +
> > + job->sched_fence =
> > dma_fence_get(&job->base.s_fence->finished);
> > + drm_syncobj_replace_fence(job->syncobj,
> > + job->sched_fence);
> > +
> > + kref_get(&job->refcnt);
> > + drm_sched_entity_push_job(&job->base);
> > +
> > + dma_resv_add_fence(job->bo->resv, job->sched_fence,
> > + DMA_RESV_USAGE_WRITE);
> > + }
> > +
> > +out_unlock_res:
> > + drm_gem_unlock_reservations(&job->bo, 1, &acquire_ctx);
> > +
> > + return ret;
> > +}
> > +
> > +int neutron_ioctl_submit_job(struct drm_device *drm, void *data,
> > +struct drm_file *filp) {
> > + struct neutron_device *ndev = to_neutron_device(drm);
> > + struct neutron_file_priv *npriv = filp->driver_priv;
> > + struct drm_neutron_submit_job *args = data;
> > + struct neutron_job *job;
> > + int ret;
> > +
> > + if (args->pad)
> > + return -EINVAL;
> > +
> > + job = kzalloc_obj(*job);
> > + if (!job)
> > + return -ENOMEM;
> > +
> > + job->ndev = ndev;
> > + kref_init(&job->refcnt);
> > +
> > + job->neutron_fence = kzalloc_obj(*job->neutron_fence);
> > + if (!job->neutron_fence) {
> > + ret = -ENOMEM;
> > + goto out_free_job;
> > + }
> > +
> > + switch (args->type) {
> > + case DRM_NEUTRON_JOB_INFERENCE:
> > + memcpy(&job->inference, &args->inference,
> > + sizeof(args->inference));
> > + break;
> > + default:
> > + dev_dbg(ndev->dev, "Invalid job type %d\n", args->type);
> > + ret = -EINVAL;
> > + goto out_free_fence;
> > + }
> > +
> > + job->bo = drm_gem_object_lookup(filp, args->bo_handle);
> > + if (!job->bo) {
> > + dev_dbg(ndev->dev, "Invalid BO handle\n");
> > + ret = -ENOENT;
> > + goto out_free_fence;
> > + }
> > +
> > + job->syncobj = drm_syncobj_find(filp, args->syncobj_handle);
> > + if (!job->syncobj) {
> > + dev_dbg(ndev->dev, "Invalid syncobj handle\n");
> > + ret = -ENOENT;
> > + goto out_put_gem;
> > + }
> > +
> > + ret = drm_sched_job_init(&job->base, &npriv->sched_entity, 1, NULL,
> > + filp->client_id);
> > + if (ret)
> > + goto out_put_syncobj;
> > +
> > + ret = neutron_push_job(job);
> > + if (ret)
> > + goto out_sched_cleanup;
> > +
> > + neutron_put_job(job);
> > +
> > + return 0;
> > +
> > +out_sched_cleanup:
> > + drm_sched_job_cleanup(&job->base);
> > +out_put_syncobj:
> > + drm_syncobj_put(job->syncobj);
> > +out_put_gem:
> > + drm_gem_object_put(job->bo);
> > +out_free_fence:
> > + kfree(job->neutron_fence);
> > +out_free_job:
> > + kfree(job);
> > +
> > + return ret;
> > +}
> > diff --git a/drivers/accel/neutron/neutron_job.h
> > b/drivers/accel/neutron/neutron_job.h
> > new file mode 100644
> > index 000000000000..bb7773aeb218
> > --- /dev/null
> > +++ b/drivers/accel/neutron/neutron_job.h
> > @@ -0,0 +1,45 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/* Copyright 2025-2026 NXP */
> > +
> > +#ifndef __NEUTRON_JOB_H__
> > +#define __NEUTRON_JOB_H__
> > +
> > +#include <linux/kref.h>
> > +#include <drm/drm_gem.h>
> > +#include <drm/drm_syncobj.h>
> > +#include <drm/gpu_scheduler.h>
> > +#include <drm/neutron_accel.h>
> > +
> > +#include "neutron_driver.h"
> > +
> > +struct neutron_device;
> > +struct neutron_file_priv;
> > +
> > +struct neutron_job {
> > + struct drm_sched_job base;
> > + struct neutron_device *ndev;
> > + struct dma_fence *neutron_fence;
>
> > + struct dma_fence *sched_fence;
>
> That looks superflous to me. You should always have the scheduler fence
> through the base.
Ok.
>
> > + struct drm_syncobj *syncobj;
>
> Why do you want to keep the syncobj around?
No good enough reason, I'll remove it from job structure.
>
>
> Apart from those notes looks pretty good to me, but I'm a bit disapointed that
> there isn't any DMA-buf support to review :)
Thanks for reviewing!
Ioana
>
> Regards,
> Christian.
>
> > + struct drm_gem_object *bo;
> > + enum drm_neutron_job_type type;
> > + union {
> > + struct drm_neutron_inference_job inference;
> > + };
> > + struct kref refcnt;
> > +};
> > +
> > +#define to_neutron_job(job) \
> > + container_of(job, struct neutron_job, base)
> > +
> > +int neutron_job_init(struct neutron_device *dev); void
> > +neutron_job_fini(struct neutron_device *dev); int
> > +neutron_job_open(struct neutron_file_priv *npriv); void
> > +neutron_job_close(struct neutron_file_priv *npriv);
> > +
> > +void neutron_job_done_handler(struct neutron_device *dev); void
> > +neutron_job_err_handler(struct neutron_device *dev);
> > +
> > +int neutron_ioctl_submit_job(struct drm_device *dev, void *data,
> > +struct drm_file *filp);
> > +
> > +#endif /* __NEUTRON_JOB_H__ */
> > diff --git a/include/uapi/drm/neutron_accel.h
> > b/include/uapi/drm/neutron_accel.h
> > index 2f5639f2e0e8..a9e5682709d2 100644
> > --- a/include/uapi/drm/neutron_accel.h
> > +++ b/include/uapi/drm/neutron_accel.h
> > @@ -15,10 +15,12 @@ extern "C" {
> > *
> > * @DRM_NEUTRON_CREATE_BO: Create a buffer object
> > * @DRM_NEUTRON_SYNC_BO: Sync (parts of) the buffer object memory
> > + * @DRM_NEUTRON_SUBMIT_JOB: Submit a job to the device
> > */
> > enum drm_neutron_ioctl {
> > DRM_NEUTRON_CREATE_BO = 0,
> > DRM_NEUTRON_SYNC_BO,
> > + DRM_NEUTRON_SUBMIT_JOB,
> > };
> >
> > /**
> > @@ -64,6 +66,51 @@ struct drm_neutron_sync_bo {
> > __u64 offset;
> > };
> >
> > +/**
> > + * enum drm_neutron_job_type - Type of job to submit to Neutron
> > +device
> > + *
> > + * @DRM_NEUTRON_JOB_INFERENCE: Inference job */ enum
> > +drm_neutron_job_type {
> > + DRM_NEUTRON_JOB_INFERENCE = 0, };
> > +
> > +/**
> > + * struct drm_neutron_inference_job - Inference job descriptor
> > + *
> > + * @tensor_offset: Offset of tensor array inside job BO
> > + * @microcode_offset: Microcode offset inside BO
> > + * @tensor_count: Number of valid tensors
> > + * @pad: MBZ
> > + */
> > +struct drm_neutron_inference_job {
> > + __u32 tensor_offset;
> > + __u32 microcode_offset;
> > + __u32 tensor_count;
> > + __u32 pad[5];
> > +};
> > +
> > +/**
> > + * struct drm_neutron_submit_job - Submit a job to Neutron device
> > + *
> > + * @type: Job type, one of enum drm_neutron_job_type
> > + * @bo_handle: BO handle for this job
> > + * @inference: Inference job descriptor (when type is
> > +DRM_NEUTRON_JOB_INFERENCE)
> > + * @reserved: Reserved for future job types
> > + * @syncobj_handle: Handle of syncobj on which user waits for job
> > +completion
> > + * @pad: MBZ
> > + */
> > +struct drm_neutron_submit_job {
> > + __u32 type;
> > + __u32 bo_handle;
> > + union {
> > + struct drm_neutron_inference_job inference;
> > + __u32 reserved[8];
> > + };
> > + __u32 syncobj_handle;
> > + __u32 pad;
> > +};
> > +
> > #define DRM_IOCTL_NEUTRON_CREATE_BO \
> > DRM_IOWR(DRM_COMMAND_BASE + DRM_NEUTRON_CREATE_BO, \
> > struct drm_neutron_create_bo) @@ -72,6 +119,10 @@
> > struct drm_neutron_sync_bo {
> > DRM_IOWR(DRM_COMMAND_BASE + DRM_NEUTRON_SYNC_BO, \
> > struct drm_neutron_sync_bo)
> >
> > +#define DRM_IOCTL_NEUTRON_SUBMIT_JOB \
> > + DRM_IOWR(DRM_COMMAND_BASE + DRM_NEUTRON_SUBMIT_JOB,
> \
> > + struct drm_neutron_submit_job)
> > +
> > #if defined(__cplusplus)
> > }
> > #endif
> >
> > --
> > 2.34.1
> >