162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright © 2014 Broadcom
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next
1262306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
1362306a36Sopenharmony_ci * Software.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1862306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1962306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2062306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2162306a36Sopenharmony_ci * IN THE SOFTWARE.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * DOC: Interrupt management for the V3D engine
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * We have an interrupt status register (V3D_INTCTL) which reports
2862306a36Sopenharmony_ci * interrupts, and where writing 1 bits clears those interrupts.
2962306a36Sopenharmony_ci * There are also a pair of interrupt registers
3062306a36Sopenharmony_ci * (V3D_INTENA/V3D_INTDIS) where writing a 1 to their bits enables or
3162306a36Sopenharmony_ci * disables that specific interrupt, and 0s written are ignored
3262306a36Sopenharmony_ci * (reading either one returns the set of enabled interrupts).
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * When we take a binning flush done interrupt, we need to submit the
3562306a36Sopenharmony_ci * next frame for binning and move the finished frame to the render
3662306a36Sopenharmony_ci * thread.
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci * When we take a render frame interrupt, we need to wake the
3962306a36Sopenharmony_ci * processes waiting for some frame to be done, and get the next frame
4062306a36Sopenharmony_ci * submitted ASAP (so the hardware doesn't sit idle when there's work
4162306a36Sopenharmony_ci * to do).
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * When we take the binner out of memory interrupt, we need to
4462306a36Sopenharmony_ci * allocate some new memory and pass it to the binner so that the
4562306a36Sopenharmony_ci * current job can make progress.
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#include <linux/platform_device.h>
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#include <drm/drm_drv.h>
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#include "vc4_drv.h"
5362306a36Sopenharmony_ci#include "vc4_regs.h"
5462306a36Sopenharmony_ci#include "vc4_trace.h"
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define V3D_DRIVER_IRQS (V3D_INT_OUTOMEM | \
5762306a36Sopenharmony_ci			 V3D_INT_FLDONE | \
5862306a36Sopenharmony_ci			 V3D_INT_FRDONE)
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void
6162306a36Sopenharmony_civc4_overflow_mem_work(struct work_struct *work)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct vc4_dev *vc4 =
6462306a36Sopenharmony_ci		container_of(work, struct vc4_dev, overflow_mem_work);
6562306a36Sopenharmony_ci	struct vc4_bo *bo;
6662306a36Sopenharmony_ci	int bin_bo_slot;
6762306a36Sopenharmony_ci	struct vc4_exec_info *exec;
6862306a36Sopenharmony_ci	unsigned long irqflags;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	mutex_lock(&vc4->bin_bo_lock);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (!vc4->bin_bo)
7362306a36Sopenharmony_ci		goto complete;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	bo = vc4->bin_bo;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	bin_bo_slot = vc4_v3d_get_bin_slot(vc4);
7862306a36Sopenharmony_ci	if (bin_bo_slot < 0) {
7962306a36Sopenharmony_ci		DRM_ERROR("Couldn't allocate binner overflow mem\n");
8062306a36Sopenharmony_ci		goto complete;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (vc4->bin_alloc_overflow) {
8662306a36Sopenharmony_ci		/* If we had overflow memory allocated previously,
8762306a36Sopenharmony_ci		 * then that chunk will free when the current bin job
8862306a36Sopenharmony_ci		 * is done.  If we don't have a bin job running, then
8962306a36Sopenharmony_ci		 * the chunk will be done whenever the list of render
9062306a36Sopenharmony_ci		 * jobs has drained.
9162306a36Sopenharmony_ci		 */
9262306a36Sopenharmony_ci		exec = vc4_first_bin_job(vc4);
9362306a36Sopenharmony_ci		if (!exec)
9462306a36Sopenharmony_ci			exec = vc4_last_render_job(vc4);
9562306a36Sopenharmony_ci		if (exec) {
9662306a36Sopenharmony_ci			exec->bin_slots |= vc4->bin_alloc_overflow;
9762306a36Sopenharmony_ci		} else {
9862306a36Sopenharmony_ci			/* There's nothing queued in the hardware, so
9962306a36Sopenharmony_ci			 * the old slot is free immediately.
10062306a36Sopenharmony_ci			 */
10162306a36Sopenharmony_ci			vc4->bin_alloc_used &= ~vc4->bin_alloc_overflow;
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	vc4->bin_alloc_overflow = BIT(bin_bo_slot);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	V3D_WRITE(V3D_BPOA, bo->base.dma_addr + bin_bo_slot * vc4->bin_alloc_size);
10762306a36Sopenharmony_ci	V3D_WRITE(V3D_BPOS, bo->base.base.size);
10862306a36Sopenharmony_ci	V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
10962306a36Sopenharmony_ci	V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
11062306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cicomplete:
11362306a36Sopenharmony_ci	mutex_unlock(&vc4->bin_bo_lock);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void
11762306a36Sopenharmony_civc4_irq_finish_bin_job(struct drm_device *dev)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
12062306a36Sopenharmony_ci	struct vc4_exec_info *next, *exec = vc4_first_bin_job(vc4);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (!exec)
12362306a36Sopenharmony_ci		return;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	trace_vc4_bcl_end_irq(dev, exec->seqno);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	vc4_move_job_to_render(dev, exec);
12862306a36Sopenharmony_ci	next = vc4_first_bin_job(vc4);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* Only submit the next job in the bin list if it matches the perfmon
13162306a36Sopenharmony_ci	 * attached to the one that just finished (or if both jobs don't have
13262306a36Sopenharmony_ci	 * perfmon attached to them).
13362306a36Sopenharmony_ci	 */
13462306a36Sopenharmony_ci	if (next && next->perfmon == exec->perfmon)
13562306a36Sopenharmony_ci		vc4_submit_next_bin_job(dev);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void
13962306a36Sopenharmony_civc4_cancel_bin_job(struct drm_device *dev)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
14262306a36Sopenharmony_ci	struct vc4_exec_info *exec = vc4_first_bin_job(vc4);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (!exec)
14562306a36Sopenharmony_ci		return;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* Stop the perfmon so that the next bin job can be started. */
14862306a36Sopenharmony_ci	if (exec->perfmon)
14962306a36Sopenharmony_ci		vc4_perfmon_stop(vc4, exec->perfmon, false);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	list_move_tail(&exec->head, &vc4->bin_job_list);
15262306a36Sopenharmony_ci	vc4_submit_next_bin_job(dev);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic void
15662306a36Sopenharmony_civc4_irq_finish_render_job(struct drm_device *dev)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
15962306a36Sopenharmony_ci	struct vc4_exec_info *exec = vc4_first_render_job(vc4);
16062306a36Sopenharmony_ci	struct vc4_exec_info *nextbin, *nextrender;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (!exec)
16362306a36Sopenharmony_ci		return;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	trace_vc4_rcl_end_irq(dev, exec->seqno);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	vc4->finished_seqno++;
16862306a36Sopenharmony_ci	list_move_tail(&exec->head, &vc4->job_done_list);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	nextbin = vc4_first_bin_job(vc4);
17162306a36Sopenharmony_ci	nextrender = vc4_first_render_job(vc4);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* Only stop the perfmon if following jobs in the queue don't expect it
17462306a36Sopenharmony_ci	 * to be enabled.
17562306a36Sopenharmony_ci	 */
17662306a36Sopenharmony_ci	if (exec->perfmon && !nextrender &&
17762306a36Sopenharmony_ci	    (!nextbin || nextbin->perfmon != exec->perfmon))
17862306a36Sopenharmony_ci		vc4_perfmon_stop(vc4, exec->perfmon, true);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* If there's a render job waiting, start it. If this is not the case
18162306a36Sopenharmony_ci	 * we may have to unblock the binner if it's been stalled because of
18262306a36Sopenharmony_ci	 * perfmon (this can be checked by comparing the perfmon attached to
18362306a36Sopenharmony_ci	 * the finished renderjob to the one attached to the next bin job: if
18462306a36Sopenharmony_ci	 * they don't match, this means the binner is stalled and should be
18562306a36Sopenharmony_ci	 * restarted).
18662306a36Sopenharmony_ci	 */
18762306a36Sopenharmony_ci	if (nextrender)
18862306a36Sopenharmony_ci		vc4_submit_next_render_job(dev);
18962306a36Sopenharmony_ci	else if (nextbin && nextbin->perfmon != exec->perfmon)
19062306a36Sopenharmony_ci		vc4_submit_next_bin_job(dev);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (exec->fence) {
19362306a36Sopenharmony_ci		dma_fence_signal_locked(exec->fence);
19462306a36Sopenharmony_ci		dma_fence_put(exec->fence);
19562306a36Sopenharmony_ci		exec->fence = NULL;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	wake_up_all(&vc4->job_wait_queue);
19962306a36Sopenharmony_ci	schedule_work(&vc4->job_done_work);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic irqreturn_t
20362306a36Sopenharmony_civc4_irq(int irq, void *arg)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct drm_device *dev = arg;
20662306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
20762306a36Sopenharmony_ci	uint32_t intctl;
20862306a36Sopenharmony_ci	irqreturn_t status = IRQ_NONE;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	barrier();
21162306a36Sopenharmony_ci	intctl = V3D_READ(V3D_INTCTL);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* Acknowledge the interrupts we're handling here. The binner
21462306a36Sopenharmony_ci	 * last flush / render frame done interrupt will be cleared,
21562306a36Sopenharmony_ci	 * while OUTOMEM will stay high until the underlying cause is
21662306a36Sopenharmony_ci	 * cleared.
21762306a36Sopenharmony_ci	 */
21862306a36Sopenharmony_ci	V3D_WRITE(V3D_INTCTL, intctl);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (intctl & V3D_INT_OUTOMEM) {
22162306a36Sopenharmony_ci		/* Disable OUTOMEM until the work is done. */
22262306a36Sopenharmony_ci		V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM);
22362306a36Sopenharmony_ci		schedule_work(&vc4->overflow_mem_work);
22462306a36Sopenharmony_ci		status = IRQ_HANDLED;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (intctl & V3D_INT_FLDONE) {
22862306a36Sopenharmony_ci		spin_lock(&vc4->job_lock);
22962306a36Sopenharmony_ci		vc4_irq_finish_bin_job(dev);
23062306a36Sopenharmony_ci		spin_unlock(&vc4->job_lock);
23162306a36Sopenharmony_ci		status = IRQ_HANDLED;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (intctl & V3D_INT_FRDONE) {
23562306a36Sopenharmony_ci		spin_lock(&vc4->job_lock);
23662306a36Sopenharmony_ci		vc4_irq_finish_render_job(dev);
23762306a36Sopenharmony_ci		spin_unlock(&vc4->job_lock);
23862306a36Sopenharmony_ci		status = IRQ_HANDLED;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	return status;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void
24562306a36Sopenharmony_civc4_irq_prepare(struct drm_device *dev)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (!vc4->v3d)
25062306a36Sopenharmony_ci		return;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	init_waitqueue_head(&vc4->job_wait_queue);
25362306a36Sopenharmony_ci	INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/* Clear any pending interrupts someone might have left around
25662306a36Sopenharmony_ci	 * for us.
25762306a36Sopenharmony_ci	 */
25862306a36Sopenharmony_ci	V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_civoid
26262306a36Sopenharmony_civc4_irq_enable(struct drm_device *dev)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
26762306a36Sopenharmony_ci		return;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (!vc4->v3d)
27062306a36Sopenharmony_ci		return;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* Enable the render done interrupts. The out-of-memory interrupt is
27362306a36Sopenharmony_ci	 * enabled as soon as we have a binner BO allocated.
27462306a36Sopenharmony_ci	 */
27562306a36Sopenharmony_ci	V3D_WRITE(V3D_INTENA, V3D_INT_FLDONE | V3D_INT_FRDONE);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_civoid
27962306a36Sopenharmony_civc4_irq_disable(struct drm_device *dev)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
28462306a36Sopenharmony_ci		return;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!vc4->v3d)
28762306a36Sopenharmony_ci		return;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Disable sending interrupts for our driver's IRQs. */
29062306a36Sopenharmony_ci	V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Clear any pending interrupts we might have left. */
29362306a36Sopenharmony_ci	V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* Finish any interrupt handler still in flight. */
29662306a36Sopenharmony_ci	synchronize_irq(vc4->irq);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	cancel_work_sync(&vc4->overflow_mem_work);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ciint vc4_irq_install(struct drm_device *dev, int irq)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
30462306a36Sopenharmony_ci	int ret;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
30762306a36Sopenharmony_ci		return -ENODEV;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (irq == IRQ_NOTCONNECTED)
31062306a36Sopenharmony_ci		return -ENOTCONN;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	vc4_irq_prepare(dev);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = request_irq(irq, vc4_irq, 0, dev->driver->name, dev);
31562306a36Sopenharmony_ci	if (ret)
31662306a36Sopenharmony_ci		return ret;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	vc4_irq_enable(dev);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return 0;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_civoid vc4_irq_uninstall(struct drm_device *dev)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
32862306a36Sopenharmony_ci		return;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	vc4_irq_disable(dev);
33162306a36Sopenharmony_ci	free_irq(vc4->irq, dev);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/** Reinitializes interrupt registers when a GPU reset is performed. */
33562306a36Sopenharmony_civoid vc4_irq_reset(struct drm_device *dev)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
33862306a36Sopenharmony_ci	unsigned long irqflags;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
34162306a36Sopenharmony_ci		return;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* Acknowledge any stale IRQs. */
34462306a36Sopenharmony_ci	V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/*
34762306a36Sopenharmony_ci	 * Turn all our interrupts on.  Binner out of memory is the
34862306a36Sopenharmony_ci	 * only one we expect to trigger at this point, since we've
34962306a36Sopenharmony_ci	 * just come from poweron and haven't supplied any overflow
35062306a36Sopenharmony_ci	 * memory yet.
35162306a36Sopenharmony_ci	 */
35262306a36Sopenharmony_ci	V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
35562306a36Sopenharmony_ci	vc4_cancel_bin_job(dev);
35662306a36Sopenharmony_ci	vc4_irq_finish_render_job(dev);
35762306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
35862306a36Sopenharmony_ci}
359