On 10/23/26 08:39, Tomeu Vizoso wrote:
A Job is created once from a program, its parameters and its transfers, checked then, and submitted any number of times; the params buffer's contents may change between submissions. A submission pushes the Job onto a core's Ring and rings the doorbell; the firmware moves the Fence when the Job is done. Each core has a drm-gpu-scheduler instance, with one credit per Ring entry. A submit call takes an array of Jobs, the buffers they use for implicit synchronisation, and sync objects to signal; it waits on none.
The job submission code was initially based on Panfrost.
I only skimmed over this, but what I saw looks sane to me from the dma_fence requirement side.
Regards, Christian.
Assisted-by: LLM Signed-off-by: Tomeu Vizoso tomeu@tomeuvizoso.net
v3:
- Add Job create/destroy, for validating transfers.
- Add sync objects to the submission IOCTL.
- Allocate the done fence with the Job; run_job allocates nothing (Christian König).
v2:
- Add thames_accel.h UAPI header (Robert Nelson).
drivers/accel/thames/Kconfig | 1 + drivers/accel/thames/Makefile | 3 + drivers/accel/thames/thames_core.c | 36 +- drivers/accel/thames/thames_core.h | 14 + drivers/accel/thames/thames_device.h | 6 + drivers/accel/thames/thames_drv.c | 43 ++ drivers/accel/thames/thames_drv.h | 7 + drivers/accel/thames/thames_job.c | 869 +++++++++++++++++++++++++++++++++++ drivers/accel/thames/thames_job.h | 73 +++ drivers/accel/thames/thames_trace.h | 77 ++++ include/uapi/drm/thames_accel.h | 259 ++++++++++- 11 files changed, 1381 insertions(+), 7 deletions(-)
diff --git a/drivers/accel/thames/Kconfig b/drivers/accel/thames/Kconfig index 47a2986fcabd..f2c32ce8d228 100644 --- a/drivers/accel/thames/Kconfig +++ b/drivers/accel/thames/Kconfig @@ -6,6 +6,7 @@ config DRM_ACCEL_THAMES depends on TI_K3_DSP_REMOTEPROC depends on REMOTEPROC depends on MAILBOX
select DRM_SCHED select DRM_GEM_DMA_HELPER select DRM_GEM_SHMEM_HELPER select IOMMU_IO_PGTABLE_LPAEdiff --git a/drivers/accel/thames/Makefile b/drivers/accel/thames/Makefile index 143a829f24cf..7bd652795cf5 100644 --- a/drivers/accel/thames/Makefile +++ b/drivers/accel/thames/Makefile @@ -6,4 +6,7 @@ thames-y := \ thames_core.o \ thames_drv.o \ thames_gem.o \
thames_job.o \ thames_transfers.o+CFLAGS_thames_job.o := -I$(src) diff --git a/drivers/accel/thames/thames_core.c b/drivers/accel/thames/thames_core.c index dff76abd8951..85e8f48aec60 100644 --- a/drivers/accel/thames/thames_core.c +++ b/drivers/accel/thames/thames_core.c @@ -25,6 +25,7 @@ #include "thames_core.h" #include "thames_device.h" #include "thames_gem.h" +#include "thames_job.h" #include "thames_transfers.h"
#define THAMES_RESTART_TIMEOUT_MS 5000 @@ -327,6 +328,7 @@ static void thames_mbox_rx(struct mbox_client *cl, void *mssg)
dev_dbg(core->dev, "Doorbell on " THAMES_CORE_FMT ": 0x%x\n", THAMES_CORE_ARG(core), (u32)(uintptr_t)mssg);
thames_job_complete(core);}
/* Embedded in struct thames_core: nothing to free. */ @@ -515,10 +517,18 @@ int thames_core_init(struct thames_core *core) if (err) goto err_timers;
err = thames_core_request_doorbell(core);
/** Before the doorbell: requesting it delivers at once a word the core* wrote while no driver was loaded.*/err = thames_job_init(core); if (err) goto err_unmap;err = thames_core_request_doorbell(core);if (err)goto err_job;if (rproc->state == RPROC_OFFLINE) { err = rproc_boot(rproc); if (err)@@ -540,6 +550,8 @@ int thames_core_init(struct thames_core *core)
err_doorbell: thames_core_free_doorbell(core); +err_job:
thames_job_fini(core);err_unmap: thames_core_unmap_ipc(core); err_timers: @@ -554,6 +566,7 @@ void thames_core_fini(struct thames_core *core) { if (core->mbox_chan) thames_core_free_doorbell(core);
thames_job_fini(core); /* * Drop only our reference and keep the boot count: the core runs on * for the next load to reattach to; only a reset or a suspend, which@@ -567,6 +580,21 @@ void thames_core_fini(struct thames_core *core) core->of_node = NULL; }
+/* The system firmware accepts a stop only of a core in IDLE. */ +bool thames_core_idles(struct thames_core *core) +{
u32 head = READ_ONCE(core->ipc->ring.head);u32 slot = head % THAMES_RING_DEPTH;struct thames_fault_record *record = &core->ipc->fault_records[slot];if (head == core->ring_tail)return true;/* Read the fault record after the head that says it is there. */rmb();return READ_ONCE(record->kind) == DRM_THAMES_FAULT_FIRMWARE;+}
/*
- rproc_shutdown() reaches the system firmware because the core has one power
- count, remoteproc's auto-boot or this driver's boot: the probe defers while
@@ -577,6 +605,10 @@ int thames_core_suspend(struct thames_core *core) struct rproc *rproc = core->rproc; int err;
err = thames_job_suspend(core);if (err)return err;if (core->suspended) return 0;@@ -589,6 +621,7 @@ int thames_core_suspend(struct thames_core *core) err = -EBUSY; } if (err) {
thames_job_resume(core); return err; }@@ -640,5 +673,6 @@ int thames_core_resume(struct thames_core *core)
offline: restart:
thames_job_resume(core); return err;} diff --git a/drivers/accel/thames/thames_core.h b/drivers/accel/thames/thames_core.h index 590c5716f7f5..a78414d18ad3 100644 --- a/drivers/accel/thames/thames_core.h +++ b/drivers/accel/thames/thames_core.h @@ -10,6 +10,7 @@ #include <linux/mutex_types.h> #include <linux/remoteproc.h>
+#include <drm/gpu_scheduler.h>
#include "thames_abi.h"
@@ -44,13 +45,25 @@ struct thames_core { struct mbox_chan *mbox_chan;
u32 ring_tail;
struct list_head submitted;struct list_head held;struct mutex job_lock;spinlock_t fence_lock;/* * @rproc_caller is the task inside the driver's own rproc_boot() or * rproc_shutdown(), whose subdevice callbacks then do nothing. */ struct task_struct *rproc_caller;struct drm_gpu_scheduler sched;u64 fence_context;u64 emit_seqno;bool suspended;unsigned long timeout_left; struct resource l2sram; struct resource l2aux;@@ -81,6 +94,7 @@ static inline const struct resource *thames_core_window(const struct thames_core
int thames_core_init(struct thames_core *core); void thames_core_fini(struct thames_core *core); +bool thames_core_idles(struct thames_core *core); int thames_core_suspend(struct thames_core *core); int thames_core_resume(struct thames_core *core);
diff --git a/drivers/accel/thames/thames_device.h b/drivers/accel/thames/thames_device.h index e183c78e2acf..0a34764885df 100644 --- a/drivers/accel/thames/thames_device.h +++ b/drivers/accel/thames/thames_device.h @@ -36,7 +36,10 @@ struct thames_caps { struct thames_device { struct drm_device ddev;
struct mutex sched_lock;struct thames_core cores[THAMES_MAX_CORES];struct drm_gpu_scheduler *sched_list[THAMES_MAX_CORES]; unsigned int num_cores; struct thames_caps caps;@@ -72,6 +75,9 @@ struct thames_device { */ struct mutex bindings_lock;
/* Whoever takes a file off the list closes its Jobs. */struct mutex files_lock;struct list_head files;};
#define to_thames_device(drm_dev) \ diff --git a/drivers/accel/thames/thames_drv.c b/drivers/accel/thames/thames_drv.c index 030c60963a0a..fc3328834162 100644 --- a/drivers/accel/thames/thames_drv.c +++ b/drivers/accel/thames/thames_drv.c @@ -23,6 +23,7 @@ #include "thames_core.h" #include "thames_drv.h" #include "thames_gem.h" +#include "thames_job.h" #include "thames_transfers.h"
static struct platform_device *drm_dev; @@ -54,14 +55,23 @@ static int thames_open(struct drm_device *dev, struct drm_file *file)
file->driver_priv = thames_priv;
ret = thames_job_open(thames_priv);if (ret)goto err_free;ret = thames_context_create(thames_priv); if (ret) goto err_close_job;scoped_guard(mutex, &tdev->files_lock)list_add_tail(&thames_priv->file_node, &tdev->files);drm_dev_exit(idx); return 0;err_close_job:
thames_job_close(thames_priv);+err_free: kfree(thames_priv); err_exit: drm_dev_exit(idx); @@ -87,6 +97,14 @@ void thames_file_priv_put(struct thames_file_priv *priv) static void thames_postclose(struct drm_device *dev, struct drm_file *file) { struct thames_file_priv *thames_priv = file->driver_priv;
struct thames_device *tdev = thames_priv->tdev;scoped_guard(mutex, &tdev->files_lock) {if (!list_empty(&thames_priv->file_node)) {list_del_init(&thames_priv->file_node);thames_job_close(thames_priv);}} dev_dbg(dev->dev, "Context %u: file closed, %u references left\n", thames_priv->context_id, kref_read(&thames_priv->refcount) - 1);@@ -208,6 +226,9 @@ static int thames_ioctl_##func##_entered(struct drm_device *dev, void *data, \ THAMES_IOCTL_ENTERED(bo_create) THAMES_IOCTL_ENTERED(bo_mmap_offset) THAMES_IOCTL_ENTERED(dev_query) +THAMES_IOCTL_ENTERED(job_create) +THAMES_IOCTL_ENTERED(job_submit) +THAMES_IOCTL_ENTERED(job_destroy) THAMES_IOCTL_ENTERED(bo_bind)
static const struct drm_ioctl_desc thames_drm_driver_ioctls[] = { @@ -215,6 +236,9 @@ static const struct drm_ioctl_desc thames_drm_driver_ioctls[] = { THAMES_IOCTL(BO_CREATE, bo_create_entered), THAMES_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset_entered), THAMES_IOCTL(DEV_QUERY, dev_query_entered),
THAMES_IOCTL(JOB_CREATE, job_create_entered),THAMES_IOCTL(JOB_SUBMIT, job_submit_entered),THAMES_IOCTL(JOB_DESTROY, job_destroy_entered), THAMES_IOCTL(BO_BIND, bo_bind_entered),};
@@ -272,6 +296,10 @@ static struct thames_device *thames_device_init(struct platform_device *pdev, if (err) return ERR_PTR(err);
err = drmm_mutex_init(&tdev->ddev, &tdev->sched_lock);if (err)return ERR_PTR(err);err = drmm_mutex_init(&tdev->ddev, &tdev->transfer_table_pool_lock); if (err) return ERR_PTR(err);@@ -288,8 +316,13 @@ static struct thames_device *thames_device_init(struct platform_device *pdev, if (err) return ERR_PTR(err);
err = drmm_mutex_init(&tdev->ddev, &tdev->files_lock);if (err)return ERR_PTR(err);INIT_LIST_HEAD(&tdev->transfer_tables); INIT_LIST_HEAD(&tdev->contexts);INIT_LIST_HEAD(&tdev->files); tdev->asid_next = 1; /*@@ -347,6 +380,7 @@ static int thames_platform_probe(struct platform_device *pdev) continue; }
tdev->sched_list[core] = &tdev->cores[core].sched; tdev->num_cores++; core++; }@@ -391,6 +425,15 @@ static void thames_platform_remove(struct platform_device *pdev)
drm_dev_unplug(&tdev->ddev);
scoped_guard(mutex, &tdev->files_lock) {struct thames_file_priv *priv, *tmp;list_for_each_entry_safe(priv, tmp, &tdev->files, file_node) {list_del_init(&priv->file_node);thames_job_close(priv);}}for (core = 0; core < tdev->num_cores; core++) thames_core_fini(&tdev->cores[core]); tdev->num_cores = 0;diff --git a/drivers/accel/thames/thames_drv.h b/drivers/accel/thames/thames_drv.h index 7f9031b63780..2115f7eae589 100644 --- a/drivers/accel/thames/thames_drv.h +++ b/drivers/accel/thames/thames_drv.h @@ -11,6 +11,7 @@ #include <linux/xarray.h>
#include <drm/drm_mm.h> +#include <drm/gpu_scheduler.h>
#include "thames_device.h"
@@ -26,6 +27,12 @@ struct thames_file_priv { */ struct kref refcount;
struct list_head file_node;struct drm_sched_entity sched_entity;struct xarray jobs;struct io_pgtable_ops *pgtbl_ops[THAMES_MAX_CORES]; u64 page_table_base[THAMES_MAX_CORES]; u32 asid;diff --git a/drivers/accel/thames/thames_job.c b/drivers/accel/thames/thames_job.c new file mode 100644 index 000000000000..6560106d47a8 --- /dev/null +++ b/drivers/accel/thames/thames_job.c @@ -0,0 +1,869 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2019 Linaro, Ltd, Rob Herring robh@kernel.org */ +/* Copyright 2019 Collabora ltd. */ +/* Copyright 2024-2025 Tomeu Vizoso tomeu@tomeuvizoso.net */ +/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+#include <clocksource/arm_arch_timer.h> +#include <linux/dev_printk.h> +#include <linux/dma-fence-chain.h> +#include <linux/iopoll.h> +#include <linux/mailbox_client.h> +#include <linux/platform_device.h> +#include <linux/xarray.h>
+#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_gem.h> +#include <drm/drm_print.h> +#include <drm/drm_syncobj.h> +#include <drm/thames_accel.h>
+#include "thames_core.h" +#include "thames_device.h" +#include "thames_drv.h" +#include "thames_gem.h" +#include "thames_job.h"
+#define CREATE_TRACE_POINTS +#include "thames_trace.h"
+static struct thames_sched_job *to_thames_sched_job(struct drm_sched_job *sched_job) +{
return container_of(sched_job, struct thames_sched_job, base);+}
+static void thames_sched_job_put(struct thames_sched_job *job); +static void thames_job_put(struct thames_job *job);
+#define THAMES_MAX_JOBS_PER_SUBMIT 256 +#define THAMES_MAX_SUBMIT_BOS 4096 +#define THAMES_MAX_SUBMIT_SYNCS 64
+/*
- The host's timeout is longer than the device's: a Job still on the Ring when
- it fires is on a stalled core.
- */
+#define THAMES_DEVICE_TIMEOUT_MS 500
+struct thames_submit_bos {
struct kref refcount;u32 read_count;u32 count;struct drm_gem_object *bos[] __counted_by(count);+};
+static void thames_submit_bos_free(struct kref *ref) +{
struct thames_submit_bos *bos = container_of(ref, struct thames_submit_bos, refcount);u32 i;for (i = 0; i < bos->count; i++) {if (bos->bos[i])drm_gem_object_put(bos->bos[i]);}kvfree(bos);+}
+static void thames_submit_bos_put(struct thames_submit_bos *bos) +{
if (bos)kref_put(&bos->refcount, thames_submit_bos_free);+}
+static const char *thames_fence_get_driver_name(struct dma_fence *fence) +{
return "thames";+}
+static const char *thames_fence_get_timeline_name(struct dma_fence *fence) +{
return "thames";+}
+static const struct dma_fence_ops thames_fence_ops = {
.get_driver_name = thames_fence_get_driver_name,.get_timeline_name = thames_fence_get_timeline_name,+};
+static void thames_job_fail_done_fence(struct thames_sched_job *job, int error) +{
if (!dma_fence_was_initialized(job->done_fence) || dma_fence_is_signaled(job->done_fence))return;dma_fence_set_error(job->done_fence, error);dma_fence_signal(job->done_fence);+}
+static int thames_job_hw_submit(struct thames_core *core, struct thames_sched_job *sched_job) +{
struct thames_ring *ring = &core->ipc->ring;struct thames_ring_host *host;u32 tail = core->ring_tail;u32 index = tail % THAMES_RING_DEPTH;int ret;if (tail - READ_ONCE(ring->head) >= THAMES_RING_DEPTH) {dev_err(core->dev, "Ring full on " THAMES_CORE_FMT "\n", THAMES_CORE_ARG(core));return -ENOSPC;}sched_job->stamps[0] = arch_timer_read_counter();sched_job->ring_index = index;host = &ring->entries[index].host;host->context_id = sched_job->file_priv->context_id;host->transfer_count = sched_job->job->transfers.count;host->transfer_group_count = sched_job->job->transfers.group_count;host->transfer_table_offset = sched_job->job->transfers.offset;host->seqno = sched_job->seqno;host->program_dsp_va = sched_job->job->program_dsp_va;host->params_dsp_va = sched_job->job->params_dsp_va;host->context_serial = sched_job->file_priv->context_serial;host->timeout_ms = THAMES_DEVICE_TIMEOUT_MS;scoped_guard(mutex, &core->job_lock) {kref_get(&sched_job->refcount); /* Put by thames_job_complete(). */list_add_tail(&sched_job->node, &core->submitted);/* Order the host half before the tail. */wmb();WRITE_ONCE(ring->tail, tail + 1);core->ring_tail = tail + 1;/* Order the tail before the doorbell. */wmb();}/** The Job is on the Ring whatever the send returns; it completes* through the Fence or the timeout.*/ret = mbox_send_message(core->mbox_chan, (void *)(uintptr_t)THAMES_DOORBELL);if (ret < 0)dev_warn_ratelimited(core->dev, "Doorbell not sent: %d\n", ret);return 0;+}
+void thames_job_complete(struct thames_core *core) +{
struct thames_sched_job *job, *tmp;LIST_HEAD(done);u64 completed, signalled;completed = READ_ONCE(core->ipc->fence.seqno);/* Read the entries after the Fence that covers them. */rmb();scoped_guard(mutex, &core->job_lock) {list_for_each_entry_safe(job, tmp, &core->submitted, node) {if (job->seqno > completed)break;list_move_tail(&job->node, &done);}}list_for_each_entry_safe(job, tmp, &done, node) {struct thames_ring_fw *fw = &core->ipc->ring.entries[job->ring_index].fw;job->stamps[1] = fw->doorbell_seen_stamp;job->stamps[2] = fw->run_start_stamp;job->stamps[3] = fw->run_end_stamp;job->stamps[4] = fw->kernel_entry_stamp;job->stamps[5] = fw->kernel_exit_stamp;if (fw->fence_value == THAMES_FENCE_ERROR) {dma_fence_set_error(job->done_fence, -EIO);}list_del_init(&job->node);signalled = arch_timer_read_counter();dma_fence_signal(job->done_fence);trace_thames_job_done(core, job, signalled);thames_sched_job_put(job);}+}
+static int thames_acquire_object_fences(struct drm_gem_object **bos, int bo_count,
struct drm_sched_job *job, bool is_write)+{
int i, ret;for (i = 0; i < bo_count; i++) {ret = dma_resv_reserve_fences(bos[i]->resv, 1);if (ret)return ret;ret = drm_sched_job_add_implicit_dependencies(job, bos[i], is_write);if (ret)return ret;}return 0;+}
+static void thames_attach_object_fences(struct drm_gem_object **bos, int bo_count,
struct dma_fence *fence, enum dma_resv_usage usage)+{
int i;for (i = 0; i < bo_count; i++)dma_resv_add_fence(bos[i]->resv, fence, usage);+}
+/* Nothing here fails, so a submit call pushes every Job or none. */ +static void thames_job_push(struct thames_sched_job *sched_job) +{
scoped_guard(mutex, &sched_job->tdev->sched_lock) {drm_sched_job_arm(&sched_job->base);sched_job->inference_done_fence = dma_fence_get(&sched_job->base.s_fence->finished);kref_get(&sched_job->refcount); /* put by scheduler job completion */drm_sched_entity_push_job(&sched_job->base);}+}
+static void thames_sched_job_cleanup(struct kref *ref) +{
struct thames_sched_job *sched_job = container_of(ref, struct thames_sched_job, refcount);if (dma_fence_was_initialized(sched_job->done_fence))dma_fence_put(sched_job->done_fence);elsedma_fence_free(sched_job->done_fence);dma_fence_put(sched_job->inference_done_fence);thames_submit_bos_put(sched_job->bos);if (sched_job->file_priv)thames_file_priv_put(sched_job->file_priv);if (sched_job->job)thames_job_put(sched_job->job);kfree(sched_job);+}
+static void thames_sched_job_put(struct thames_sched_job *job) +{
kref_put(&job->refcount, thames_sched_job_cleanup);+}
+static void thames_sched_job_free(struct drm_sched_job *sched_job) +{
struct thames_sched_job *job = to_thames_sched_job(sched_job);drm_sched_job_cleanup(sched_job);thames_sched_job_put(job);+}
+static struct dma_fence *thames_job_run(struct drm_sched_job *sched_job) +{
struct thames_sched_job *job = to_thames_sched_job(sched_job);struct thames_core *core = container_of(sched_job->sched, struct thames_core, sched);int ret;if (unlikely(job->base.s_fence->finished.error))return NULL;/* The done fence keeps the seqno of the Job's first push. */job->seqno = ++core->emit_seqno;if (!dma_fence_was_initialized(job->done_fence))dma_fence_init(job->done_fence, &thames_fence_ops, &core->fence_lock,core->fence_context, job->seqno);ret = thames_job_hw_submit(core, job);if (ret) {thames_job_fail_done_fence(job, ret);return ERR_PTR(ret);}return dma_fence_get(job->done_fence);+}
+/*
- A done fence not signalled here is a Job on a live core's Ring that may still
- run.
- */
+static void thames_job_cancel(struct drm_sched_job *sched_job) +{
struct thames_sched_job *job = to_thames_sched_job(sched_job);struct thames_core *core = container_of(sched_job->sched, struct thames_core, sched);if (!dma_fence_was_initialized(job->done_fence) || dma_fence_is_signaled(job->done_fence))return;scoped_guard(mutex, &core->job_lock)list_move_tail(&job->node, &core->held);thames_job_fail_done_fence(job, -ECANCELED);+}
+static const struct drm_sched_backend_ops thames_sched_ops = {
.run_job = thames_job_run,.free_job = thames_sched_job_free,.cancel_job = thames_job_cancel,+};
+int thames_job_init(struct thames_core *core) +{
/* One credit per Ring entry, so the tail never overruns the head. */struct drm_sched_init_args args = {.ops = &thames_sched_ops,.num_rqs = 1,.credit_limit = THAMES_RING_DEPTH,.timeout = MAX_SCHEDULE_TIMEOUT,.name = dev_name(core->dev),.dev = core->dev,};u32 head, tail, before;u64 fence, before_seqno, last;int ret;spin_lock_init(&core->fence_lock);mutex_init(&core->job_lock);INIT_LIST_HEAD(&core->submitted);INIT_LIST_HEAD(&core->held);/** Both indices run free, so only a tail not at the head is wrong: the* previous instance's entries are still pending. The Fence may trail* the head by one entry, the firmware moving the head first.*/head = READ_ONCE(core->ipc->ring.head);tail = READ_ONCE(core->ipc->ring.tail);before = (head - 1) % THAMES_RING_DEPTH;if (tail != head) {dev_err(core->dev, "Ring on " THAMES_CORE_FMT" has %u pending entries (tail %u, head %u); not attaching\n",THAMES_CORE_ARG(core), tail - head, tail, head);return -EBUSY;}/* Read the Fence and the entry after the head and the tail. */rmb();fence = READ_ONCE(core->ipc->fence.seqno);before_seqno = core->ipc->ring.entries[before].host.seqno;last = max(fence, before_seqno);core->ring_tail = head;core->emit_seqno = last;core->fence_context = dma_fence_context_alloc(1);ret = drm_sched_init(&core->sched, &args);if (ret) {dev_err(core->dev, "Failed to create scheduler: %d.", ret);return ret;}return 0;+}
+/*
- These Jobs' core may still run their Kernels: the Jobs, Contexts and buffers
- stay allocated.
- */
+void thames_job_fini(struct thames_core *core) +{
struct thames_sched_job *job;u64 first = U64_MAX, last = 0;size_t kept;drm_sched_fini(&core->sched);guard(mutex)(&core->job_lock);kept = list_count_nodes(&core->held);if (!kept)return;list_for_each_entry(job, &core->held, node) {first = min(first, job->seqno);last = max(last, job->seqno);}drm_dev_get(&core->tdev->ddev);dev_warn(core->dev, THAMES_CORE_FMT" may still run %zu Jobs (seqno %llu to %llu); keeping them and their buffers until reboot\n",THAMES_CORE_ARG(core), kept, first, last);+}
+/*
- Above the device's timeout, so the firmware ends a runaway Kernel within the
- wait.
- */
+#define THAMES_SUSPEND_TIMEOUT_MS (THAMES_DEVICE_TIMEOUT_MS + 100)
+int thames_job_suspend(struct thames_core *core) +{
struct drm_gpu_scheduler *sched = &core->sched;unsigned int wait_ms;size_t pending;bool idle;int err;drm_sched_wqueue_stop(sched);core->timeout_left = drm_sched_suspend_timeout(sched);cancel_delayed_work_sync(&sched->work_tdr);drm_sched_wqueue_stop(sched);scoped_guard(mutex, &core->job_lock)pending = list_count_nodes(&core->submitted);wait_ms = max_t(unsigned int, pending, 1) * THAMES_SUSPEND_TIMEOUT_MS;err = read_poll_timeout(thames_core_idles, idle, idle, USEC_PER_MSEC,wait_ms * USEC_PER_MSEC, false, core);if (!err) {thames_job_complete(core);scoped_guard(mutex, &core->job_lock) {if (!list_empty(&core->submitted))err = -EBUSY;}}if (err) {dev_err(core->dev, THAMES_CORE_FMT" is not idle with its Jobs completed after %u ms; refusing the suspend\n",THAMES_CORE_ARG(core), wait_ms);thames_job_resume(core);return -EBUSY;}return 0;+}
+void thames_job_resume(struct thames_core *core) +{
drm_sched_resume_timeout(&core->sched, core->timeout_left);drm_sched_wqueue_start(&core->sched);+}
+int thames_job_open(struct thames_file_priv *thames_priv) +{
struct thames_device *tdev = thames_priv->tdev;int ret;ret = drm_sched_entity_init(&thames_priv->sched_entity, 0, tdev->sched_list,tdev->num_cores, NULL);if (WARN_ON(ret))return ret;xa_init_flags(&thames_priv->jobs, XA_FLAGS_ALLOC1);return 0;+}
+void thames_job_close(struct thames_file_priv *thames_priv) +{
struct thames_job *job;unsigned long handle;drm_sched_entity_destroy(&thames_priv->sched_entity);xa_for_each(&thames_priv->jobs, handle, job)thames_job_put(job);xa_destroy(&thames_priv->jobs);+}
+static void thames_job_free(struct kref *ref) +{
struct thames_job *job = container_of(ref, struct thames_job, refcount);if (job->program)drm_gem_object_put(job->program);if (job->params)drm_gem_object_put(job->params);thames_transfers_free(job->tdev, &job->transfers);mutex_destroy(&job->lock);kfree(job);+}
+static void thames_job_put(struct thames_job *job) +{
kref_put(&job->refcount, thames_job_free);+}
+int thames_ioctl_job_create(struct drm_device *dev, void *data, struct drm_file *file) +{
struct drm_thames_job_create *args = data;struct thames_file_priv *file_priv = file->driver_priv;struct thames_device *tdev = to_thames_device(dev);struct thames_job *job;u32 handle;int ret;if (args->flags || args->pad)return -EINVAL;if (args->type != DRM_THAMES_JOB_DISPATCH)return -EINVAL;if (!args->program || !args->params)return -EINVAL;job = kzalloc_obj(*job, GFP_KERNEL);if (!job)return -ENOMEM;kref_init(&job->refcount);mutex_init(&job->lock);job->tdev = tdev;job->program = drm_gem_object_lookup(file, args->program);if (!job->program) {ret = -ENOENT;goto err_put;}if (!(to_thames_bo(job->program)->flags & DRM_THAMES_BO_EXECUTABLE)) {ret = -EINVAL;goto err_put;}job->program_dsp_va = thames_bo_dsp_va(to_thames_bo(job->program), file_priv);if (!job->program_dsp_va) {ret = -EINVAL;goto err_put;}job->params = drm_gem_object_lookup(file, args->params);if (!job->params) {ret = -ENOENT;goto err_put;}job->params_dsp_va = thames_bo_dsp_va(to_thames_bo(job->params), file_priv);if (!job->params_dsp_va) {ret = -EINVAL;goto err_put;}ret = thames_transfers_build(file, &args->transfers, &job->transfers);if (ret)goto err_put;ret = xa_alloc(&file_priv->jobs, &handle, job, xa_limit_32b, GFP_KERNEL);if (ret)goto err_put;job->handle = handle;args->handle = handle;return 0;+err_put:
thames_job_put(job);return ret;+}
+static struct thames_sched_job *thames_sched_job_new(struct drm_file *file, u32 handle,
struct thames_submit_bos *bos)+{
struct thames_file_priv *file_priv = file->driver_priv;struct thames_job *job;struct thames_sched_job *sched_job;int ret;xa_lock(&file_priv->jobs);job = xa_load(&file_priv->jobs, handle);if (job)kref_get(&job->refcount);xa_unlock(&file_priv->jobs);if (!job)return ERR_PTR(-ENOENT);sched_job = kzalloc_obj(*sched_job, GFP_KERNEL);if (!sched_job) {thames_job_put(job);return ERR_PTR(-ENOMEM);}kref_init(&sched_job->refcount);INIT_LIST_HEAD(&sched_job->node);sched_job->tdev = job->tdev;sched_job->job = job; /* The lookup's reference. *//* Put by the completion path once it has counted the Job for the ban. */kref_get(&file_priv->refcount);sched_job->file_priv = file_priv;kref_get(&bos->refcount);sched_job->bos = bos;sched_job->done_fence = kzalloc_obj(*sched_job->done_fence);if (!sched_job->done_fence) {thames_sched_job_put(sched_job);return ERR_PTR(-ENOMEM);}ret = drm_sched_job_init(&sched_job->base, &file_priv->sched_entity, 1, NULL,file->client_id);if (ret) {thames_sched_job_put(sched_job);return ERR_PTR(ret);}return sched_job;+}
+static int thames_submit_bos_lookup(struct drm_file *file,
const struct drm_thames_job_submit *args,struct thames_submit_bos **out)+{
const struct drm_thames_obj_array *lists[2] = { &args->read_handles,&args->write_handles };struct thames_submit_bos *bos;u32 total = 0, n = 0, l, i;for (l = 0; l < ARRAY_SIZE(lists); l++) {if (lists[l]->count > THAMES_MAX_SUBMIT_BOS - total)return -EINVAL;total += lists[l]->count;}bos = kvzalloc(struct_size(bos, bos, total), GFP_KERNEL);if (!bos)return -ENOMEM;kref_init(&bos->refcount);bos->count = total;bos->read_count = args->read_handles.count;for (l = 0; l < ARRAY_SIZE(lists); l++) {u32 *handles;if (!lists[l]->count)continue;handles = thames_get_uobj_array(lists[l], sizeof(*handles), sizeof(*handles));if (IS_ERR(handles)) {thames_submit_bos_put(bos);return PTR_ERR(handles);}for (i = 0; i < lists[l]->count; i++, n++) {bos->bos[n] = drm_gem_object_lookup(file, handles[i]);if (!bos->bos[n]) {kvfree(handles);thames_submit_bos_put(bos);return -ENOENT;}}kvfree(handles);}*out = bos;return 0;+}
+/*
- Allocated before the first push, so signalling cannot fail once the Jobs run.
- */
+struct thames_submit_syncs {
u32 count;struct drm_thames_sync_op *ops;struct thames_sync_signal {struct drm_syncobj *syncobj;struct dma_fence_chain *chain;} *signals;+};
+static int thames_sync_op_check(const struct drm_thames_sync_op *op) +{
if (op->flags != (DRM_THAMES_SYNC_OP_HANDLE_TYPE_TIMELINE_SYNCOBJ |DRM_THAMES_SYNC_OP_SIGNAL))return -EINVAL;return op->timeline_value ? 0 : -EINVAL;+}
+static void thames_submit_syncs_free(struct thames_submit_syncs *syncs) +{
u32 i;for (i = 0; i < syncs->count; i++) {if (syncs->signals[i].syncobj)drm_syncobj_put(syncs->signals[i].syncobj);dma_fence_chain_free(syncs->signals[i].chain);}kvfree(syncs->signals);kvfree(syncs->ops);+}
+static int thames_submit_syncs_get(struct drm_file *file, const struct drm_thames_obj_array *in,
struct thames_submit_syncs *syncs)+{
struct drm_thames_sync_op *ops;u32 i;int ret;if (!in->count)return 0;if (in->count > THAMES_MAX_SUBMIT_SYNCS)return -EINVAL;ops = thames_get_uobj_array(in, offsetofend(struct drm_thames_sync_op, timeline_value),sizeof(*ops));if (IS_ERR(ops))return PTR_ERR(ops);syncs->ops = ops;syncs->signals = kvcalloc(in->count, sizeof(*syncs->signals), GFP_KERNEL);if (!syncs->signals)return -ENOMEM;syncs->count = in->count;for (i = 0; i < syncs->count; i++) {struct thames_sync_signal *signal = &syncs->signals[i];ret = thames_sync_op_check(&ops[i]);if (ret)return ret;signal->syncobj = drm_syncobj_find(file, ops[i].handle);if (!signal->syncobj)return -ENOENT;signal->chain = dma_fence_chain_alloc();if (!signal->chain)return -ENOMEM;}return 0;+}
+static void thames_submit_syncs_signal(struct thames_submit_syncs *syncs, struct dma_fence *fence) +{
u32 i;for (i = 0; i < syncs->count; i++) {struct thames_sync_signal *signal = &syncs->signals[i];drm_syncobj_add_point(signal->syncobj, signal->chain, fence,syncs->ops[i].timeline_value);signal->chain = NULL;}+}
+int thames_ioctl_job_submit(struct drm_device *dev, void *data, struct drm_file *file) +{
struct drm_thames_job_submit *args = data;struct thames_submit_syncs syncs = {};struct thames_submit_bos *bos = NULL;struct ww_acquire_ctx acquire_ctx;struct thames_sched_job **subs = NULL;struct dma_fence *last;u32 count = args->jobs.count;u32 pushed = 0;u32 *handles;unsigned int i;int ret = 0;if (args->flags || args->pad)return -EINVAL;if (!count || count > THAMES_MAX_JOBS_PER_SUBMIT)return -EINVAL;handles = thames_get_uobj_array(&args->jobs, sizeof(u32), sizeof(u32));if (IS_ERR(handles))return PTR_ERR(handles);ret = thames_submit_syncs_get(file, &args->syncs, &syncs);if (ret)goto out_syncs;ret = thames_submit_bos_lookup(file, args, &bos);if (ret)goto out_syncs;subs = kvcalloc(count, sizeof(*subs), GFP_KERNEL);if (!subs) {ret = -ENOMEM;goto out_put;}for (i = 0; i < count; i++) {subs[i] = thames_sched_job_new(file, handles[i], bos);if (IS_ERR(subs[i])) {ret = PTR_ERR(subs[i]);subs[i] = NULL;goto out_subs;}}ret = drm_gem_lock_reservations(bos->bos, bos->count, &acquire_ctx);if (ret)goto out_subs;/** Only the first Job waits: the entity runs the call's Jobs in order.*/ret = thames_acquire_object_fences(bos->bos, bos->read_count, &subs[0]->base, false);if (ret)goto out_unlock;ret = thames_acquire_object_fences(&bos->bos[bos->read_count], bos->count - bos->read_count,&subs[0]->base, true);if (ret)goto out_unlock;for (pushed = 0; pushed < count; pushed++)thames_job_push(subs[pushed]);last = subs[count - 1]->inference_done_fence;thames_attach_object_fences(bos->bos, bos->read_count, last, DMA_RESV_USAGE_READ);thames_attach_object_fences(&bos->bos[bos->read_count], bos->count - bos->read_count, last,DMA_RESV_USAGE_WRITE);thames_submit_syncs_signal(&syncs, last);+out_unlock:
drm_gem_unlock_reservations(bos->bos, bos->count, &acquire_ctx);+out_subs:
for (i = 0; i < count; i++) {if (!subs[i])continue;if (i >= pushed)drm_sched_job_cleanup(&subs[i]->base);/* The creation reference; the scheduler holds its own. */thames_sched_job_put(subs[i]);}kvfree(subs);+out_put:
thames_submit_bos_put(bos);+out_syncs:
thames_submit_syncs_free(&syncs);kvfree(handles);return ret;+}
+int thames_ioctl_job_destroy(struct drm_device *dev, void *data, struct drm_file *file) +{
struct drm_thames_job_destroy *args = data;struct thames_file_priv *file_priv = file->driver_priv;struct thames_job *job;if (args->pad)return -EINVAL;job = xa_erase(&file_priv->jobs, args->handle);if (!job)return -ENOENT;/* A submission still running holds its own reference. */thames_job_put(job);return 0;+}
diff --git a/drivers/accel/thames/thames_job.h b/drivers/accel/thames/thames_job.h new file mode 100644 index 000000000000..a851febe8f98 --- /dev/null +++ b/drivers/accel/thames/thames_job.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2024-2025 Tomeu Vizoso tomeu@tomeuvizoso.net */ +/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+#ifndef __THAMES_JOB_H__ +#define __THAMES_JOB_H__
+#include <drm/drm_drv.h> +#include <drm/gpu_scheduler.h> +#include <drm/thames_accel.h>
+#include "thames_core.h" +#include "thames_drv.h" +#include "thames_transfers.h"
+struct thames_submit_bos;
+struct thames_sched_job {
struct drm_sched_job base;struct thames_device *tdev;struct thames_file_priv *file_priv;struct dma_fence *inference_done_fence;struct dma_fence *done_fence;/* @seqno moves at every push; the done fence keeps the first push's. */u64 seqno;u32 ring_index;u64 stamps[6];struct list_head node;struct thames_job *job;struct thames_submit_bos *bos;struct kref refcount;+};
+struct thames_job {
struct kref refcount;struct thames_device *tdev;u32 handle;struct drm_gem_object *program;struct drm_gem_object *params;/** Taken at create: after an unbind the Job faults in its Context rather* than read a binding that is gone.*/u64 program_dsp_va;u64 params_dsp_va;struct thames_transfer_table transfers;struct mutex lock;+};
+int thames_ioctl_job_create(struct drm_device *dev, void *data, struct drm_file *file); +int thames_ioctl_job_submit(struct drm_device *dev, void *data, struct drm_file *file); +int thames_ioctl_job_destroy(struct drm_device *dev, void *data, struct drm_file *file);
+void thames_job_complete(struct thames_core *core);
+int thames_job_init(struct thames_core *core); +int thames_job_suspend(struct thames_core *core); +void thames_job_resume(struct thames_core *core); +void thames_job_fini(struct thames_core *core); +int thames_job_open(struct thames_file_priv *thames_priv); +void thames_job_close(struct thames_file_priv *thames_priv);
+#endif diff --git a/drivers/accel/thames/thames_trace.h b/drivers/accel/thames/thames_trace.h new file mode 100644 index 000000000000..3d1e59708869 --- /dev/null +++ b/drivers/accel/thames/thames_trace.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2026 Texas Instruments Incorporated - https://www.ti.com/ */
+#undef TRACE_SYSTEM +#define TRACE_SYSTEM thames
+#if !defined(__THAMES_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __THAMES_TRACE_H__
+#include <linux/math64.h> +#include <linux/time64.h> +#include <linux/tracepoint.h> +#include <linux/types.h>
+#include "thames_core.h" +#include "thames_device.h" +#include "thames_drv.h" +#include "thames_job.h"
+/* Every stamp is the SoC's system counter in microseconds, so any two subtract. */ +TRACE_EVENT(thames_job_done,
TP_PROTO(const struct thames_core *core, const struct thames_sched_job *job,u64 signalled),TP_ARGS(core, job, signalled),TP_STRUCT__entry(__string(dev_name, dev_name(core->dev))__field(u32, core)__field(u32, context)__field(u64, seqno)__field(int, error)__field(u64, submit_us)__field(u64, doorbell_seen_us)__field(u64, run_start_us)__field(u64, kernel_entry_us)__field(u64, kernel_exit_us)__field(u64, run_end_us)__field(u64, signalled_us)),TP_fast_assign(u32 hz = core->tdev->caps.timestamp_hz;__assign_str(dev_name);__entry->core = core->index;__entry->context = job->file_priv->context_id;__entry->seqno = job->seqno;__entry->error = job->done_fence->error;__entry->submit_us =mul_u64_u32_div(job->stamps[0], USEC_PER_SEC, hz);__entry->doorbell_seen_us =mul_u64_u32_div(job->stamps[1], USEC_PER_SEC, hz);__entry->run_start_us =mul_u64_u32_div(job->stamps[2], USEC_PER_SEC, hz);__entry->run_end_us =mul_u64_u32_div(job->stamps[3], USEC_PER_SEC, hz);__entry->kernel_entry_us =mul_u64_u32_div(job->stamps[4], USEC_PER_SEC, hz);__entry->kernel_exit_us =mul_u64_u32_div(job->stamps[5], USEC_PER_SEC, hz);__entry->signalled_us =mul_u64_u32_div(signalled, USEC_PER_SEC, hz);),TP_printk("%s: core=%u context=%u seqno=%llu error=%d submit_us=%llu doorbell_seen_us=%llu run_start_us=%llu kernel_entry_us=%llu kernel_exit_us=%llu run_end_us=%llu signalled_us=%llu",__get_str(dev_name), __entry->core, __entry->context,__entry->seqno, __entry->error, __entry->submit_us,__entry->doorbell_seen_us, __entry->run_start_us,__entry->kernel_entry_us, __entry->kernel_exit_us,__entry->run_end_us, __entry->signalled_us)+);
+#endif /* __THAMES_TRACE_H__ */
+#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE thames_trace
+#include <trace/define_trace.h> diff --git a/include/uapi/drm/thames_accel.h b/include/uapi/drm/thames_accel.h index 5671cffaf8f9..bd2b4271b896 100644 --- a/include/uapi/drm/thames_accel.h +++ b/include/uapi/drm/thames_accel.h @@ -57,6 +57,15 @@ enum drm_thames_ioctl_id { /** @DRM_THAMES_DEV_QUERY: Query the device's capabilities. */ DRM_THAMES_DEV_QUERY,
/** @DRM_THAMES_JOB_CREATE: Validate a Dispatch once into a Job. */DRM_THAMES_JOB_CREATE,/** @DRM_THAMES_JOB_SUBMIT: Run created Jobs once, in order. */DRM_THAMES_JOB_SUBMIT,/** @DRM_THAMES_JOB_DESTROY: Release a Job and the buffers it holds. */DRM_THAMES_JOB_DESTROY,/** * @DRM_THAMES_BO_BIND: Bind buffers into the file's Context at the * addresses the client chooses, or unbind them.@@ -244,6 +253,21 @@ enum drm_thames_dev_query_type {
};
+/**
- enum drm_thames_job_type - What a Job is
- A Job is one Dispatch, and a submission is an ordered array of Jobs. A Job
- whose transfers are not K3 DMA records would be a new type, not a new
- operation, and so are the copies, fills, timestamps and the list-carrying
- command buffer a Vulkan compute driver will add. The query grows a field
- naming the types beyond the Dispatch when the first one comes; an absent type
- is refused at Job creation.
- */
+enum drm_thames_job_type {
/** @DRM_THAMES_JOB_DISPATCH: Run a Kernel with its transfers. */DRM_THAMES_JOB_DISPATCH = 0,+};
/**
- enum drm_thames_hardware_id - &drm_thames_caps.hardware_id
*/ @@ -255,8 +279,9 @@ enum drm_thames_hardware_id { /**
- struct drm_thames_caps - The capability query
- Each window sits at one address whatever core runs the Job and reaches that
- core's own memory.
- The submit's limits are stated on &struct drm_thames_job_submit. Each window
- sits at one address whatever core runs the Job and reaches that core's own
*/
- memory.
struct drm_thames_caps { /** @l2_size: Size in bytes of each core's L2 SRAM. */ @@ -328,6 +353,35 @@ struct drm_thames_caps { __u64 dru_capabilities; };
+/**
- enum drm_thames_fault_kind - The kind of a Job's fault
- The kind is what the firmware knows without decoding a register: what stopped
- the Job and whose mode it stopped in.
- */
+enum drm_thames_fault_kind {
/*** @DRM_THAMES_FAULT_KERNEL: A Kernel took an exception in user mode.* The Context continues; the registers say why.*/DRM_THAMES_FAULT_KERNEL = 1,/*** @DRM_THAMES_FAULT_FIRMWARE: The firmware took an exception in* supervisor mode while running this Job. The core was reset.*/DRM_THAMES_FAULT_FIRMWARE,/*** @DRM_THAMES_FAULT_TIMEOUT: The device's Job timeout fired: the Kernel* ran longer than the driver lets a Kernel run and the device ended it,* or the Kernel returned with transfers still running and they did not* drain. The Context continues; NRP and NTSR say where the Kernel was,* and are 0 for transfers that did not drain.*/DRM_THAMES_FAULT_TIMEOUT,+};
/**
- struct drm_thames_dev_query - Arguments passed to DRM_IOCTL_THAMES_DEV_QUERY
*/ @@ -353,9 +407,9 @@ struct drm_thames_dev_query {
- order: the first group starts at the first transfer, each later group at a
- transfer carrying this flag. The firmware submits the first group before it
- enters the Kernel, and each later group, whole, when the Kernel asks for it
- with system call 2. No group may hold more transfers on one channel than
- &drm_thames_caps.dru_queue_depth. Never on the first transfer. A Job
- without the flag is one group.
- with system call 2 (see &struct drm_thames_job_create). No group may hold
- more transfers on one channel than &drm_thames_caps.dru_queue_depth. Never
*/
- on the first transfer. A Job without the flag is one group.
#define DRM_THAMES_TRANSFER_GROUP (1 << 0)
@@ -393,7 +447,9 @@ struct drm_thames_dev_query {
- auxiliary aperture, at &drm_thames_caps.l2_main_dsp_va and l2_aux, which the
- kernel translates to the physical aperture of the core the Job runs on. The
- Job holds a reference on each buffer a transfer resolves to until it is
- destroyed and its last submission has completed.
- destroyed and its last submission has completed. No list names these
- buffers: the read and write lists of &drm_thames_job_submit are for
*/
- synchronisation alone.
struct drm_thames_transfer { /** @@ -409,6 +465,191 @@ struct drm_thames_transfer { __u32 words[16]; };
+/**
- struct drm_thames_job_create - Arguments passed to DRM_IOCTL_THAMES_JOB_CREATE
- Once per Dispatch: the transfers are copied, each checked and translated, a
- reference taken on the program, the params and every buffer a transfer
- resolves to so the translation holds for the Job's life, and a handle
- returned. The buffers the Kernel reads and writes are named at each
- submit, in &drm_thames_job_submit.
- The firmware enters the Kernel at the program's start as a C function
- taking one argument, the params' address, and returning a status word:
int kernel(void *params);
- That is the whole of what the kernel and the firmware know. The Kernel finds
- everything else, its inputs, outputs, constants, grid and status buffer,
- through params, whose layout is private to the userspace driver that emitted
- the Kernel. The program must have been created DRM_THAMES_BO_EXECUTABLE.
- The program and the params must be bound in the Context.
- The Job keeps the two addresses they are bound at when it is created, and
- every submission runs the Kernel at them.
- A Kernel asks for its Job's next transfer group with system call 2 (the
- SYSCALL instruction with 2, no argument). The call returns to the
- instruction after it with every register kept but A0-A3 and RP. The firmware
- submits the group only if the Job has one left and every channel the group
- uses has no transfer queued or running; otherwise it fails the Job, writing
- none of the group's transfers. A Kernel waits for the tile event of its
- channels' DRU records with system call 3.
- */
+struct drm_thames_job_create {
/** @type: One of &enum drm_thames_job_type. */__u32 type;/** @flags: MBZ. */__u32 flags;/** @program: Handle of the buffer holding the Kernel. */__u32 program;/** @params: Handle of the params buffer. */__u32 params;/*** @transfers: Array of &struct drm_thames_transfer, the DRU records* the compiler scheduled for this Kernel; may be empty.*/struct drm_thames_obj_array transfers;/** @handle: Returned handle of the Job. Job handles are nonzero. */__u32 handle;/** @pad: MBZ. */__u32 pad;+};
+/**
- DOC: Sync operation flags
- The low byte of &drm_thames_sync_op.flags is the handle type, bit 31 the
- direction; every other bit is MBZ.
- The one op accepted is a timeline sync object signalled,
- %DRM_THAMES_SYNC_OP_HANDLE_TYPE_TIMELINE_SYNCOBJ |
- %DRM_THAMES_SYNC_OP_SIGNAL. Handle type 0 and a clear bit 31, a binary sync
- object and a wait, are left for the client that will use them.
- %DRM_THAMES_SYNC_OP_HANDLE_TYPE_MASK: The bits of the handle type.
- %DRM_THAMES_SYNC_OP_HANDLE_TYPE_TIMELINE_SYNCOBJ: A timeline sync
- object, at the point &drm_thames_sync_op.timeline_value.
- %DRM_THAMES_SYNC_OP_SIGNAL: The call's last Job's finished fence is
- added to the timeline at the point.
- */
+#define DRM_THAMES_SYNC_OP_HANDLE_TYPE_MASK 0xff +#define DRM_THAMES_SYNC_OP_HANDLE_TYPE_TIMELINE_SYNCOBJ 1 +#define DRM_THAMES_SYNC_OP_SIGNAL (1u << 31)
+/**
- struct drm_thames_sync_op - One signal of a submit call
- */
+struct drm_thames_sync_op {
/*** @flags: %DRM_THAMES_SYNC_OP_HANDLE_TYPE_TIMELINE_SYNCOBJ |* %DRM_THAMES_SYNC_OP_SIGNAL.*/__u32 flags;/** @handle: A timeline sync object of the client. */__u32 handle;/*** @timeline_value: The point on the timeline sync object, nonzero.*/__u64 timeline_value;+};
+/**
- struct drm_thames_job_submit - Arguments passed to DRM_IOCTL_THAMES_JOB_SUBMIT
- A submission is an ordered array of Jobs, one Ring entry each, so a sequence
- of Dispatches is one call. Per run the kernel re-checks only the Context's
- current state, waits on the implicit fences of the buffers the call names and
- pushes the Ring entries. A submission of a Job while an earlier
- submission of the same Job is in flight waits for it, which the Ring's order
- gives on its own. One Ring completes its Jobs in submission order; there is
- no order between the two cores' Rings, but the Jobs of one call run in the
- order named.
- The buffers the call's Jobs read and write are named here, one pair of lists
- per call. They are for synchronisation alone: the first Job waits on the
- fences already on them, and the last Job's fence is set on them once the Jobs
- are pushed, for read usage on @read_handles and write usage on
- @write_handles, so DRM_IOCTL_THAMES_BO_PREP on a written buffer waits for the
- call's Jobs. No flag turns either side off. The call holds a reference on
- each buffer named until its last Job completes. A buffer may be named once
- in the two lists together.
- The sync objects the call signals are named in @syncs. Every op receives the
- call's last Job's finished fence, the same fence the listed buffers get; the
- call waits on no sync object. The call is all or nothing: every Job handle
- is looked up and every sync op resolved before the first Job is pushed, so a
- call that fails has submitted nothing and signals nothing. An opt-out of the
- implicit synchronisation would be a bit of @flags, appended under a minor
- bump.
- Every Job the call submits has its fault record cleared; a refused call
- clears none.
- A Job of the call that fails does not stop the ones after it: each later Job
- runs, and completes or fails on its own merits. Only a Context banned after
- its faults has its Jobs not yet run cancelled, their fences signalled with
- -ECANCELED, and its later submit calls refused with -ECANCELED.
- */
+struct drm_thames_job_submit {
/** @flags: MBZ. */__u32 flags;/** @pad: MBZ. */__u32 pad;/*** @jobs: Array of __u32 Job handles, run in order.** At least 1 and at most 256 Jobs.*/struct drm_thames_obj_array jobs;/*** @read_handles: Array of __u32 handles of the buffers the Jobs read;* may be empty.** At most 4096 handles in @read_handles and @write_handles together.*/struct drm_thames_obj_array read_handles;/*** @write_handles: Array of __u32 handles of the buffers the Jobs write;* may be empty. Counted with @read_handles.*/struct drm_thames_obj_array write_handles;/*** @syncs: Array of &struct drm_thames_sync_op; may be empty.** At most 64 ops.*/struct drm_thames_obj_array syncs;+};
+/**
- struct drm_thames_job_destroy - Arguments passed to DRM_IOCTL_THAMES_JOB_DESTROY
- Releases the handle and returns at once; it does not wait for the Job's
- submissions. A submission still in flight keeps the Job, its validated copy
- and the references on its buffers until it completes, and its fence signals
- as it would have. The handle names nothing afterwards.
- */
+struct drm_thames_job_destroy {
/** @handle: The Job. */__u32 handle;/** @pad: MBZ. */__u32 pad;+};
/**
- DRM_IOCTL_THAMES() - Build a thames IOCTL number
- @__access: Access type. Must be R, W or RW.
@@ -431,6 +672,12 @@ enum { DRM_IOCTL_THAMES(WR, BO_MMAP_OFFSET, bo_mmap_offset), DRM_IOCTL_THAMES_DEV_QUERY = DRM_IOCTL_THAMES(WR, DEV_QUERY, dev_query),
DRM_IOCTL_THAMES_JOB_CREATE =DRM_IOCTL_THAMES(WR, JOB_CREATE, job_create),DRM_IOCTL_THAMES_JOB_SUBMIT =DRM_IOCTL_THAMES(WR, JOB_SUBMIT, job_submit),DRM_IOCTL_THAMES_JOB_DESTROY =DRM_IOCTL_THAMES(WR, JOB_DESTROY, job_destroy), DRM_IOCTL_THAMES_BO_BIND = DRM_IOCTL_THAMES(WR, BO_BIND, bo_bind),};
-- 2.54.0
linaro-mm-sig@lists.linaro.org