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