18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright © 2014 Broadcom 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 138c2ecf20Sopenharmony_ci * Software. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 218c2ecf20Sopenharmony_ci * IN THE SOFTWARE. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/** 258c2ecf20Sopenharmony_ci * DOC: Interrupt management for the V3D engine 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * We have an interrupt status register (V3D_INTCTL) which reports 288c2ecf20Sopenharmony_ci * interrupts, and where writing 1 bits clears those interrupts. 298c2ecf20Sopenharmony_ci * There are also a pair of interrupt registers 308c2ecf20Sopenharmony_ci * (V3D_INTENA/V3D_INTDIS) where writing a 1 to their bits enables or 318c2ecf20Sopenharmony_ci * disables that specific interrupt, and 0s written are ignored 328c2ecf20Sopenharmony_ci * (reading either one returns the set of enabled interrupts). 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * When we take a binning flush done interrupt, we need to submit the 358c2ecf20Sopenharmony_ci * next frame for binning and move the finished frame to the render 368c2ecf20Sopenharmony_ci * thread. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * When we take a render frame interrupt, we need to wake the 398c2ecf20Sopenharmony_ci * processes waiting for some frame to be done, and get the next frame 408c2ecf20Sopenharmony_ci * submitted ASAP (so the hardware doesn't sit idle when there's work 418c2ecf20Sopenharmony_ci * to do). 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * When we take the binner out of memory interrupt, we need to 448c2ecf20Sopenharmony_ci * allocate some new memory and pass it to the binner so that the 458c2ecf20Sopenharmony_ci * current job can make progress. 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include "vc4_drv.h" 498c2ecf20Sopenharmony_ci#include "vc4_regs.h" 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define V3D_DRIVER_IRQS (V3D_INT_OUTOMEM | \ 528c2ecf20Sopenharmony_ci V3D_INT_FLDONE | \ 538c2ecf20Sopenharmony_ci V3D_INT_FRDONE) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciDECLARE_WAIT_QUEUE_HEAD(render_wait); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void 588c2ecf20Sopenharmony_civc4_overflow_mem_work(struct work_struct *work) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = 618c2ecf20Sopenharmony_ci container_of(work, struct vc4_dev, overflow_mem_work); 628c2ecf20Sopenharmony_ci struct vc4_bo *bo; 638c2ecf20Sopenharmony_ci int bin_bo_slot; 648c2ecf20Sopenharmony_ci struct vc4_exec_info *exec; 658c2ecf20Sopenharmony_ci unsigned long irqflags; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci mutex_lock(&vc4->bin_bo_lock); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!vc4->bin_bo) 708c2ecf20Sopenharmony_ci goto complete; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci bo = vc4->bin_bo; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci bin_bo_slot = vc4_v3d_get_bin_slot(vc4); 758c2ecf20Sopenharmony_ci if (bin_bo_slot < 0) { 768c2ecf20Sopenharmony_ci DRM_ERROR("Couldn't allocate binner overflow mem\n"); 778c2ecf20Sopenharmony_ci goto complete; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci spin_lock_irqsave(&vc4->job_lock, irqflags); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (vc4->bin_alloc_overflow) { 838c2ecf20Sopenharmony_ci /* If we had overflow memory allocated previously, 848c2ecf20Sopenharmony_ci * then that chunk will free when the current bin job 858c2ecf20Sopenharmony_ci * is done. If we don't have a bin job running, then 868c2ecf20Sopenharmony_ci * the chunk will be done whenever the list of render 878c2ecf20Sopenharmony_ci * jobs has drained. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci exec = vc4_first_bin_job(vc4); 908c2ecf20Sopenharmony_ci if (!exec) 918c2ecf20Sopenharmony_ci exec = vc4_last_render_job(vc4); 928c2ecf20Sopenharmony_ci if (exec) { 938c2ecf20Sopenharmony_ci exec->bin_slots |= vc4->bin_alloc_overflow; 948c2ecf20Sopenharmony_ci } else { 958c2ecf20Sopenharmony_ci /* There's nothing queued in the hardware, so 968c2ecf20Sopenharmony_ci * the old slot is free immediately. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci vc4->bin_alloc_used &= ~vc4->bin_alloc_overflow; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci vc4->bin_alloc_overflow = BIT(bin_bo_slot); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci V3D_WRITE(V3D_BPOA, bo->base.paddr + bin_bo_slot * vc4->bin_alloc_size); 1048c2ecf20Sopenharmony_ci V3D_WRITE(V3D_BPOS, bo->base.base.size); 1058c2ecf20Sopenharmony_ci V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); 1068c2ecf20Sopenharmony_ci V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); 1078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc4->job_lock, irqflags); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cicomplete: 1108c2ecf20Sopenharmony_ci mutex_unlock(&vc4->bin_bo_lock); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void 1148c2ecf20Sopenharmony_civc4_irq_finish_bin_job(struct drm_device *dev) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 1178c2ecf20Sopenharmony_ci struct vc4_exec_info *next, *exec = vc4_first_bin_job(vc4); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (!exec) 1208c2ecf20Sopenharmony_ci return; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci vc4_move_job_to_render(dev, exec); 1238c2ecf20Sopenharmony_ci next = vc4_first_bin_job(vc4); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Only submit the next job in the bin list if it matches the perfmon 1268c2ecf20Sopenharmony_ci * attached to the one that just finished (or if both jobs don't have 1278c2ecf20Sopenharmony_ci * perfmon attached to them). 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci if (next && next->perfmon == exec->perfmon) 1308c2ecf20Sopenharmony_ci vc4_submit_next_bin_job(dev); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void 1348c2ecf20Sopenharmony_civc4_cancel_bin_job(struct drm_device *dev) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 1378c2ecf20Sopenharmony_ci struct vc4_exec_info *exec = vc4_first_bin_job(vc4); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!exec) 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Stop the perfmon so that the next bin job can be started. */ 1438c2ecf20Sopenharmony_ci if (exec->perfmon) 1448c2ecf20Sopenharmony_ci vc4_perfmon_stop(vc4, exec->perfmon, false); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci list_move_tail(&exec->head, &vc4->bin_job_list); 1478c2ecf20Sopenharmony_ci vc4_submit_next_bin_job(dev); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void 1518c2ecf20Sopenharmony_civc4_irq_finish_render_job(struct drm_device *dev) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 1548c2ecf20Sopenharmony_ci struct vc4_exec_info *exec = vc4_first_render_job(vc4); 1558c2ecf20Sopenharmony_ci struct vc4_exec_info *nextbin, *nextrender; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!exec) 1588c2ecf20Sopenharmony_ci return; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci vc4->finished_seqno++; 1618c2ecf20Sopenharmony_ci list_move_tail(&exec->head, &vc4->job_done_list); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci nextbin = vc4_first_bin_job(vc4); 1648c2ecf20Sopenharmony_ci nextrender = vc4_first_render_job(vc4); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Only stop the perfmon if following jobs in the queue don't expect it 1678c2ecf20Sopenharmony_ci * to be enabled. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci if (exec->perfmon && !nextrender && 1708c2ecf20Sopenharmony_ci (!nextbin || nextbin->perfmon != exec->perfmon)) 1718c2ecf20Sopenharmony_ci vc4_perfmon_stop(vc4, exec->perfmon, true); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* If there's a render job waiting, start it. If this is not the case 1748c2ecf20Sopenharmony_ci * we may have to unblock the binner if it's been stalled because of 1758c2ecf20Sopenharmony_ci * perfmon (this can be checked by comparing the perfmon attached to 1768c2ecf20Sopenharmony_ci * the finished renderjob to the one attached to the next bin job: if 1778c2ecf20Sopenharmony_ci * they don't match, this means the binner is stalled and should be 1788c2ecf20Sopenharmony_ci * restarted). 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci if (nextrender) 1818c2ecf20Sopenharmony_ci vc4_submit_next_render_job(dev); 1828c2ecf20Sopenharmony_ci else if (nextbin && nextbin->perfmon != exec->perfmon) 1838c2ecf20Sopenharmony_ci vc4_submit_next_bin_job(dev); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (exec->fence) { 1868c2ecf20Sopenharmony_ci dma_fence_signal_locked(exec->fence); 1878c2ecf20Sopenharmony_ci dma_fence_put(exec->fence); 1888c2ecf20Sopenharmony_ci exec->fence = NULL; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci wake_up_all(&vc4->job_wait_queue); 1928c2ecf20Sopenharmony_ci schedule_work(&vc4->job_done_work); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ciirqreturn_t 1968c2ecf20Sopenharmony_civc4_irq(int irq, void *arg) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct drm_device *dev = arg; 1998c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 2008c2ecf20Sopenharmony_ci uint32_t intctl; 2018c2ecf20Sopenharmony_ci irqreturn_t status = IRQ_NONE; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci barrier(); 2048c2ecf20Sopenharmony_ci intctl = V3D_READ(V3D_INTCTL); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Acknowledge the interrupts we're handling here. The binner 2078c2ecf20Sopenharmony_ci * last flush / render frame done interrupt will be cleared, 2088c2ecf20Sopenharmony_ci * while OUTOMEM will stay high until the underlying cause is 2098c2ecf20Sopenharmony_ci * cleared. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci V3D_WRITE(V3D_INTCTL, intctl); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (intctl & V3D_INT_OUTOMEM) { 2148c2ecf20Sopenharmony_ci /* Disable OUTOMEM until the work is done. */ 2158c2ecf20Sopenharmony_ci V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM); 2168c2ecf20Sopenharmony_ci schedule_work(&vc4->overflow_mem_work); 2178c2ecf20Sopenharmony_ci status = IRQ_HANDLED; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (intctl & V3D_INT_FLDONE) { 2218c2ecf20Sopenharmony_ci spin_lock(&vc4->job_lock); 2228c2ecf20Sopenharmony_ci vc4_irq_finish_bin_job(dev); 2238c2ecf20Sopenharmony_ci spin_unlock(&vc4->job_lock); 2248c2ecf20Sopenharmony_ci status = IRQ_HANDLED; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (intctl & V3D_INT_FRDONE) { 2288c2ecf20Sopenharmony_ci spin_lock(&vc4->job_lock); 2298c2ecf20Sopenharmony_ci vc4_irq_finish_render_job(dev); 2308c2ecf20Sopenharmony_ci spin_unlock(&vc4->job_lock); 2318c2ecf20Sopenharmony_ci status = IRQ_HANDLED; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return status; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_civoid 2388c2ecf20Sopenharmony_civc4_irq_preinstall(struct drm_device *dev) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (!vc4->v3d) 2438c2ecf20Sopenharmony_ci return; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci init_waitqueue_head(&vc4->job_wait_queue); 2468c2ecf20Sopenharmony_ci INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Clear any pending interrupts someone might have left around 2498c2ecf20Sopenharmony_ci * for us. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ciint 2558c2ecf20Sopenharmony_civc4_irq_postinstall(struct drm_device *dev) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!vc4->v3d) 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Enable the render done interrupts. The out-of-memory interrupt is 2638c2ecf20Sopenharmony_ci * enabled as soon as we have a binner BO allocated. 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci V3D_WRITE(V3D_INTENA, V3D_INT_FLDONE | V3D_INT_FRDONE); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_civoid 2718c2ecf20Sopenharmony_civc4_irq_uninstall(struct drm_device *dev) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!vc4->v3d) 2768c2ecf20Sopenharmony_ci return; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Disable sending interrupts for our driver's IRQs. */ 2798c2ecf20Sopenharmony_ci V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Clear any pending interrupts we might have left. */ 2828c2ecf20Sopenharmony_ci V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Finish any interrupt handler still in flight. */ 2858c2ecf20Sopenharmony_ci disable_irq(dev->irq); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci cancel_work_sync(&vc4->overflow_mem_work); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/** Reinitializes interrupt registers when a GPU reset is performed. */ 2918c2ecf20Sopenharmony_civoid vc4_irq_reset(struct drm_device *dev) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 2948c2ecf20Sopenharmony_ci unsigned long irqflags; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Acknowledge any stale IRQs. */ 2978c2ecf20Sopenharmony_ci V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* 3008c2ecf20Sopenharmony_ci * Turn all our interrupts on. Binner out of memory is the 3018c2ecf20Sopenharmony_ci * only one we expect to trigger at this point, since we've 3028c2ecf20Sopenharmony_ci * just come from poweron and haven't supplied any overflow 3038c2ecf20Sopenharmony_ci * memory yet. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci spin_lock_irqsave(&vc4->job_lock, irqflags); 3088c2ecf20Sopenharmony_ci vc4_cancel_bin_job(dev); 3098c2ecf20Sopenharmony_ci vc4_irq_finish_render_job(dev); 3108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc4->job_lock, irqflags); 3118c2ecf20Sopenharmony_ci} 312