162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2014 The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat
562306a36Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/component.h>
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "vc4_drv.h"
1462306a36Sopenharmony_ci#include "vc4_regs.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic const struct debugfs_reg32 v3d_regs[] = {
1762306a36Sopenharmony_ci	VC4_REG32(V3D_IDENT0),
1862306a36Sopenharmony_ci	VC4_REG32(V3D_IDENT1),
1962306a36Sopenharmony_ci	VC4_REG32(V3D_IDENT2),
2062306a36Sopenharmony_ci	VC4_REG32(V3D_SCRATCH),
2162306a36Sopenharmony_ci	VC4_REG32(V3D_L2CACTL),
2262306a36Sopenharmony_ci	VC4_REG32(V3D_SLCACTL),
2362306a36Sopenharmony_ci	VC4_REG32(V3D_INTCTL),
2462306a36Sopenharmony_ci	VC4_REG32(V3D_INTENA),
2562306a36Sopenharmony_ci	VC4_REG32(V3D_INTDIS),
2662306a36Sopenharmony_ci	VC4_REG32(V3D_CT0CS),
2762306a36Sopenharmony_ci	VC4_REG32(V3D_CT1CS),
2862306a36Sopenharmony_ci	VC4_REG32(V3D_CT0EA),
2962306a36Sopenharmony_ci	VC4_REG32(V3D_CT1EA),
3062306a36Sopenharmony_ci	VC4_REG32(V3D_CT0CA),
3162306a36Sopenharmony_ci	VC4_REG32(V3D_CT1CA),
3262306a36Sopenharmony_ci	VC4_REG32(V3D_CT00RA0),
3362306a36Sopenharmony_ci	VC4_REG32(V3D_CT01RA0),
3462306a36Sopenharmony_ci	VC4_REG32(V3D_CT0LC),
3562306a36Sopenharmony_ci	VC4_REG32(V3D_CT1LC),
3662306a36Sopenharmony_ci	VC4_REG32(V3D_CT0PC),
3762306a36Sopenharmony_ci	VC4_REG32(V3D_CT1PC),
3862306a36Sopenharmony_ci	VC4_REG32(V3D_PCS),
3962306a36Sopenharmony_ci	VC4_REG32(V3D_BFC),
4062306a36Sopenharmony_ci	VC4_REG32(V3D_RFC),
4162306a36Sopenharmony_ci	VC4_REG32(V3D_BPCA),
4262306a36Sopenharmony_ci	VC4_REG32(V3D_BPCS),
4362306a36Sopenharmony_ci	VC4_REG32(V3D_BPOA),
4462306a36Sopenharmony_ci	VC4_REG32(V3D_BPOS),
4562306a36Sopenharmony_ci	VC4_REG32(V3D_BXCF),
4662306a36Sopenharmony_ci	VC4_REG32(V3D_SQRSV0),
4762306a36Sopenharmony_ci	VC4_REG32(V3D_SQRSV1),
4862306a36Sopenharmony_ci	VC4_REG32(V3D_SQCNTL),
4962306a36Sopenharmony_ci	VC4_REG32(V3D_SRQPC),
5062306a36Sopenharmony_ci	VC4_REG32(V3D_SRQUA),
5162306a36Sopenharmony_ci	VC4_REG32(V3D_SRQUL),
5262306a36Sopenharmony_ci	VC4_REG32(V3D_SRQCS),
5362306a36Sopenharmony_ci	VC4_REG32(V3D_VPACNTL),
5462306a36Sopenharmony_ci	VC4_REG32(V3D_VPMBASE),
5562306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRC),
5662306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRE),
5762306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(0)),
5862306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(0)),
5962306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(1)),
6062306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(1)),
6162306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(2)),
6262306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(2)),
6362306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(3)),
6462306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(3)),
6562306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(4)),
6662306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(4)),
6762306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(5)),
6862306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(5)),
6962306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(6)),
7062306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(6)),
7162306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(7)),
7262306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(7)),
7362306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(8)),
7462306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(8)),
7562306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(9)),
7662306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(9)),
7762306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(10)),
7862306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(10)),
7962306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(11)),
8062306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(11)),
8162306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(12)),
8262306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(12)),
8362306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(13)),
8462306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(13)),
8562306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(14)),
8662306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(14)),
8762306a36Sopenharmony_ci	VC4_REG32(V3D_PCTR(15)),
8862306a36Sopenharmony_ci	VC4_REG32(V3D_PCTRS(15)),
8962306a36Sopenharmony_ci	VC4_REG32(V3D_DBGE),
9062306a36Sopenharmony_ci	VC4_REG32(V3D_FDBGO),
9162306a36Sopenharmony_ci	VC4_REG32(V3D_FDBGB),
9262306a36Sopenharmony_ci	VC4_REG32(V3D_FDBGR),
9362306a36Sopenharmony_ci	VC4_REG32(V3D_FDBGS),
9462306a36Sopenharmony_ci	VC4_REG32(V3D_ERRSTAT),
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct drm_debugfs_entry *entry = m->private;
10062306a36Sopenharmony_ci	struct drm_device *dev = entry->dev;
10162306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
10262306a36Sopenharmony_ci	int ret = vc4_v3d_pm_get(vc4);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (ret == 0) {
10562306a36Sopenharmony_ci		uint32_t ident1 = V3D_READ(V3D_IDENT1);
10662306a36Sopenharmony_ci		uint32_t nslc = VC4_GET_FIELD(ident1, V3D_IDENT1_NSLC);
10762306a36Sopenharmony_ci		uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS);
10862306a36Sopenharmony_ci		uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		seq_printf(m, "Revision:   %d\n",
11162306a36Sopenharmony_ci			   VC4_GET_FIELD(ident1, V3D_IDENT1_REV));
11262306a36Sopenharmony_ci		seq_printf(m, "Slices:     %d\n", nslc);
11362306a36Sopenharmony_ci		seq_printf(m, "TMUs:       %d\n", nslc * tups);
11462306a36Sopenharmony_ci		seq_printf(m, "QPUs:       %d\n", nslc * qups);
11562306a36Sopenharmony_ci		seq_printf(m, "Semaphores: %d\n",
11662306a36Sopenharmony_ci			   VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM));
11762306a36Sopenharmony_ci		vc4_v3d_pm_put(vc4);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return 0;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/*
12462306a36Sopenharmony_ci * Wraps pm_runtime_get_sync() in a refcount, so that we can reliably
12562306a36Sopenharmony_ci * get the pm_runtime refcount to 0 in vc4_reset().
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_ciint
12862306a36Sopenharmony_civc4_v3d_pm_get(struct vc4_dev *vc4)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
13162306a36Sopenharmony_ci		return -ENODEV;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	mutex_lock(&vc4->power_lock);
13462306a36Sopenharmony_ci	if (vc4->power_refcount++ == 0) {
13562306a36Sopenharmony_ci		int ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		if (ret < 0) {
13862306a36Sopenharmony_ci			vc4->power_refcount--;
13962306a36Sopenharmony_ci			mutex_unlock(&vc4->power_lock);
14062306a36Sopenharmony_ci			return ret;
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	mutex_unlock(&vc4->power_lock);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_civoid
14962306a36Sopenharmony_civc4_v3d_pm_put(struct vc4_dev *vc4)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
15262306a36Sopenharmony_ci		return;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	mutex_lock(&vc4->power_lock);
15562306a36Sopenharmony_ci	if (--vc4->power_refcount == 0) {
15662306a36Sopenharmony_ci		pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev);
15762306a36Sopenharmony_ci		pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev);
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci	mutex_unlock(&vc4->power_lock);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic void vc4_v3d_init_hw(struct drm_device *dev)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Take all the memory that would have been reserved for user
16762306a36Sopenharmony_ci	 * QPU programs, since we don't have an interface for running
16862306a36Sopenharmony_ci	 * them, anyway.
16962306a36Sopenharmony_ci	 */
17062306a36Sopenharmony_ci	V3D_WRITE(V3D_VPMBASE, 0);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ciint vc4_v3d_get_bin_slot(struct vc4_dev *vc4)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct drm_device *dev = &vc4->base;
17662306a36Sopenharmony_ci	unsigned long irqflags;
17762306a36Sopenharmony_ci	int slot;
17862306a36Sopenharmony_ci	uint64_t seqno = 0;
17962306a36Sopenharmony_ci	struct vc4_exec_info *exec;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
18262306a36Sopenharmony_ci		return -ENODEV;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_citry_again:
18562306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
18662306a36Sopenharmony_ci	slot = ffs(~vc4->bin_alloc_used);
18762306a36Sopenharmony_ci	if (slot != 0) {
18862306a36Sopenharmony_ci		/* Switch from ffs() bit index to a 0-based index. */
18962306a36Sopenharmony_ci		slot--;
19062306a36Sopenharmony_ci		vc4->bin_alloc_used |= BIT(slot);
19162306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
19262306a36Sopenharmony_ci		return slot;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Couldn't find an open slot.  Wait for render to complete
19662306a36Sopenharmony_ci	 * and try again.
19762306a36Sopenharmony_ci	 */
19862306a36Sopenharmony_ci	exec = vc4_last_render_job(vc4);
19962306a36Sopenharmony_ci	if (exec)
20062306a36Sopenharmony_ci		seqno = exec->seqno;
20162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (seqno) {
20462306a36Sopenharmony_ci		int ret = vc4_wait_for_seqno(dev, seqno, ~0ull, true);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		if (ret == 0)
20762306a36Sopenharmony_ci			goto try_again;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		return ret;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	return -ENOMEM;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/*
21662306a36Sopenharmony_ci * bin_bo_alloc() - allocates the memory that will be used for
21762306a36Sopenharmony_ci * tile binning.
21862306a36Sopenharmony_ci *
21962306a36Sopenharmony_ci * The binner has a limitation that the addresses in the tile state
22062306a36Sopenharmony_ci * buffer that point into the tile alloc buffer or binner overflow
22162306a36Sopenharmony_ci * memory only have 28 bits (256MB), and the top 4 on the bus for
22262306a36Sopenharmony_ci * tile alloc references end up coming from the tile state buffer's
22362306a36Sopenharmony_ci * address.
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci * To work around this, we allocate a single large buffer while V3D is
22662306a36Sopenharmony_ci * in use, make sure that it has the top 4 bits constant across its
22762306a36Sopenharmony_ci * entire extent, and then put the tile state, tile alloc, and binner
22862306a36Sopenharmony_ci * overflow memory inside that buffer.
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * This creates a limitation where we may not be able to execute a job
23162306a36Sopenharmony_ci * if it doesn't fit within the buffer that we allocated up front.
23262306a36Sopenharmony_ci * However, it turns out that 16MB is "enough for anybody", and
23362306a36Sopenharmony_ci * real-world applications run into allocation failures from the
23462306a36Sopenharmony_ci * overall DMA pool before they make scenes complicated enough to run
23562306a36Sopenharmony_ci * out of bin space.
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_cistatic int bin_bo_alloc(struct vc4_dev *vc4)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct vc4_v3d *v3d = vc4->v3d;
24062306a36Sopenharmony_ci	uint32_t size = 16 * 1024 * 1024;
24162306a36Sopenharmony_ci	int ret = 0;
24262306a36Sopenharmony_ci	struct list_head list;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (!v3d)
24562306a36Sopenharmony_ci		return -ENODEV;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* We may need to try allocating more than once to get a BO
24862306a36Sopenharmony_ci	 * that doesn't cross 256MB.  Track the ones we've allocated
24962306a36Sopenharmony_ci	 * that failed so far, so that we can free them when we've got
25062306a36Sopenharmony_ci	 * one that succeeded (if we freed them right away, our next
25162306a36Sopenharmony_ci	 * allocation would probably be the same chunk of memory).
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci	INIT_LIST_HEAD(&list);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	while (true) {
25662306a36Sopenharmony_ci		struct vc4_bo *bo = vc4_bo_create(&vc4->base, size, true,
25762306a36Sopenharmony_ci						  VC4_BO_TYPE_BIN);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		if (IS_ERR(bo)) {
26062306a36Sopenharmony_ci			ret = PTR_ERR(bo);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci			dev_err(&v3d->pdev->dev,
26362306a36Sopenharmony_ci				"Failed to allocate memory for tile binning: "
26462306a36Sopenharmony_ci				"%d. You may need to enable DMA or give it "
26562306a36Sopenharmony_ci				"more memory.",
26662306a36Sopenharmony_ci				ret);
26762306a36Sopenharmony_ci			break;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		/* Check if this BO won't trigger the addressing bug. */
27162306a36Sopenharmony_ci		if ((bo->base.dma_addr & 0xf0000000) ==
27262306a36Sopenharmony_ci		    ((bo->base.dma_addr + bo->base.base.size - 1) & 0xf0000000)) {
27362306a36Sopenharmony_ci			vc4->bin_bo = bo;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci			/* Set up for allocating 512KB chunks of
27662306a36Sopenharmony_ci			 * binner memory.  The biggest allocation we
27762306a36Sopenharmony_ci			 * need to do is for the initial tile alloc +
27862306a36Sopenharmony_ci			 * tile state buffer.  We can render to a
27962306a36Sopenharmony_ci			 * maximum of ((2048*2048) / (32*32) = 4096
28062306a36Sopenharmony_ci			 * tiles in a frame (until we do floating
28162306a36Sopenharmony_ci			 * point rendering, at which point it would be
28262306a36Sopenharmony_ci			 * 8192).  Tile state is 48b/tile (rounded to
28362306a36Sopenharmony_ci			 * a page), and tile alloc is 32b/tile
28462306a36Sopenharmony_ci			 * (rounded to a page), plus a page of extra,
28562306a36Sopenharmony_ci			 * for a total of 320kb for our worst-case.
28662306a36Sopenharmony_ci			 * We choose 512kb so that it divides evenly
28762306a36Sopenharmony_ci			 * into our 16MB, and the rest of the 512kb
28862306a36Sopenharmony_ci			 * will be used as storage for the overflow
28962306a36Sopenharmony_ci			 * from the initial 32b CL per bin.
29062306a36Sopenharmony_ci			 */
29162306a36Sopenharmony_ci			vc4->bin_alloc_size = 512 * 1024;
29262306a36Sopenharmony_ci			vc4->bin_alloc_used = 0;
29362306a36Sopenharmony_ci			vc4->bin_alloc_overflow = 0;
29462306a36Sopenharmony_ci			WARN_ON_ONCE(sizeof(vc4->bin_alloc_used) * 8 !=
29562306a36Sopenharmony_ci				     bo->base.base.size / vc4->bin_alloc_size);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci			kref_init(&vc4->bin_bo_kref);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci			/* Enable the out-of-memory interrupt to set our
30062306a36Sopenharmony_ci			 * newly-allocated binner BO, potentially from an
30162306a36Sopenharmony_ci			 * already-pending-but-masked interrupt.
30262306a36Sopenharmony_ci			 */
30362306a36Sopenharmony_ci			V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci			break;
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		/* Put it on the list to free later, and try again. */
30962306a36Sopenharmony_ci		list_add(&bo->unref_head, &list);
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* Free all the BOs we allocated but didn't choose. */
31362306a36Sopenharmony_ci	while (!list_empty(&list)) {
31462306a36Sopenharmony_ci		struct vc4_bo *bo = list_last_entry(&list,
31562306a36Sopenharmony_ci						    struct vc4_bo, unref_head);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		list_del(&bo->unref_head);
31862306a36Sopenharmony_ci		drm_gem_object_put(&bo->base.base);
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	return ret;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ciint vc4_v3d_bin_bo_get(struct vc4_dev *vc4, bool *used)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	int ret = 0;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
32962306a36Sopenharmony_ci		return -ENODEV;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	mutex_lock(&vc4->bin_bo_lock);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (used && *used)
33462306a36Sopenharmony_ci		goto complete;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (vc4->bin_bo)
33762306a36Sopenharmony_ci		kref_get(&vc4->bin_bo_kref);
33862306a36Sopenharmony_ci	else
33962306a36Sopenharmony_ci		ret = bin_bo_alloc(vc4);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (ret == 0 && used)
34262306a36Sopenharmony_ci		*used = true;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cicomplete:
34562306a36Sopenharmony_ci	mutex_unlock(&vc4->bin_bo_lock);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return ret;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic void bin_bo_release(struct kref *ref)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct vc4_dev *vc4 = container_of(ref, struct vc4_dev, bin_bo_kref);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (WARN_ON_ONCE(!vc4->bin_bo))
35562306a36Sopenharmony_ci		return;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	drm_gem_object_put(&vc4->bin_bo->base.base);
35862306a36Sopenharmony_ci	vc4->bin_bo = NULL;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_civoid vc4_v3d_bin_bo_put(struct vc4_dev *vc4)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
36462306a36Sopenharmony_ci		return;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	mutex_lock(&vc4->bin_bo_lock);
36762306a36Sopenharmony_ci	kref_put(&vc4->bin_bo_kref, bin_bo_release);
36862306a36Sopenharmony_ci	mutex_unlock(&vc4->bin_bo_lock);
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci#ifdef CONFIG_PM
37262306a36Sopenharmony_cistatic int vc4_v3d_runtime_suspend(struct device *dev)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct vc4_v3d *v3d = dev_get_drvdata(dev);
37562306a36Sopenharmony_ci	struct vc4_dev *vc4 = v3d->vc4;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	vc4_irq_disable(&vc4->base);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	clk_disable_unprepare(v3d->clk);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return 0;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int vc4_v3d_runtime_resume(struct device *dev)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct vc4_v3d *v3d = dev_get_drvdata(dev);
38762306a36Sopenharmony_ci	struct vc4_dev *vc4 = v3d->vc4;
38862306a36Sopenharmony_ci	int ret;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ret = clk_prepare_enable(v3d->clk);
39162306a36Sopenharmony_ci	if (ret != 0)
39262306a36Sopenharmony_ci		return ret;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	vc4_v3d_init_hw(&vc4->base);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	vc4_irq_enable(&vc4->base);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci#endif
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ciint vc4_v3d_debugfs_init(struct drm_minor *minor)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct drm_device *drm = minor->dev;
40562306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(drm);
40662306a36Sopenharmony_ci	struct vc4_v3d *v3d = vc4->v3d;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (!vc4->v3d)
40962306a36Sopenharmony_ci		return -ENODEV;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	drm_debugfs_add_file(drm, "v3d_ident", vc4_v3d_debugfs_ident, NULL);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	vc4_debugfs_add_regset32(drm, "v3d_regs", &v3d->regset);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return 0;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
42162306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(master);
42262306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(drm);
42362306a36Sopenharmony_ci	struct vc4_v3d *v3d = NULL;
42462306a36Sopenharmony_ci	int ret;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL);
42762306a36Sopenharmony_ci	if (!v3d)
42862306a36Sopenharmony_ci		return -ENOMEM;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	dev_set_drvdata(dev, v3d);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	v3d->pdev = pdev;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	v3d->regs = vc4_ioremap_regs(pdev, 0);
43562306a36Sopenharmony_ci	if (IS_ERR(v3d->regs))
43662306a36Sopenharmony_ci		return PTR_ERR(v3d->regs);
43762306a36Sopenharmony_ci	v3d->regset.base = v3d->regs;
43862306a36Sopenharmony_ci	v3d->regset.regs = v3d_regs;
43962306a36Sopenharmony_ci	v3d->regset.nregs = ARRAY_SIZE(v3d_regs);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	vc4->v3d = v3d;
44262306a36Sopenharmony_ci	v3d->vc4 = vc4;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	v3d->clk = devm_clk_get(dev, NULL);
44562306a36Sopenharmony_ci	if (IS_ERR(v3d->clk)) {
44662306a36Sopenharmony_ci		int ret = PTR_ERR(v3d->clk);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		if (ret == -ENOENT) {
44962306a36Sopenharmony_ci			/* bcm2835 didn't have a clock reference in the DT. */
45062306a36Sopenharmony_ci			ret = 0;
45162306a36Sopenharmony_ci			v3d->clk = NULL;
45262306a36Sopenharmony_ci		} else {
45362306a36Sopenharmony_ci			if (ret != -EPROBE_DEFER)
45462306a36Sopenharmony_ci				dev_err(dev, "Failed to get V3D clock: %d\n",
45562306a36Sopenharmony_ci					ret);
45662306a36Sopenharmony_ci			return ret;
45762306a36Sopenharmony_ci		}
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	ret = platform_get_irq(pdev, 0);
46162306a36Sopenharmony_ci	if (ret < 0)
46262306a36Sopenharmony_ci		return ret;
46362306a36Sopenharmony_ci	vc4->irq = ret;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	ret = devm_pm_runtime_enable(dev);
46662306a36Sopenharmony_ci	if (ret)
46762306a36Sopenharmony_ci		return ret;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
47062306a36Sopenharmony_ci	if (ret)
47162306a36Sopenharmony_ci		return ret;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) {
47462306a36Sopenharmony_ci		DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n",
47562306a36Sopenharmony_ci			  V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0);
47662306a36Sopenharmony_ci		ret = -EINVAL;
47762306a36Sopenharmony_ci		goto err_put_runtime_pm;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* Reset the binner overflow address/size at setup, to be sure
48162306a36Sopenharmony_ci	 * we don't reuse an old one.
48262306a36Sopenharmony_ci	 */
48362306a36Sopenharmony_ci	V3D_WRITE(V3D_BPOA, 0);
48462306a36Sopenharmony_ci	V3D_WRITE(V3D_BPOS, 0);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	ret = vc4_irq_install(drm, vc4->irq);
48762306a36Sopenharmony_ci	if (ret) {
48862306a36Sopenharmony_ci		DRM_ERROR("Failed to install IRQ handler\n");
48962306a36Sopenharmony_ci		goto err_put_runtime_pm;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	pm_runtime_use_autosuspend(dev);
49362306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(dev, 40); /* a little over 2 frames. */
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return 0;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cierr_put_runtime_pm:
49862306a36Sopenharmony_ci	pm_runtime_put(dev);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return ret;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic void vc4_v3d_unbind(struct device *dev, struct device *master,
50462306a36Sopenharmony_ci			   void *data)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(master);
50762306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(drm);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	vc4_irq_uninstall(drm);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/* Disable the binner's overflow memory address, so the next
51262306a36Sopenharmony_ci	 * driver probe (if any) doesn't try to reuse our old
51362306a36Sopenharmony_ci	 * allocation.
51462306a36Sopenharmony_ci	 */
51562306a36Sopenharmony_ci	V3D_WRITE(V3D_BPOA, 0);
51662306a36Sopenharmony_ci	V3D_WRITE(V3D_BPOS, 0);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	vc4->v3d = NULL;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic const struct dev_pm_ops vc4_v3d_pm_ops = {
52262306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(vc4_v3d_runtime_suspend, vc4_v3d_runtime_resume, NULL)
52362306a36Sopenharmony_ci};
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic const struct component_ops vc4_v3d_ops = {
52662306a36Sopenharmony_ci	.bind   = vc4_v3d_bind,
52762306a36Sopenharmony_ci	.unbind = vc4_v3d_unbind,
52862306a36Sopenharmony_ci};
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic int vc4_v3d_dev_probe(struct platform_device *pdev)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	return component_add(&pdev->dev, &vc4_v3d_ops);
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic void vc4_v3d_dev_remove(struct platform_device *pdev)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	component_del(&pdev->dev, &vc4_v3d_ops);
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ciconst struct of_device_id vc4_v3d_dt_match[] = {
54162306a36Sopenharmony_ci	{ .compatible = "brcm,bcm2835-v3d" },
54262306a36Sopenharmony_ci	{ .compatible = "brcm,cygnus-v3d" },
54362306a36Sopenharmony_ci	{ .compatible = "brcm,vc4-v3d" },
54462306a36Sopenharmony_ci	{}
54562306a36Sopenharmony_ci};
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistruct platform_driver vc4_v3d_driver = {
54862306a36Sopenharmony_ci	.probe = vc4_v3d_dev_probe,
54962306a36Sopenharmony_ci	.remove_new = vc4_v3d_dev_remove,
55062306a36Sopenharmony_ci	.driver = {
55162306a36Sopenharmony_ci		.name = "vc4_v3d",
55262306a36Sopenharmony_ci		.of_match_table = vc4_v3d_dt_match,
55362306a36Sopenharmony_ci		.pm = &vc4_v3d_pm_ops,
55462306a36Sopenharmony_ci	},
55562306a36Sopenharmony_ci};
556