162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020-2023 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <drm/drm_file.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/bitfield.h>
962306a36Sopenharmony_ci#include <linux/highmem.h>
1062306a36Sopenharmony_ci#include <linux/kthread.h>
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <uapi/drm/ivpu_accel.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "ivpu_drv.h"
1662306a36Sopenharmony_ci#include "ivpu_hw.h"
1762306a36Sopenharmony_ci#include "ivpu_ipc.h"
1862306a36Sopenharmony_ci#include "ivpu_job.h"
1962306a36Sopenharmony_ci#include "ivpu_jsm_msg.h"
2062306a36Sopenharmony_ci#include "ivpu_pm.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define CMD_BUF_IDX	     0
2362306a36Sopenharmony_ci#define JOB_ID_JOB_MASK	     GENMASK(7, 0)
2462306a36Sopenharmony_ci#define JOB_ID_CONTEXT_MASK  GENMASK(31, 8)
2562306a36Sopenharmony_ci#define JOB_MAX_BUFFER_COUNT 65535
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic unsigned int ivpu_tdr_timeout_ms;
2862306a36Sopenharmony_cimodule_param_named(tdr_timeout_ms, ivpu_tdr_timeout_ms, uint, 0644);
2962306a36Sopenharmony_ciMODULE_PARM_DESC(tdr_timeout_ms, "Timeout for device hang detection, in milliseconds, 0 - default");
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void ivpu_cmdq_ring_db(struct ivpu_device *vdev, struct ivpu_cmdq *cmdq)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	ivpu_hw_reg_db_set(vdev, cmdq->db_id);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv, u16 engine)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct ivpu_device *vdev = file_priv->vdev;
3962306a36Sopenharmony_ci	struct vpu_job_queue_header *jobq_header;
4062306a36Sopenharmony_ci	struct ivpu_cmdq *cmdq;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	cmdq = kzalloc(sizeof(*cmdq), GFP_KERNEL);
4362306a36Sopenharmony_ci	if (!cmdq)
4462306a36Sopenharmony_ci		return NULL;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	cmdq->mem = ivpu_bo_alloc_internal(vdev, 0, SZ_4K, DRM_IVPU_BO_WC);
4762306a36Sopenharmony_ci	if (!cmdq->mem)
4862306a36Sopenharmony_ci		goto cmdq_free;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	cmdq->db_id = file_priv->ctx.id + engine * ivpu_get_context_count(vdev);
5162306a36Sopenharmony_ci	cmdq->entry_count = (u32)((cmdq->mem->base.size - sizeof(struct vpu_job_queue_header)) /
5262306a36Sopenharmony_ci				  sizeof(struct vpu_job_queue_entry));
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	cmdq->jobq = (struct vpu_job_queue *)cmdq->mem->kvaddr;
5562306a36Sopenharmony_ci	jobq_header = &cmdq->jobq->header;
5662306a36Sopenharmony_ci	jobq_header->engine_idx = engine;
5762306a36Sopenharmony_ci	jobq_header->head = 0;
5862306a36Sopenharmony_ci	jobq_header->tail = 0;
5962306a36Sopenharmony_ci	wmb(); /* Flush WC buffer for jobq->header */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return cmdq;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cicmdq_free:
6462306a36Sopenharmony_ci	kfree(cmdq);
6562306a36Sopenharmony_ci	return NULL;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void ivpu_cmdq_free(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	if (!cmdq)
7162306a36Sopenharmony_ci		return;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	ivpu_bo_free_internal(cmdq->mem);
7462306a36Sopenharmony_ci	kfree(cmdq);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u16 engine)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct ivpu_device *vdev = file_priv->vdev;
8062306a36Sopenharmony_ci	struct ivpu_cmdq *cmdq = file_priv->cmdq[engine];
8162306a36Sopenharmony_ci	int ret;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	lockdep_assert_held(&file_priv->lock);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (!cmdq) {
8662306a36Sopenharmony_ci		cmdq = ivpu_cmdq_alloc(file_priv, engine);
8762306a36Sopenharmony_ci		if (!cmdq)
8862306a36Sopenharmony_ci			return NULL;
8962306a36Sopenharmony_ci		file_priv->cmdq[engine] = cmdq;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (cmdq->db_registered)
9362306a36Sopenharmony_ci		return cmdq;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	ret = ivpu_jsm_register_db(vdev, file_priv->ctx.id, cmdq->db_id,
9662306a36Sopenharmony_ci				   cmdq->mem->vpu_addr, cmdq->mem->base.size);
9762306a36Sopenharmony_ci	if (ret)
9862306a36Sopenharmony_ci		return NULL;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	cmdq->db_registered = true;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return cmdq;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void ivpu_cmdq_release_locked(struct ivpu_file_priv *file_priv, u16 engine)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct ivpu_cmdq *cmdq = file_priv->cmdq[engine];
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	lockdep_assert_held(&file_priv->lock);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (cmdq) {
11262306a36Sopenharmony_ci		file_priv->cmdq[engine] = NULL;
11362306a36Sopenharmony_ci		if (cmdq->db_registered)
11462306a36Sopenharmony_ci			ivpu_jsm_unregister_db(file_priv->vdev, cmdq->db_id);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		ivpu_cmdq_free(file_priv, cmdq);
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_civoid ivpu_cmdq_release_all(struct ivpu_file_priv *file_priv)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	int i;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	mutex_lock(&file_priv->lock);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	for (i = 0; i < IVPU_NUM_ENGINES; i++)
12762306a36Sopenharmony_ci		ivpu_cmdq_release_locked(file_priv, i);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	mutex_unlock(&file_priv->lock);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/*
13362306a36Sopenharmony_ci * Mark the doorbell as unregistered and reset job queue pointers.
13462306a36Sopenharmony_ci * This function needs to be called when the VPU hardware is restarted
13562306a36Sopenharmony_ci * and FW looses job queue state. The next time job queue is used it
13662306a36Sopenharmony_ci * will be registered again.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistatic void ivpu_cmdq_reset_locked(struct ivpu_file_priv *file_priv, u16 engine)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct ivpu_cmdq *cmdq = file_priv->cmdq[engine];
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	lockdep_assert_held(&file_priv->lock);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (cmdq) {
14562306a36Sopenharmony_ci		cmdq->db_registered = false;
14662306a36Sopenharmony_ci		cmdq->jobq->header.head = 0;
14762306a36Sopenharmony_ci		cmdq->jobq->header.tail = 0;
14862306a36Sopenharmony_ci		wmb(); /* Flush WC buffer for jobq header */
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void ivpu_cmdq_reset_all(struct ivpu_file_priv *file_priv)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	int i;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	mutex_lock(&file_priv->lock);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	for (i = 0; i < IVPU_NUM_ENGINES; i++)
15962306a36Sopenharmony_ci		ivpu_cmdq_reset_locked(file_priv, i);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	mutex_unlock(&file_priv->lock);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_civoid ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct ivpu_file_priv *file_priv;
16762306a36Sopenharmony_ci	unsigned long ctx_id;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	xa_for_each(&vdev->context_xa, ctx_id, file_priv) {
17062306a36Sopenharmony_ci		file_priv = ivpu_file_priv_get_by_ctx_id(vdev, ctx_id);
17162306a36Sopenharmony_ci		if (!file_priv)
17262306a36Sopenharmony_ci			continue;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		ivpu_cmdq_reset_all(file_priv);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		ivpu_file_priv_put(&file_priv);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int ivpu_cmdq_push_job(struct ivpu_cmdq *cmdq, struct ivpu_job *job)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct ivpu_device *vdev = job->vdev;
18362306a36Sopenharmony_ci	struct vpu_job_queue_header *header = &cmdq->jobq->header;
18462306a36Sopenharmony_ci	struct vpu_job_queue_entry *entry;
18562306a36Sopenharmony_ci	u32 tail = READ_ONCE(header->tail);
18662306a36Sopenharmony_ci	u32 next_entry = (tail + 1) % cmdq->entry_count;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Check if there is space left in job queue */
18962306a36Sopenharmony_ci	if (next_entry == header->head) {
19062306a36Sopenharmony_ci		ivpu_dbg(vdev, JOB, "Job queue full: ctx %d engine %d db %d head %d tail %d\n",
19162306a36Sopenharmony_ci			 job->file_priv->ctx.id, job->engine_idx, cmdq->db_id, header->head, tail);
19262306a36Sopenharmony_ci		return -EBUSY;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	entry = &cmdq->jobq->job[tail];
19662306a36Sopenharmony_ci	entry->batch_buf_addr = job->cmd_buf_vpu_addr;
19762306a36Sopenharmony_ci	entry->job_id = job->job_id;
19862306a36Sopenharmony_ci	entry->flags = 0;
19962306a36Sopenharmony_ci	wmb(); /* Ensure that tail is updated after filling entry */
20062306a36Sopenharmony_ci	header->tail = next_entry;
20162306a36Sopenharmony_ci	wmb(); /* Flush WC buffer for jobq header */
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistruct ivpu_fence {
20762306a36Sopenharmony_ci	struct dma_fence base;
20862306a36Sopenharmony_ci	spinlock_t lock; /* protects base */
20962306a36Sopenharmony_ci	struct ivpu_device *vdev;
21062306a36Sopenharmony_ci};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic inline struct ivpu_fence *to_vpu_fence(struct dma_fence *fence)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	return container_of(fence, struct ivpu_fence, base);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic const char *ivpu_fence_get_driver_name(struct dma_fence *fence)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	return DRIVER_NAME;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic const char *ivpu_fence_get_timeline_name(struct dma_fence *fence)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct ivpu_fence *ivpu_fence = to_vpu_fence(fence);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return dev_name(ivpu_fence->vdev->drm.dev);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic const struct dma_fence_ops ivpu_fence_ops = {
23062306a36Sopenharmony_ci	.get_driver_name = ivpu_fence_get_driver_name,
23162306a36Sopenharmony_ci	.get_timeline_name = ivpu_fence_get_timeline_name,
23262306a36Sopenharmony_ci};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic struct dma_fence *ivpu_fence_create(struct ivpu_device *vdev)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct ivpu_fence *fence;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
23962306a36Sopenharmony_ci	if (!fence)
24062306a36Sopenharmony_ci		return NULL;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	fence->vdev = vdev;
24362306a36Sopenharmony_ci	spin_lock_init(&fence->lock);
24462306a36Sopenharmony_ci	dma_fence_init(&fence->base, &ivpu_fence_ops, &fence->lock, dma_fence_context_alloc(1), 1);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return &fence->base;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void job_get(struct ivpu_job *job, struct ivpu_job **link)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct ivpu_device *vdev = job->vdev;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	kref_get(&job->ref);
25462306a36Sopenharmony_ci	*link = job;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	ivpu_dbg(vdev, KREF, "Job get: id %u refcount %u\n", job->job_id, kref_read(&job->ref));
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void job_release(struct kref *ref)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct ivpu_job *job = container_of(ref, struct ivpu_job, ref);
26262306a36Sopenharmony_ci	struct ivpu_device *vdev = job->vdev;
26362306a36Sopenharmony_ci	u32 i;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	for (i = 0; i < job->bo_count; i++)
26662306a36Sopenharmony_ci		if (job->bos[i])
26762306a36Sopenharmony_ci			drm_gem_object_put(&job->bos[i]->base);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	dma_fence_put(job->done_fence);
27062306a36Sopenharmony_ci	ivpu_file_priv_put(&job->file_priv);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	ivpu_dbg(vdev, KREF, "Job released: id %u\n", job->job_id);
27362306a36Sopenharmony_ci	kfree(job);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* Allow the VPU to get suspended, must be called after ivpu_file_priv_put() */
27662306a36Sopenharmony_ci	ivpu_rpm_put(vdev);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic void job_put(struct ivpu_job *job)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct ivpu_device *vdev = job->vdev;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ivpu_dbg(vdev, KREF, "Job put: id %u refcount %u\n", job->job_id, kref_read(&job->ref));
28462306a36Sopenharmony_ci	kref_put(&job->ref, job_release);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic struct ivpu_job *
28862306a36Sopenharmony_ciivpu_create_job(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct ivpu_device *vdev = file_priv->vdev;
29162306a36Sopenharmony_ci	struct ivpu_job *job;
29262306a36Sopenharmony_ci	int ret;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	ret = ivpu_rpm_get(vdev);
29562306a36Sopenharmony_ci	if (ret < 0)
29662306a36Sopenharmony_ci		return NULL;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	job = kzalloc(struct_size(job, bos, bo_count), GFP_KERNEL);
29962306a36Sopenharmony_ci	if (!job)
30062306a36Sopenharmony_ci		goto err_rpm_put;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	kref_init(&job->ref);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	job->vdev = vdev;
30562306a36Sopenharmony_ci	job->engine_idx = engine_idx;
30662306a36Sopenharmony_ci	job->bo_count = bo_count;
30762306a36Sopenharmony_ci	job->done_fence = ivpu_fence_create(vdev);
30862306a36Sopenharmony_ci	if (!job->done_fence) {
30962306a36Sopenharmony_ci		ivpu_warn_ratelimited(vdev, "Failed to create a fence\n");
31062306a36Sopenharmony_ci		goto err_free_job;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	job->file_priv = ivpu_file_priv_get(file_priv);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	ivpu_dbg(vdev, JOB, "Job created: ctx %2d engine %d", file_priv->ctx.id, job->engine_idx);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	return job;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cierr_free_job:
32062306a36Sopenharmony_ci	kfree(job);
32162306a36Sopenharmony_cierr_rpm_put:
32262306a36Sopenharmony_ci	ivpu_rpm_put(vdev);
32362306a36Sopenharmony_ci	return NULL;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int ivpu_job_done(struct ivpu_device *vdev, u32 job_id, u32 job_status)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct ivpu_job *job;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	job = xa_erase(&vdev->submitted_jobs_xa, job_id);
33162306a36Sopenharmony_ci	if (!job)
33262306a36Sopenharmony_ci		return -ENOENT;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (job->file_priv->has_mmu_faults)
33562306a36Sopenharmony_ci		job_status = VPU_JSM_STATUS_ABORTED;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	job->bos[CMD_BUF_IDX]->job_status = job_status;
33862306a36Sopenharmony_ci	dma_fence_signal(job->done_fence);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ivpu_dbg(vdev, JOB, "Job complete:  id %3u ctx %2d engine %d status 0x%x\n",
34162306a36Sopenharmony_ci		 job->job_id, job->file_priv->ctx.id, job->engine_idx, job_status);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	job_put(job);
34462306a36Sopenharmony_ci	return 0;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void ivpu_job_done_message(struct ivpu_device *vdev, void *msg)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct vpu_ipc_msg_payload_job_done *payload;
35062306a36Sopenharmony_ci	struct vpu_jsm_msg *job_ret_msg = msg;
35162306a36Sopenharmony_ci	int ret;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	payload = (struct vpu_ipc_msg_payload_job_done *)&job_ret_msg->payload;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	ret = ivpu_job_done(vdev, payload->job_id, payload->job_status);
35662306a36Sopenharmony_ci	if (ret)
35762306a36Sopenharmony_ci		ivpu_err(vdev, "Failed to finish job %d: %d\n", payload->job_id, ret);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_civoid ivpu_jobs_abort_all(struct ivpu_device *vdev)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct ivpu_job *job;
36362306a36Sopenharmony_ci	unsigned long id;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	xa_for_each(&vdev->submitted_jobs_xa, id, job)
36662306a36Sopenharmony_ci		ivpu_job_done(vdev, id, VPU_JSM_STATUS_ABORTED);
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int ivpu_direct_job_submission(struct ivpu_job *job)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct ivpu_file_priv *file_priv = job->file_priv;
37262306a36Sopenharmony_ci	struct ivpu_device *vdev = job->vdev;
37362306a36Sopenharmony_ci	struct xa_limit job_id_range;
37462306a36Sopenharmony_ci	struct ivpu_cmdq *cmdq;
37562306a36Sopenharmony_ci	int ret;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	mutex_lock(&file_priv->lock);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	cmdq = ivpu_cmdq_acquire(job->file_priv, job->engine_idx);
38062306a36Sopenharmony_ci	if (!cmdq) {
38162306a36Sopenharmony_ci		ivpu_warn(vdev, "Failed get job queue, ctx %d engine %d\n",
38262306a36Sopenharmony_ci			  file_priv->ctx.id, job->engine_idx);
38362306a36Sopenharmony_ci		ret = -EINVAL;
38462306a36Sopenharmony_ci		goto err_unlock;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	job_id_range.min = FIELD_PREP(JOB_ID_CONTEXT_MASK, (file_priv->ctx.id - 1));
38862306a36Sopenharmony_ci	job_id_range.max = job_id_range.min | JOB_ID_JOB_MASK;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	job_get(job, &job);
39162306a36Sopenharmony_ci	ret = xa_alloc(&vdev->submitted_jobs_xa, &job->job_id, job, job_id_range, GFP_KERNEL);
39262306a36Sopenharmony_ci	if (ret) {
39362306a36Sopenharmony_ci		ivpu_warn_ratelimited(vdev, "Failed to allocate job id: %d\n", ret);
39462306a36Sopenharmony_ci		goto err_job_put;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	ret = ivpu_cmdq_push_job(cmdq, job);
39862306a36Sopenharmony_ci	if (ret)
39962306a36Sopenharmony_ci		goto err_xa_erase;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	ivpu_dbg(vdev, JOB, "Job submitted: id %3u addr 0x%llx ctx %2d engine %d next %d\n",
40262306a36Sopenharmony_ci		 job->job_id, job->cmd_buf_vpu_addr, file_priv->ctx.id,
40362306a36Sopenharmony_ci		 job->engine_idx, cmdq->jobq->header.tail);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (ivpu_test_mode == IVPU_TEST_MODE_NULL_HW) {
40662306a36Sopenharmony_ci		ivpu_job_done(vdev, job->job_id, VPU_JSM_STATUS_SUCCESS);
40762306a36Sopenharmony_ci		cmdq->jobq->header.head = cmdq->jobq->header.tail;
40862306a36Sopenharmony_ci		wmb(); /* Flush WC buffer for jobq header */
40962306a36Sopenharmony_ci	} else {
41062306a36Sopenharmony_ci		ivpu_cmdq_ring_db(vdev, cmdq);
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	mutex_unlock(&file_priv->lock);
41462306a36Sopenharmony_ci	return 0;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cierr_xa_erase:
41762306a36Sopenharmony_ci	xa_erase(&vdev->submitted_jobs_xa, job->job_id);
41862306a36Sopenharmony_cierr_job_put:
41962306a36Sopenharmony_ci	job_put(job);
42062306a36Sopenharmony_cierr_unlock:
42162306a36Sopenharmony_ci	mutex_unlock(&file_priv->lock);
42262306a36Sopenharmony_ci	return ret;
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic int
42662306a36Sopenharmony_ciivpu_job_prepare_bos_for_submit(struct drm_file *file, struct ivpu_job *job, u32 *buf_handles,
42762306a36Sopenharmony_ci				u32 buf_count, u32 commands_offset)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct ivpu_file_priv *file_priv = file->driver_priv;
43062306a36Sopenharmony_ci	struct ivpu_device *vdev = file_priv->vdev;
43162306a36Sopenharmony_ci	struct ww_acquire_ctx acquire_ctx;
43262306a36Sopenharmony_ci	enum dma_resv_usage usage;
43362306a36Sopenharmony_ci	struct ivpu_bo *bo;
43462306a36Sopenharmony_ci	int ret;
43562306a36Sopenharmony_ci	u32 i;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	for (i = 0; i < buf_count; i++) {
43862306a36Sopenharmony_ci		struct drm_gem_object *obj = drm_gem_object_lookup(file, buf_handles[i]);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		if (!obj)
44162306a36Sopenharmony_ci			return -ENOENT;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		job->bos[i] = to_ivpu_bo(obj);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		ret = ivpu_bo_pin(job->bos[i]);
44662306a36Sopenharmony_ci		if (ret)
44762306a36Sopenharmony_ci			return ret;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	bo = job->bos[CMD_BUF_IDX];
45162306a36Sopenharmony_ci	if (!dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_READ)) {
45262306a36Sopenharmony_ci		ivpu_warn(vdev, "Buffer is already in use\n");
45362306a36Sopenharmony_ci		return -EBUSY;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (commands_offset >= bo->base.size) {
45762306a36Sopenharmony_ci		ivpu_warn(vdev, "Invalid command buffer offset %u\n", commands_offset);
45862306a36Sopenharmony_ci		return -EINVAL;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	job->cmd_buf_vpu_addr = bo->vpu_addr + commands_offset;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	ret = drm_gem_lock_reservations((struct drm_gem_object **)job->bos, buf_count,
46462306a36Sopenharmony_ci					&acquire_ctx);
46562306a36Sopenharmony_ci	if (ret) {
46662306a36Sopenharmony_ci		ivpu_warn(vdev, "Failed to lock reservations: %d\n", ret);
46762306a36Sopenharmony_ci		return ret;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	for (i = 0; i < buf_count; i++) {
47162306a36Sopenharmony_ci		ret = dma_resv_reserve_fences(job->bos[i]->base.resv, 1);
47262306a36Sopenharmony_ci		if (ret) {
47362306a36Sopenharmony_ci			ivpu_warn(vdev, "Failed to reserve fences: %d\n", ret);
47462306a36Sopenharmony_ci			goto unlock_reservations;
47562306a36Sopenharmony_ci		}
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	for (i = 0; i < buf_count; i++) {
47962306a36Sopenharmony_ci		usage = (i == CMD_BUF_IDX) ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_BOOKKEEP;
48062306a36Sopenharmony_ci		dma_resv_add_fence(job->bos[i]->base.resv, job->done_fence, usage);
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ciunlock_reservations:
48462306a36Sopenharmony_ci	drm_gem_unlock_reservations((struct drm_gem_object **)job->bos, buf_count, &acquire_ctx);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	wmb(); /* Flush write combining buffers */
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return ret;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ciint ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct ivpu_file_priv *file_priv = file->driver_priv;
49462306a36Sopenharmony_ci	struct ivpu_device *vdev = file_priv->vdev;
49562306a36Sopenharmony_ci	struct drm_ivpu_submit *params = data;
49662306a36Sopenharmony_ci	struct ivpu_job *job;
49762306a36Sopenharmony_ci	u32 *buf_handles;
49862306a36Sopenharmony_ci	int idx, ret;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (params->engine > DRM_IVPU_ENGINE_COPY)
50162306a36Sopenharmony_ci		return -EINVAL;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (params->buffer_count == 0 || params->buffer_count > JOB_MAX_BUFFER_COUNT)
50462306a36Sopenharmony_ci		return -EINVAL;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (!IS_ALIGNED(params->commands_offset, 8))
50762306a36Sopenharmony_ci		return -EINVAL;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (!file_priv->ctx.id)
51062306a36Sopenharmony_ci		return -EINVAL;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (file_priv->has_mmu_faults)
51362306a36Sopenharmony_ci		return -EBADFD;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	buf_handles = kcalloc(params->buffer_count, sizeof(u32), GFP_KERNEL);
51662306a36Sopenharmony_ci	if (!buf_handles)
51762306a36Sopenharmony_ci		return -ENOMEM;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	ret = copy_from_user(buf_handles,
52062306a36Sopenharmony_ci			     (void __user *)params->buffers_ptr,
52162306a36Sopenharmony_ci			     params->buffer_count * sizeof(u32));
52262306a36Sopenharmony_ci	if (ret) {
52362306a36Sopenharmony_ci		ret = -EFAULT;
52462306a36Sopenharmony_ci		goto free_handles;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (!drm_dev_enter(&vdev->drm, &idx)) {
52862306a36Sopenharmony_ci		ret = -ENODEV;
52962306a36Sopenharmony_ci		goto free_handles;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	ivpu_dbg(vdev, JOB, "Submit ioctl: ctx %u buf_count %u\n",
53362306a36Sopenharmony_ci		 file_priv->ctx.id, params->buffer_count);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	job = ivpu_create_job(file_priv, params->engine, params->buffer_count);
53662306a36Sopenharmony_ci	if (!job) {
53762306a36Sopenharmony_ci		ivpu_err(vdev, "Failed to create job\n");
53862306a36Sopenharmony_ci		ret = -ENOMEM;
53962306a36Sopenharmony_ci		goto dev_exit;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	ret = ivpu_job_prepare_bos_for_submit(file, job, buf_handles, params->buffer_count,
54362306a36Sopenharmony_ci					      params->commands_offset);
54462306a36Sopenharmony_ci	if (ret) {
54562306a36Sopenharmony_ci		ivpu_err(vdev, "Failed to prepare job, ret %d\n", ret);
54662306a36Sopenharmony_ci		goto job_put;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	ret = ivpu_direct_job_submission(job);
55062306a36Sopenharmony_ci	if (ret) {
55162306a36Sopenharmony_ci		dma_fence_signal(job->done_fence);
55262306a36Sopenharmony_ci		ivpu_err(vdev, "Failed to submit job to the HW, ret %d\n", ret);
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cijob_put:
55662306a36Sopenharmony_ci	job_put(job);
55762306a36Sopenharmony_cidev_exit:
55862306a36Sopenharmony_ci	drm_dev_exit(idx);
55962306a36Sopenharmony_cifree_handles:
56062306a36Sopenharmony_ci	kfree(buf_handles);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	return ret;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic int ivpu_job_done_thread(void *arg)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	struct ivpu_device *vdev = (struct ivpu_device *)arg;
56862306a36Sopenharmony_ci	struct ivpu_ipc_consumer cons;
56962306a36Sopenharmony_ci	struct vpu_jsm_msg jsm_msg;
57062306a36Sopenharmony_ci	bool jobs_submitted;
57162306a36Sopenharmony_ci	unsigned int timeout;
57262306a36Sopenharmony_ci	int ret;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	ivpu_dbg(vdev, JOB, "Started %s\n", __func__);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	ivpu_ipc_consumer_add(vdev, &cons, VPU_IPC_CHAN_JOB_RET);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	while (!kthread_should_stop()) {
57962306a36Sopenharmony_ci		timeout = ivpu_tdr_timeout_ms ? ivpu_tdr_timeout_ms : vdev->timeout.tdr;
58062306a36Sopenharmony_ci		jobs_submitted = !xa_empty(&vdev->submitted_jobs_xa);
58162306a36Sopenharmony_ci		ret = ivpu_ipc_receive(vdev, &cons, NULL, &jsm_msg, timeout);
58262306a36Sopenharmony_ci		if (!ret) {
58362306a36Sopenharmony_ci			ivpu_job_done_message(vdev, &jsm_msg);
58462306a36Sopenharmony_ci		} else if (ret == -ETIMEDOUT) {
58562306a36Sopenharmony_ci			if (jobs_submitted && !xa_empty(&vdev->submitted_jobs_xa)) {
58662306a36Sopenharmony_ci				ivpu_err(vdev, "TDR detected, timeout %d ms", timeout);
58762306a36Sopenharmony_ci				ivpu_hw_diagnose_failure(vdev);
58862306a36Sopenharmony_ci				ivpu_pm_schedule_recovery(vdev);
58962306a36Sopenharmony_ci			}
59062306a36Sopenharmony_ci		}
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	ivpu_ipc_consumer_del(vdev, &cons);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	ivpu_jobs_abort_all(vdev);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	ivpu_dbg(vdev, JOB, "Stopped %s\n", __func__);
59862306a36Sopenharmony_ci	return 0;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ciint ivpu_job_done_thread_init(struct ivpu_device *vdev)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	struct task_struct *thread;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	thread = kthread_run(&ivpu_job_done_thread, (void *)vdev, "ivpu_job_done_thread");
60662306a36Sopenharmony_ci	if (IS_ERR(thread)) {
60762306a36Sopenharmony_ci		ivpu_err(vdev, "Failed to start job completion thread\n");
60862306a36Sopenharmony_ci		return -EIO;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	get_task_struct(thread);
61262306a36Sopenharmony_ci	wake_up_process(thread);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	vdev->job_done_thread = thread;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	return 0;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_civoid ivpu_job_done_thread_fini(struct ivpu_device *vdev)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	kthread_stop(vdev->job_done_thread);
62262306a36Sopenharmony_ci	put_task_struct(vdev->job_done_thread);
62362306a36Sopenharmony_ci}
624