xref: /linux/drivers/accel/rocket/rocket_job.c (revision 58809f614e0e3f4e12b489bddf680bfeb31c0a20)
10810d5adSTomeu Vizoso // SPDX-License-Identifier: GPL-2.0-only
20810d5adSTomeu Vizoso /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
30810d5adSTomeu Vizoso /* Copyright 2019 Collabora ltd. */
40810d5adSTomeu Vizoso /* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
50810d5adSTomeu Vizoso 
60810d5adSTomeu Vizoso #include <drm/drm_print.h>
70810d5adSTomeu Vizoso #include <drm/drm_file.h>
80810d5adSTomeu Vizoso #include <drm/drm_gem.h>
90810d5adSTomeu Vizoso #include <drm/rocket_accel.h>
100810d5adSTomeu Vizoso #include <linux/interrupt.h>
110810d5adSTomeu Vizoso #include <linux/iommu.h>
120810d5adSTomeu Vizoso #include <linux/platform_device.h>
130810d5adSTomeu Vizoso #include <linux/pm_runtime.h>
140810d5adSTomeu Vizoso 
150810d5adSTomeu Vizoso #include "rocket_core.h"
160810d5adSTomeu Vizoso #include "rocket_device.h"
170810d5adSTomeu Vizoso #include "rocket_drv.h"
180810d5adSTomeu Vizoso #include "rocket_job.h"
190810d5adSTomeu Vizoso #include "rocket_registers.h"
200810d5adSTomeu Vizoso 
210810d5adSTomeu Vizoso #define JOB_TIMEOUT_MS 500
220810d5adSTomeu Vizoso 
230810d5adSTomeu Vizoso static struct rocket_job *
240810d5adSTomeu Vizoso to_rocket_job(struct drm_sched_job *sched_job)
250810d5adSTomeu Vizoso {
260810d5adSTomeu Vizoso 	return container_of(sched_job, struct rocket_job, base);
270810d5adSTomeu Vizoso }
280810d5adSTomeu Vizoso 
290810d5adSTomeu Vizoso static const char *rocket_fence_get_driver_name(struct dma_fence *fence)
300810d5adSTomeu Vizoso {
310810d5adSTomeu Vizoso 	return "rocket";
320810d5adSTomeu Vizoso }
330810d5adSTomeu Vizoso 
340810d5adSTomeu Vizoso static const char *rocket_fence_get_timeline_name(struct dma_fence *fence)
350810d5adSTomeu Vizoso {
360810d5adSTomeu Vizoso 	return "rockchip-npu";
370810d5adSTomeu Vizoso }
380810d5adSTomeu Vizoso 
390810d5adSTomeu Vizoso static const struct dma_fence_ops rocket_fence_ops = {
400810d5adSTomeu Vizoso 	.get_driver_name = rocket_fence_get_driver_name,
410810d5adSTomeu Vizoso 	.get_timeline_name = rocket_fence_get_timeline_name,
420810d5adSTomeu Vizoso };
430810d5adSTomeu Vizoso 
440810d5adSTomeu Vizoso static struct dma_fence *rocket_fence_create(struct rocket_core *core)
450810d5adSTomeu Vizoso {
460810d5adSTomeu Vizoso 	struct dma_fence *fence;
470810d5adSTomeu Vizoso 
480810d5adSTomeu Vizoso 	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
490810d5adSTomeu Vizoso 	if (!fence)
500810d5adSTomeu Vizoso 		return ERR_PTR(-ENOMEM);
510810d5adSTomeu Vizoso 
520810d5adSTomeu Vizoso 	dma_fence_init(fence, &rocket_fence_ops, &core->fence_lock,
530810d5adSTomeu Vizoso 		       core->fence_context, ++core->emit_seqno);
540810d5adSTomeu Vizoso 
550810d5adSTomeu Vizoso 	return fence;
560810d5adSTomeu Vizoso }
570810d5adSTomeu Vizoso 
580810d5adSTomeu Vizoso static int
590810d5adSTomeu Vizoso rocket_copy_tasks(struct drm_device *dev,
600810d5adSTomeu Vizoso 		  struct drm_file *file_priv,
610810d5adSTomeu Vizoso 		  struct drm_rocket_job *job,
620810d5adSTomeu Vizoso 		  struct rocket_job *rjob)
630810d5adSTomeu Vizoso {
640810d5adSTomeu Vizoso 	int ret = 0;
650810d5adSTomeu Vizoso 
660810d5adSTomeu Vizoso 	if (job->task_struct_size < sizeof(struct drm_rocket_task))
670810d5adSTomeu Vizoso 		return -EINVAL;
680810d5adSTomeu Vizoso 
690810d5adSTomeu Vizoso 	rjob->task_count = job->task_count;
700810d5adSTomeu Vizoso 
710810d5adSTomeu Vizoso 	if (!rjob->task_count)
720810d5adSTomeu Vizoso 		return 0;
730810d5adSTomeu Vizoso 
740810d5adSTomeu Vizoso 	rjob->tasks = kvmalloc_array(job->task_count, sizeof(*rjob->tasks), GFP_KERNEL);
750810d5adSTomeu Vizoso 	if (!rjob->tasks) {
760810d5adSTomeu Vizoso 		drm_dbg(dev, "Failed to allocate task array\n");
770810d5adSTomeu Vizoso 		return -ENOMEM;
780810d5adSTomeu Vizoso 	}
790810d5adSTomeu Vizoso 
800810d5adSTomeu Vizoso 	for (int i = 0; i < rjob->task_count; i++) {
810810d5adSTomeu Vizoso 		struct drm_rocket_task task = {0};
820810d5adSTomeu Vizoso 
830810d5adSTomeu Vizoso 		if (copy_from_user(&task,
840810d5adSTomeu Vizoso 				   u64_to_user_ptr(job->tasks) + i * job->task_struct_size,
850810d5adSTomeu Vizoso 				   sizeof(task))) {
860810d5adSTomeu Vizoso 			drm_dbg(dev, "Failed to copy incoming tasks\n");
870810d5adSTomeu Vizoso 			ret = -EFAULT;
880810d5adSTomeu Vizoso 			goto fail;
890810d5adSTomeu Vizoso 		}
900810d5adSTomeu Vizoso 
910810d5adSTomeu Vizoso 		if (task.regcmd_count == 0) {
920810d5adSTomeu Vizoso 			drm_dbg(dev, "regcmd_count field in drm_rocket_task should be > 0.\n");
930810d5adSTomeu Vizoso 			ret = -EINVAL;
940810d5adSTomeu Vizoso 			goto fail;
950810d5adSTomeu Vizoso 		}
960810d5adSTomeu Vizoso 
970810d5adSTomeu Vizoso 		rjob->tasks[i].regcmd = task.regcmd;
980810d5adSTomeu Vizoso 		rjob->tasks[i].regcmd_count = task.regcmd_count;
990810d5adSTomeu Vizoso 	}
1000810d5adSTomeu Vizoso 
1010810d5adSTomeu Vizoso 	return 0;
1020810d5adSTomeu Vizoso 
1030810d5adSTomeu Vizoso fail:
1040810d5adSTomeu Vizoso 	kvfree(rjob->tasks);
1050810d5adSTomeu Vizoso 	return ret;
1060810d5adSTomeu Vizoso }
1070810d5adSTomeu Vizoso 
1080810d5adSTomeu Vizoso static void rocket_job_hw_submit(struct rocket_core *core, struct rocket_job *job)
1090810d5adSTomeu Vizoso {
1100810d5adSTomeu Vizoso 	struct rocket_task *task;
1110810d5adSTomeu Vizoso 	unsigned int extra_bit;
1120810d5adSTomeu Vizoso 
1130810d5adSTomeu Vizoso 	/* Don't queue the job if a reset is in progress */
1140810d5adSTomeu Vizoso 	if (atomic_read(&core->reset.pending))
1150810d5adSTomeu Vizoso 		return;
1160810d5adSTomeu Vizoso 
1170810d5adSTomeu Vizoso 	/* GO ! */
1180810d5adSTomeu Vizoso 
1190810d5adSTomeu Vizoso 	task = &job->tasks[job->next_task_idx];
1200810d5adSTomeu Vizoso 	job->next_task_idx++;
1210810d5adSTomeu Vizoso 
1220810d5adSTomeu Vizoso 	rocket_pc_writel(core, BASE_ADDRESS, 0x1);
1230810d5adSTomeu Vizoso 
1240810d5adSTomeu Vizoso 	 /* From rknpu, in the TRM this bit is marked as reserved */
1250810d5adSTomeu Vizoso 	extra_bit = 0x10000000 * core->index;
1260810d5adSTomeu Vizoso 	rocket_cna_writel(core, S_POINTER, CNA_S_POINTER_POINTER_PP_EN(1) |
1270810d5adSTomeu Vizoso 					   CNA_S_POINTER_EXECUTER_PP_EN(1) |
1280810d5adSTomeu Vizoso 					   CNA_S_POINTER_POINTER_PP_MODE(1) |
1290810d5adSTomeu Vizoso 					   extra_bit);
1300810d5adSTomeu Vizoso 
1310810d5adSTomeu Vizoso 	rocket_core_writel(core, S_POINTER, CORE_S_POINTER_POINTER_PP_EN(1) |
1320810d5adSTomeu Vizoso 					    CORE_S_POINTER_EXECUTER_PP_EN(1) |
1330810d5adSTomeu Vizoso 					    CORE_S_POINTER_POINTER_PP_MODE(1) |
1340810d5adSTomeu Vizoso 					    extra_bit);
1350810d5adSTomeu Vizoso 
1360810d5adSTomeu Vizoso 	rocket_pc_writel(core, BASE_ADDRESS, task->regcmd);
1370810d5adSTomeu Vizoso 	rocket_pc_writel(core, REGISTER_AMOUNTS,
1380810d5adSTomeu Vizoso 			 PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT((task->regcmd_count + 1) / 2 - 1));
1390810d5adSTomeu Vizoso 
1400810d5adSTomeu Vizoso 	rocket_pc_writel(core, INTERRUPT_MASK, PC_INTERRUPT_MASK_DPU_0 | PC_INTERRUPT_MASK_DPU_1);
1410810d5adSTomeu Vizoso 	rocket_pc_writel(core, INTERRUPT_CLEAR, PC_INTERRUPT_CLEAR_DPU_0 | PC_INTERRUPT_CLEAR_DPU_1);
1420810d5adSTomeu Vizoso 
1430810d5adSTomeu Vizoso 	rocket_pc_writel(core, TASK_CON, PC_TASK_CON_RESERVED_0(1) |
1440810d5adSTomeu Vizoso 					 PC_TASK_CON_TASK_COUNT_CLEAR(1) |
1450810d5adSTomeu Vizoso 					 PC_TASK_CON_TASK_NUMBER(1) |
1460810d5adSTomeu Vizoso 					 PC_TASK_CON_TASK_PP_EN(1));
1470810d5adSTomeu Vizoso 
1480810d5adSTomeu Vizoso 	rocket_pc_writel(core, TASK_DMA_BASE_ADDR, PC_TASK_DMA_BASE_ADDR_DMA_BASE_ADDR(0x0));
1490810d5adSTomeu Vizoso 
1500810d5adSTomeu Vizoso 	rocket_pc_writel(core, OPERATION_ENABLE, PC_OPERATION_ENABLE_OP_EN(1));
1510810d5adSTomeu Vizoso 
1520810d5adSTomeu Vizoso 	dev_dbg(core->dev, "Submitted regcmd at 0x%llx to core %d", task->regcmd, core->index);
1530810d5adSTomeu Vizoso }
1540810d5adSTomeu Vizoso 
1550810d5adSTomeu Vizoso static int rocket_acquire_object_fences(struct drm_gem_object **bos,
1560810d5adSTomeu Vizoso 					int bo_count,
1570810d5adSTomeu Vizoso 					struct drm_sched_job *job,
1580810d5adSTomeu Vizoso 					bool is_write)
1590810d5adSTomeu Vizoso {
1600810d5adSTomeu Vizoso 	int i, ret;
1610810d5adSTomeu Vizoso 
1620810d5adSTomeu Vizoso 	for (i = 0; i < bo_count; i++) {
1630810d5adSTomeu Vizoso 		ret = dma_resv_reserve_fences(bos[i]->resv, 1);
1640810d5adSTomeu Vizoso 		if (ret)
1650810d5adSTomeu Vizoso 			return ret;
1660810d5adSTomeu Vizoso 
1670810d5adSTomeu Vizoso 		ret = drm_sched_job_add_implicit_dependencies(job, bos[i],
1680810d5adSTomeu Vizoso 							      is_write);
1690810d5adSTomeu Vizoso 		if (ret)
1700810d5adSTomeu Vizoso 			return ret;
1710810d5adSTomeu Vizoso 	}
1720810d5adSTomeu Vizoso 
1730810d5adSTomeu Vizoso 	return 0;
1740810d5adSTomeu Vizoso }
1750810d5adSTomeu Vizoso 
1760810d5adSTomeu Vizoso static void rocket_attach_object_fences(struct drm_gem_object **bos,
1770810d5adSTomeu Vizoso 					int bo_count,
1780810d5adSTomeu Vizoso 					struct dma_fence *fence)
1790810d5adSTomeu Vizoso {
1800810d5adSTomeu Vizoso 	int i;
1810810d5adSTomeu Vizoso 
1820810d5adSTomeu Vizoso 	for (i = 0; i < bo_count; i++)
1830810d5adSTomeu Vizoso 		dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);
1840810d5adSTomeu Vizoso }
1850810d5adSTomeu Vizoso 
1860810d5adSTomeu Vizoso static int rocket_job_push(struct rocket_job *job)
1870810d5adSTomeu Vizoso {
1880810d5adSTomeu Vizoso 	struct rocket_device *rdev = job->rdev;
1890810d5adSTomeu Vizoso 	struct drm_gem_object **bos;
1900810d5adSTomeu Vizoso 	struct ww_acquire_ctx acquire_ctx;
1910810d5adSTomeu Vizoso 	int ret = 0;
1920810d5adSTomeu Vizoso 
1930810d5adSTomeu Vizoso 	bos = kvmalloc_array(job->in_bo_count + job->out_bo_count, sizeof(void *),
1940810d5adSTomeu Vizoso 			     GFP_KERNEL);
1950810d5adSTomeu Vizoso 	memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *));
1960810d5adSTomeu Vizoso 	memcpy(&bos[job->in_bo_count], job->out_bos, job->out_bo_count * sizeof(void *));
1970810d5adSTomeu Vizoso 
1980810d5adSTomeu Vizoso 	ret = drm_gem_lock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
1990810d5adSTomeu Vizoso 	if (ret)
2000810d5adSTomeu Vizoso 		goto err;
2010810d5adSTomeu Vizoso 
2020810d5adSTomeu Vizoso 	scoped_guard(mutex, &rdev->sched_lock) {
2030810d5adSTomeu Vizoso 		drm_sched_job_arm(&job->base);
2040810d5adSTomeu Vizoso 
2050810d5adSTomeu Vizoso 		job->inference_done_fence = dma_fence_get(&job->base.s_fence->finished);
2060810d5adSTomeu Vizoso 
2070810d5adSTomeu Vizoso 		ret = rocket_acquire_object_fences(job->in_bos, job->in_bo_count, &job->base, false);
2080810d5adSTomeu Vizoso 		if (ret)
2090810d5adSTomeu Vizoso 			goto err_unlock;
2100810d5adSTomeu Vizoso 
2110810d5adSTomeu Vizoso 		ret = rocket_acquire_object_fences(job->out_bos, job->out_bo_count, &job->base, true);
2120810d5adSTomeu Vizoso 		if (ret)
2130810d5adSTomeu Vizoso 			goto err_unlock;
2140810d5adSTomeu Vizoso 
2150810d5adSTomeu Vizoso 		kref_get(&job->refcount); /* put by scheduler job completion */
2160810d5adSTomeu Vizoso 
2170810d5adSTomeu Vizoso 		drm_sched_entity_push_job(&job->base);
2180810d5adSTomeu Vizoso 	}
2190810d5adSTomeu Vizoso 
2200810d5adSTomeu Vizoso 	rocket_attach_object_fences(job->out_bos, job->out_bo_count, job->inference_done_fence);
2210810d5adSTomeu Vizoso 
2220810d5adSTomeu Vizoso err_unlock:
2230810d5adSTomeu Vizoso 	drm_gem_unlock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
2240810d5adSTomeu Vizoso err:
225ce6b656bSBrigham Campbell 	kvfree(bos);
2260810d5adSTomeu Vizoso 
2270810d5adSTomeu Vizoso 	return ret;
2280810d5adSTomeu Vizoso }
2290810d5adSTomeu Vizoso 
2300810d5adSTomeu Vizoso static void rocket_job_cleanup(struct kref *ref)
2310810d5adSTomeu Vizoso {
2320810d5adSTomeu Vizoso 	struct rocket_job *job = container_of(ref, struct rocket_job,
2330810d5adSTomeu Vizoso 						refcount);
2340810d5adSTomeu Vizoso 	unsigned int i;
2350810d5adSTomeu Vizoso 
2360810d5adSTomeu Vizoso 	rocket_iommu_domain_put(job->domain);
2370810d5adSTomeu Vizoso 
2380810d5adSTomeu Vizoso 	dma_fence_put(job->done_fence);
2390810d5adSTomeu Vizoso 	dma_fence_put(job->inference_done_fence);
2400810d5adSTomeu Vizoso 
2410810d5adSTomeu Vizoso 	if (job->in_bos) {
2420810d5adSTomeu Vizoso 		for (i = 0; i < job->in_bo_count; i++)
2430810d5adSTomeu Vizoso 			drm_gem_object_put(job->in_bos[i]);
2440810d5adSTomeu Vizoso 
2450810d5adSTomeu Vizoso 		kvfree(job->in_bos);
2460810d5adSTomeu Vizoso 	}
2470810d5adSTomeu Vizoso 
2480810d5adSTomeu Vizoso 	if (job->out_bos) {
2490810d5adSTomeu Vizoso 		for (i = 0; i < job->out_bo_count; i++)
2500810d5adSTomeu Vizoso 			drm_gem_object_put(job->out_bos[i]);
2510810d5adSTomeu Vizoso 
2520810d5adSTomeu Vizoso 		kvfree(job->out_bos);
2530810d5adSTomeu Vizoso 	}
2540810d5adSTomeu Vizoso 
2550810d5adSTomeu Vizoso 	kvfree(job->tasks);
2560810d5adSTomeu Vizoso 
2570810d5adSTomeu Vizoso 	kfree(job);
2580810d5adSTomeu Vizoso }
2590810d5adSTomeu Vizoso 
2600810d5adSTomeu Vizoso static void rocket_job_put(struct rocket_job *job)
2610810d5adSTomeu Vizoso {
2620810d5adSTomeu Vizoso 	kref_put(&job->refcount, rocket_job_cleanup);
2630810d5adSTomeu Vizoso }
2640810d5adSTomeu Vizoso 
2650810d5adSTomeu Vizoso static void rocket_job_free(struct drm_sched_job *sched_job)
2660810d5adSTomeu Vizoso {
2670810d5adSTomeu Vizoso 	struct rocket_job *job = to_rocket_job(sched_job);
2680810d5adSTomeu Vizoso 
2690810d5adSTomeu Vizoso 	drm_sched_job_cleanup(sched_job);
2700810d5adSTomeu Vizoso 
2710810d5adSTomeu Vizoso 	rocket_job_put(job);
2720810d5adSTomeu Vizoso }
2730810d5adSTomeu Vizoso 
2740810d5adSTomeu Vizoso static struct rocket_core *sched_to_core(struct rocket_device *rdev,
2750810d5adSTomeu Vizoso 					 struct drm_gpu_scheduler *sched)
2760810d5adSTomeu Vizoso {
2770810d5adSTomeu Vizoso 	unsigned int core;
2780810d5adSTomeu Vizoso 
2790810d5adSTomeu Vizoso 	for (core = 0; core < rdev->num_cores; core++) {
2800810d5adSTomeu Vizoso 		if (&rdev->cores[core].sched == sched)
2810810d5adSTomeu Vizoso 			return &rdev->cores[core];
2820810d5adSTomeu Vizoso 	}
2830810d5adSTomeu Vizoso 
2840810d5adSTomeu Vizoso 	return NULL;
2850810d5adSTomeu Vizoso }
2860810d5adSTomeu Vizoso 
2870810d5adSTomeu Vizoso static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
2880810d5adSTomeu Vizoso {
2890810d5adSTomeu Vizoso 	struct rocket_job *job = to_rocket_job(sched_job);
2900810d5adSTomeu Vizoso 	struct rocket_device *rdev = job->rdev;
2910810d5adSTomeu Vizoso 	struct rocket_core *core = sched_to_core(rdev, sched_job->sched);
2920810d5adSTomeu Vizoso 	struct dma_fence *fence = NULL;
2930810d5adSTomeu Vizoso 	int ret;
2940810d5adSTomeu Vizoso 
2950810d5adSTomeu Vizoso 	if (unlikely(job->base.s_fence->finished.error))
2960810d5adSTomeu Vizoso 		return NULL;
2970810d5adSTomeu Vizoso 
2980810d5adSTomeu Vizoso 	/*
2990810d5adSTomeu Vizoso 	 * Nothing to execute: can happen if the job has finished while
3000810d5adSTomeu Vizoso 	 * we were resetting the NPU.
3010810d5adSTomeu Vizoso 	 */
3020810d5adSTomeu Vizoso 	if (job->next_task_idx == job->task_count)
3030810d5adSTomeu Vizoso 		return NULL;
3040810d5adSTomeu Vizoso 
3050810d5adSTomeu Vizoso 	fence = rocket_fence_create(core);
3060810d5adSTomeu Vizoso 	if (IS_ERR(fence))
3070810d5adSTomeu Vizoso 		return fence;
3080810d5adSTomeu Vizoso 
3090810d5adSTomeu Vizoso 	if (job->done_fence)
3100810d5adSTomeu Vizoso 		dma_fence_put(job->done_fence);
3110810d5adSTomeu Vizoso 	job->done_fence = dma_fence_get(fence);
3120810d5adSTomeu Vizoso 
3130810d5adSTomeu Vizoso 	ret = pm_runtime_get_sync(core->dev);
3140810d5adSTomeu Vizoso 	if (ret < 0)
3150810d5adSTomeu Vizoso 		return fence;
3160810d5adSTomeu Vizoso 
3170810d5adSTomeu Vizoso 	ret = iommu_attach_group(job->domain->domain, core->iommu_group);
3180810d5adSTomeu Vizoso 	if (ret < 0)
3190810d5adSTomeu Vizoso 		return fence;
3200810d5adSTomeu Vizoso 
3210810d5adSTomeu Vizoso 	scoped_guard(mutex, &core->job_lock) {
3220810d5adSTomeu Vizoso 		core->in_flight_job = job;
3230810d5adSTomeu Vizoso 		rocket_job_hw_submit(core, job);
3240810d5adSTomeu Vizoso 	}
3250810d5adSTomeu Vizoso 
3260810d5adSTomeu Vizoso 	return fence;
3270810d5adSTomeu Vizoso }
3280810d5adSTomeu Vizoso 
3290810d5adSTomeu Vizoso static void rocket_job_handle_irq(struct rocket_core *core)
3300810d5adSTomeu Vizoso {
3310810d5adSTomeu Vizoso 	pm_runtime_mark_last_busy(core->dev);
3320810d5adSTomeu Vizoso 
3330810d5adSTomeu Vizoso 	rocket_pc_writel(core, OPERATION_ENABLE, 0x0);
3340810d5adSTomeu Vizoso 	rocket_pc_writel(core, INTERRUPT_CLEAR, 0x1ffff);
3350810d5adSTomeu Vizoso 
3360810d5adSTomeu Vizoso 	scoped_guard(mutex, &core->job_lock)
3370810d5adSTomeu Vizoso 		if (core->in_flight_job) {
3380810d5adSTomeu Vizoso 			if (core->in_flight_job->next_task_idx < core->in_flight_job->task_count) {
3390810d5adSTomeu Vizoso 				rocket_job_hw_submit(core, core->in_flight_job);
3400810d5adSTomeu Vizoso 				return;
3410810d5adSTomeu Vizoso 			}
3420810d5adSTomeu Vizoso 
3430810d5adSTomeu Vizoso 			iommu_detach_group(NULL, iommu_group_get(core->dev));
3440810d5adSTomeu Vizoso 			dma_fence_signal(core->in_flight_job->done_fence);
3450810d5adSTomeu Vizoso 			pm_runtime_put_autosuspend(core->dev);
3460810d5adSTomeu Vizoso 			core->in_flight_job = NULL;
3470810d5adSTomeu Vizoso 		}
3480810d5adSTomeu Vizoso }
3490810d5adSTomeu Vizoso 
3500810d5adSTomeu Vizoso static void
3510810d5adSTomeu Vizoso rocket_reset(struct rocket_core *core, struct drm_sched_job *bad)
3520810d5adSTomeu Vizoso {
3530810d5adSTomeu Vizoso 	if (!atomic_read(&core->reset.pending))
3540810d5adSTomeu Vizoso 		return;
3550810d5adSTomeu Vizoso 
3560810d5adSTomeu Vizoso 	drm_sched_stop(&core->sched, bad);
3570810d5adSTomeu Vizoso 
3580810d5adSTomeu Vizoso 	/*
3590810d5adSTomeu Vizoso 	 * Remaining interrupts have been handled, but we might still have
3600810d5adSTomeu Vizoso 	 * stuck jobs. Let's make sure the PM counters stay balanced by
3610810d5adSTomeu Vizoso 	 * manually calling pm_runtime_put_noidle().
3620810d5adSTomeu Vizoso 	 */
3630810d5adSTomeu Vizoso 	scoped_guard(mutex, &core->job_lock) {
3640810d5adSTomeu Vizoso 		if (core->in_flight_job)
3650810d5adSTomeu Vizoso 			pm_runtime_put_noidle(core->dev);
3660810d5adSTomeu Vizoso 
3670810d5adSTomeu Vizoso 		iommu_detach_group(NULL, core->iommu_group);
3680810d5adSTomeu Vizoso 
3690810d5adSTomeu Vizoso 		core->in_flight_job = NULL;
3700810d5adSTomeu Vizoso 	}
3710810d5adSTomeu Vizoso 
3720810d5adSTomeu Vizoso 	/* Proceed with reset now. */
3730810d5adSTomeu Vizoso 	rocket_core_reset(core);
3740810d5adSTomeu Vizoso 
3750810d5adSTomeu Vizoso 	/* NPU has been reset, we can clear the reset pending bit. */
3760810d5adSTomeu Vizoso 	atomic_set(&core->reset.pending, 0);
3770810d5adSTomeu Vizoso 
3780810d5adSTomeu Vizoso 	/* Restart the scheduler */
3790810d5adSTomeu Vizoso 	drm_sched_start(&core->sched, 0);
3800810d5adSTomeu Vizoso }
3810810d5adSTomeu Vizoso 
3820810d5adSTomeu Vizoso static enum drm_gpu_sched_stat rocket_job_timedout(struct drm_sched_job *sched_job)
3830810d5adSTomeu Vizoso {
3840810d5adSTomeu Vizoso 	struct rocket_job *job = to_rocket_job(sched_job);
3850810d5adSTomeu Vizoso 	struct rocket_device *rdev = job->rdev;
3860810d5adSTomeu Vizoso 	struct rocket_core *core = sched_to_core(rdev, sched_job->sched);
3870810d5adSTomeu Vizoso 
3880810d5adSTomeu Vizoso 	dev_err(core->dev, "NPU job timed out");
3890810d5adSTomeu Vizoso 
3900810d5adSTomeu Vizoso 	atomic_set(&core->reset.pending, 1);
3910810d5adSTomeu Vizoso 	rocket_reset(core, sched_job);
3920810d5adSTomeu Vizoso 
393218b15a3SBrigham Campbell 	return DRM_GPU_SCHED_STAT_RESET;
3940810d5adSTomeu Vizoso }
3950810d5adSTomeu Vizoso 
3960810d5adSTomeu Vizoso static void rocket_reset_work(struct work_struct *work)
3970810d5adSTomeu Vizoso {
3980810d5adSTomeu Vizoso 	struct rocket_core *core;
3990810d5adSTomeu Vizoso 
4000810d5adSTomeu Vizoso 	core = container_of(work, struct rocket_core, reset.work);
4010810d5adSTomeu Vizoso 	rocket_reset(core, NULL);
4020810d5adSTomeu Vizoso }
4030810d5adSTomeu Vizoso 
4040810d5adSTomeu Vizoso static const struct drm_sched_backend_ops rocket_sched_ops = {
4050810d5adSTomeu Vizoso 	.run_job = rocket_job_run,
4060810d5adSTomeu Vizoso 	.timedout_job = rocket_job_timedout,
4070810d5adSTomeu Vizoso 	.free_job = rocket_job_free
4080810d5adSTomeu Vizoso };
4090810d5adSTomeu Vizoso 
4100810d5adSTomeu Vizoso static irqreturn_t rocket_job_irq_handler_thread(int irq, void *data)
4110810d5adSTomeu Vizoso {
4120810d5adSTomeu Vizoso 	struct rocket_core *core = data;
4130810d5adSTomeu Vizoso 
4140810d5adSTomeu Vizoso 	rocket_job_handle_irq(core);
4150810d5adSTomeu Vizoso 
4160810d5adSTomeu Vizoso 	return IRQ_HANDLED;
4170810d5adSTomeu Vizoso }
4180810d5adSTomeu Vizoso 
4190810d5adSTomeu Vizoso static irqreturn_t rocket_job_irq_handler(int irq, void *data)
4200810d5adSTomeu Vizoso {
4210810d5adSTomeu Vizoso 	struct rocket_core *core = data;
4220810d5adSTomeu Vizoso 	u32 raw_status = rocket_pc_readl(core, INTERRUPT_RAW_STATUS);
4230810d5adSTomeu Vizoso 
4240810d5adSTomeu Vizoso 	WARN_ON(raw_status & PC_INTERRUPT_RAW_STATUS_DMA_READ_ERROR);
425*78e39995SHeiko Stuebner 	WARN_ON(raw_status & PC_INTERRUPT_RAW_STATUS_DMA_WRITE_ERROR);
4260810d5adSTomeu Vizoso 
4270810d5adSTomeu Vizoso 	if (!(raw_status & PC_INTERRUPT_RAW_STATUS_DPU_0 ||
4280810d5adSTomeu Vizoso 	      raw_status & PC_INTERRUPT_RAW_STATUS_DPU_1))
4290810d5adSTomeu Vizoso 		return IRQ_NONE;
4300810d5adSTomeu Vizoso 
4310810d5adSTomeu Vizoso 	rocket_pc_writel(core, INTERRUPT_MASK, 0x0);
4320810d5adSTomeu Vizoso 
4330810d5adSTomeu Vizoso 	return IRQ_WAKE_THREAD;
4340810d5adSTomeu Vizoso }
4350810d5adSTomeu Vizoso 
4360810d5adSTomeu Vizoso int rocket_job_init(struct rocket_core *core)
4370810d5adSTomeu Vizoso {
4380810d5adSTomeu Vizoso 	struct drm_sched_init_args args = {
4390810d5adSTomeu Vizoso 		.ops = &rocket_sched_ops,
4400810d5adSTomeu Vizoso 		.num_rqs = DRM_SCHED_PRIORITY_COUNT,
4410810d5adSTomeu Vizoso 		.credit_limit = 1,
4420810d5adSTomeu Vizoso 		.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
4430810d5adSTomeu Vizoso 		.name = dev_name(core->dev),
4440810d5adSTomeu Vizoso 		.dev = core->dev,
4450810d5adSTomeu Vizoso 	};
4460810d5adSTomeu Vizoso 	int ret;
4470810d5adSTomeu Vizoso 
4480810d5adSTomeu Vizoso 	INIT_WORK(&core->reset.work, rocket_reset_work);
4490810d5adSTomeu Vizoso 	spin_lock_init(&core->fence_lock);
4500810d5adSTomeu Vizoso 	mutex_init(&core->job_lock);
4510810d5adSTomeu Vizoso 
4520810d5adSTomeu Vizoso 	core->irq = platform_get_irq(to_platform_device(core->dev), 0);
4530810d5adSTomeu Vizoso 	if (core->irq < 0)
4540810d5adSTomeu Vizoso 		return core->irq;
4550810d5adSTomeu Vizoso 
4560810d5adSTomeu Vizoso 	ret = devm_request_threaded_irq(core->dev, core->irq,
4570810d5adSTomeu Vizoso 					rocket_job_irq_handler,
4580810d5adSTomeu Vizoso 					rocket_job_irq_handler_thread,
4590810d5adSTomeu Vizoso 					IRQF_SHARED, dev_name(core->dev),
4600810d5adSTomeu Vizoso 					core);
4610810d5adSTomeu Vizoso 	if (ret) {
4620810d5adSTomeu Vizoso 		dev_err(core->dev, "failed to request job irq");
4630810d5adSTomeu Vizoso 		return ret;
4640810d5adSTomeu Vizoso 	}
4650810d5adSTomeu Vizoso 
4660810d5adSTomeu Vizoso 	core->reset.wq = alloc_ordered_workqueue("rocket-reset-%d", 0, core->index);
4670810d5adSTomeu Vizoso 	if (!core->reset.wq)
4680810d5adSTomeu Vizoso 		return -ENOMEM;
4690810d5adSTomeu Vizoso 
4700810d5adSTomeu Vizoso 	core->fence_context = dma_fence_context_alloc(1);
4710810d5adSTomeu Vizoso 
4720810d5adSTomeu Vizoso 	args.timeout_wq = core->reset.wq;
4730810d5adSTomeu Vizoso 	ret = drm_sched_init(&core->sched, &args);
4740810d5adSTomeu Vizoso 	if (ret) {
4750810d5adSTomeu Vizoso 		dev_err(core->dev, "Failed to create scheduler: %d.", ret);
4760810d5adSTomeu Vizoso 		goto err_sched;
4770810d5adSTomeu Vizoso 	}
4780810d5adSTomeu Vizoso 
4790810d5adSTomeu Vizoso 	return 0;
4800810d5adSTomeu Vizoso 
4810810d5adSTomeu Vizoso err_sched:
4820810d5adSTomeu Vizoso 	drm_sched_fini(&core->sched);
4830810d5adSTomeu Vizoso 
4840810d5adSTomeu Vizoso 	destroy_workqueue(core->reset.wq);
4850810d5adSTomeu Vizoso 	return ret;
4860810d5adSTomeu Vizoso }
4870810d5adSTomeu Vizoso 
4880810d5adSTomeu Vizoso void rocket_job_fini(struct rocket_core *core)
4890810d5adSTomeu Vizoso {
4900810d5adSTomeu Vizoso 	drm_sched_fini(&core->sched);
4910810d5adSTomeu Vizoso 
4920810d5adSTomeu Vizoso 	cancel_work_sync(&core->reset.work);
4930810d5adSTomeu Vizoso 	destroy_workqueue(core->reset.wq);
4940810d5adSTomeu Vizoso }
4950810d5adSTomeu Vizoso 
4960810d5adSTomeu Vizoso int rocket_job_open(struct rocket_file_priv *rocket_priv)
4970810d5adSTomeu Vizoso {
4980810d5adSTomeu Vizoso 	struct rocket_device *rdev = rocket_priv->rdev;
499ce6b656bSBrigham Campbell 	struct drm_gpu_scheduler **scheds = kmalloc_array(rdev->num_cores,
500ce6b656bSBrigham Campbell 							  sizeof(*scheds),
5010810d5adSTomeu Vizoso 							  GFP_KERNEL);
5020810d5adSTomeu Vizoso 	unsigned int core;
5030810d5adSTomeu Vizoso 	int ret;
5040810d5adSTomeu Vizoso 
5050810d5adSTomeu Vizoso 	for (core = 0; core < rdev->num_cores; core++)
5060810d5adSTomeu Vizoso 		scheds[core] = &rdev->cores[core].sched;
5070810d5adSTomeu Vizoso 
5080810d5adSTomeu Vizoso 	ret = drm_sched_entity_init(&rocket_priv->sched_entity,
5090810d5adSTomeu Vizoso 				    DRM_SCHED_PRIORITY_NORMAL,
5100810d5adSTomeu Vizoso 				    scheds,
5110810d5adSTomeu Vizoso 				    rdev->num_cores, NULL);
5120810d5adSTomeu Vizoso 	if (WARN_ON(ret))
5130810d5adSTomeu Vizoso 		return ret;
5140810d5adSTomeu Vizoso 
5150810d5adSTomeu Vizoso 	return 0;
5160810d5adSTomeu Vizoso }
5170810d5adSTomeu Vizoso 
5180810d5adSTomeu Vizoso void rocket_job_close(struct rocket_file_priv *rocket_priv)
5190810d5adSTomeu Vizoso {
5200810d5adSTomeu Vizoso 	struct drm_sched_entity *entity = &rocket_priv->sched_entity;
5210810d5adSTomeu Vizoso 
5220810d5adSTomeu Vizoso 	kfree(entity->sched_list);
5230810d5adSTomeu Vizoso 	drm_sched_entity_destroy(entity);
5240810d5adSTomeu Vizoso }
5250810d5adSTomeu Vizoso 
5260810d5adSTomeu Vizoso int rocket_job_is_idle(struct rocket_core *core)
5270810d5adSTomeu Vizoso {
5280810d5adSTomeu Vizoso 	/* If there are any jobs in this HW queue, we're not idle */
5290810d5adSTomeu Vizoso 	if (atomic_read(&core->sched.credit_count))
5300810d5adSTomeu Vizoso 		return false;
5310810d5adSTomeu Vizoso 
5320810d5adSTomeu Vizoso 	return true;
5330810d5adSTomeu Vizoso }
5340810d5adSTomeu Vizoso 
5350810d5adSTomeu Vizoso static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file,
5360810d5adSTomeu Vizoso 				   struct drm_rocket_job *job)
5370810d5adSTomeu Vizoso {
5380810d5adSTomeu Vizoso 	struct rocket_device *rdev = to_rocket_device(dev);
5390810d5adSTomeu Vizoso 	struct rocket_file_priv *file_priv = file->driver_priv;
5400810d5adSTomeu Vizoso 	struct rocket_job *rjob = NULL;
5410810d5adSTomeu Vizoso 	int ret = 0;
5420810d5adSTomeu Vizoso 
5430810d5adSTomeu Vizoso 	if (job->task_count == 0)
5440810d5adSTomeu Vizoso 		return -EINVAL;
5450810d5adSTomeu Vizoso 
5460810d5adSTomeu Vizoso 	rjob = kzalloc(sizeof(*rjob), GFP_KERNEL);
5470810d5adSTomeu Vizoso 	if (!rjob)
5480810d5adSTomeu Vizoso 		return -ENOMEM;
5490810d5adSTomeu Vizoso 
5500810d5adSTomeu Vizoso 	kref_init(&rjob->refcount);
5510810d5adSTomeu Vizoso 
5520810d5adSTomeu Vizoso 	rjob->rdev = rdev;
5530810d5adSTomeu Vizoso 
5540810d5adSTomeu Vizoso 	ret = drm_sched_job_init(&rjob->base,
5550810d5adSTomeu Vizoso 				 &file_priv->sched_entity,
556218b15a3SBrigham Campbell 				 1, NULL, file->client_id);
5570810d5adSTomeu Vizoso 	if (ret)
5580810d5adSTomeu Vizoso 		goto out_put_job;
5590810d5adSTomeu Vizoso 
5600810d5adSTomeu Vizoso 	ret = rocket_copy_tasks(dev, file, job, rjob);
5610810d5adSTomeu Vizoso 	if (ret)
5620810d5adSTomeu Vizoso 		goto out_cleanup_job;
5630810d5adSTomeu Vizoso 
5640810d5adSTomeu Vizoso 	ret = drm_gem_objects_lookup(file, u64_to_user_ptr(job->in_bo_handles),
5650810d5adSTomeu Vizoso 				     job->in_bo_handle_count, &rjob->in_bos);
5660810d5adSTomeu Vizoso 	if (ret)
5670810d5adSTomeu Vizoso 		goto out_cleanup_job;
5680810d5adSTomeu Vizoso 
5690810d5adSTomeu Vizoso 	rjob->in_bo_count = job->in_bo_handle_count;
5700810d5adSTomeu Vizoso 
5710810d5adSTomeu Vizoso 	ret = drm_gem_objects_lookup(file, u64_to_user_ptr(job->out_bo_handles),
5720810d5adSTomeu Vizoso 				     job->out_bo_handle_count, &rjob->out_bos);
5730810d5adSTomeu Vizoso 	if (ret)
5740810d5adSTomeu Vizoso 		goto out_cleanup_job;
5750810d5adSTomeu Vizoso 
5760810d5adSTomeu Vizoso 	rjob->out_bo_count = job->out_bo_handle_count;
5770810d5adSTomeu Vizoso 
5780810d5adSTomeu Vizoso 	rjob->domain = rocket_iommu_domain_get(file_priv);
5790810d5adSTomeu Vizoso 
5800810d5adSTomeu Vizoso 	ret = rocket_job_push(rjob);
5810810d5adSTomeu Vizoso 	if (ret)
5820810d5adSTomeu Vizoso 		goto out_cleanup_job;
5830810d5adSTomeu Vizoso 
5840810d5adSTomeu Vizoso out_cleanup_job:
5850810d5adSTomeu Vizoso 	if (ret)
5860810d5adSTomeu Vizoso 		drm_sched_job_cleanup(&rjob->base);
5870810d5adSTomeu Vizoso out_put_job:
5880810d5adSTomeu Vizoso 	rocket_job_put(rjob);
5890810d5adSTomeu Vizoso 
5900810d5adSTomeu Vizoso 	return ret;
5910810d5adSTomeu Vizoso }
5920810d5adSTomeu Vizoso 
5930810d5adSTomeu Vizoso int rocket_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file)
5940810d5adSTomeu Vizoso {
5950810d5adSTomeu Vizoso 	struct drm_rocket_submit *args = data;
5960810d5adSTomeu Vizoso 	struct drm_rocket_job *jobs;
5970810d5adSTomeu Vizoso 	int ret = 0;
5980810d5adSTomeu Vizoso 	unsigned int i = 0;
5990810d5adSTomeu Vizoso 
6000810d5adSTomeu Vizoso 	if (args->job_count == 0)
6010810d5adSTomeu Vizoso 		return 0;
6020810d5adSTomeu Vizoso 
6030810d5adSTomeu Vizoso 	if (args->job_struct_size < sizeof(struct drm_rocket_job)) {
6040810d5adSTomeu Vizoso 		drm_dbg(dev, "job_struct_size field in drm_rocket_submit struct is too small.\n");
6050810d5adSTomeu Vizoso 		return -EINVAL;
6060810d5adSTomeu Vizoso 	}
6070810d5adSTomeu Vizoso 
6080810d5adSTomeu Vizoso 	if (args->reserved != 0) {
6090810d5adSTomeu Vizoso 		drm_dbg(dev, "Reserved field in drm_rocket_submit struct should be 0.\n");
6100810d5adSTomeu Vizoso 		return -EINVAL;
6110810d5adSTomeu Vizoso 	}
6120810d5adSTomeu Vizoso 
6130810d5adSTomeu Vizoso 	jobs = kvmalloc_array(args->job_count, sizeof(*jobs), GFP_KERNEL);
6140810d5adSTomeu Vizoso 	if (!jobs) {
6150810d5adSTomeu Vizoso 		drm_dbg(dev, "Failed to allocate incoming job array\n");
6160810d5adSTomeu Vizoso 		return -ENOMEM;
6170810d5adSTomeu Vizoso 	}
6180810d5adSTomeu Vizoso 
6190810d5adSTomeu Vizoso 	for (i = 0; i < args->job_count; i++) {
6200810d5adSTomeu Vizoso 		if (copy_from_user(&jobs[i],
6210810d5adSTomeu Vizoso 				   u64_to_user_ptr(args->jobs) + i * args->job_struct_size,
6220810d5adSTomeu Vizoso 				   sizeof(*jobs))) {
6230810d5adSTomeu Vizoso 			ret = -EFAULT;
6240810d5adSTomeu Vizoso 			drm_dbg(dev, "Failed to copy incoming job array\n");
6250810d5adSTomeu Vizoso 			goto exit;
6260810d5adSTomeu Vizoso 		}
6270810d5adSTomeu Vizoso 	}
6280810d5adSTomeu Vizoso 
6290810d5adSTomeu Vizoso 
6300810d5adSTomeu Vizoso 	for (i = 0; i < args->job_count; i++)
6310810d5adSTomeu Vizoso 		rocket_ioctl_submit_job(dev, file, &jobs[i]);
6320810d5adSTomeu Vizoso 
6330810d5adSTomeu Vizoso exit:
634ce6b656bSBrigham Campbell 	kvfree(jobs);
6350810d5adSTomeu Vizoso 
6360810d5adSTomeu Vizoso 	return ret;
6370810d5adSTomeu Vizoso }
638