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