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