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#include <linux/module.h>
2562306a36Sopenharmony_ci#include <linux/platform_device.h>
2662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2762306a36Sopenharmony_ci#include <linux/device.h>
2862306a36Sopenharmony_ci#include <linux/io.h>
2962306a36Sopenharmony_ci#include <linux/sched/signal.h>
3062306a36Sopenharmony_ci#include <linux/dma-fence-array.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <drm/drm_syncobj.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "uapi/drm/vc4_drm.h"
3562306a36Sopenharmony_ci#include "vc4_drv.h"
3662306a36Sopenharmony_ci#include "vc4_regs.h"
3762306a36Sopenharmony_ci#include "vc4_trace.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void
4062306a36Sopenharmony_civc4_queue_hangcheck(struct drm_device *dev)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	mod_timer(&vc4->hangcheck.timer,
4562306a36Sopenharmony_ci		  round_jiffies_up(jiffies + msecs_to_jiffies(100)));
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct vc4_hang_state {
4962306a36Sopenharmony_ci	struct drm_vc4_get_hang_state user_state;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	u32 bo_count;
5262306a36Sopenharmony_ci	struct drm_gem_object **bo;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void
5662306a36Sopenharmony_civc4_free_hang_state(struct drm_device *dev, struct vc4_hang_state *state)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	unsigned int i;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	for (i = 0; i < state->user_state.bo_count; i++)
6162306a36Sopenharmony_ci		drm_gem_object_put(state->bo[i]);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	kfree(state);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciint
6762306a36Sopenharmony_civc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
6862306a36Sopenharmony_ci			 struct drm_file *file_priv)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct drm_vc4_get_hang_state *get_state = data;
7162306a36Sopenharmony_ci	struct drm_vc4_get_hang_state_bo *bo_state;
7262306a36Sopenharmony_ci	struct vc4_hang_state *kernel_state;
7362306a36Sopenharmony_ci	struct drm_vc4_get_hang_state *state;
7462306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
7562306a36Sopenharmony_ci	unsigned long irqflags;
7662306a36Sopenharmony_ci	u32 i;
7762306a36Sopenharmony_ci	int ret = 0;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
8062306a36Sopenharmony_ci		return -ENODEV;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (!vc4->v3d) {
8362306a36Sopenharmony_ci		DRM_DEBUG("VC4_GET_HANG_STATE with no VC4 V3D probed\n");
8462306a36Sopenharmony_ci		return -ENODEV;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
8862306a36Sopenharmony_ci	kernel_state = vc4->hang_state;
8962306a36Sopenharmony_ci	if (!kernel_state) {
9062306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
9162306a36Sopenharmony_ci		return -ENOENT;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci	state = &kernel_state->user_state;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* If the user's array isn't big enough, just return the
9662306a36Sopenharmony_ci	 * required array size.
9762306a36Sopenharmony_ci	 */
9862306a36Sopenharmony_ci	if (get_state->bo_count < state->bo_count) {
9962306a36Sopenharmony_ci		get_state->bo_count = state->bo_count;
10062306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
10162306a36Sopenharmony_ci		return 0;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	vc4->hang_state = NULL;
10562306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Save the user's BO pointer, so we don't stomp it with the memcpy. */
10862306a36Sopenharmony_ci	state->bo = get_state->bo;
10962306a36Sopenharmony_ci	memcpy(get_state, state, sizeof(*state));
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	bo_state = kcalloc(state->bo_count, sizeof(*bo_state), GFP_KERNEL);
11262306a36Sopenharmony_ci	if (!bo_state) {
11362306a36Sopenharmony_ci		ret = -ENOMEM;
11462306a36Sopenharmony_ci		goto err_free;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	for (i = 0; i < state->bo_count; i++) {
11862306a36Sopenharmony_ci		struct vc4_bo *vc4_bo = to_vc4_bo(kernel_state->bo[i]);
11962306a36Sopenharmony_ci		u32 handle;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		ret = drm_gem_handle_create(file_priv, kernel_state->bo[i],
12262306a36Sopenharmony_ci					    &handle);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		if (ret) {
12562306a36Sopenharmony_ci			state->bo_count = i;
12662306a36Sopenharmony_ci			goto err_delete_handle;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci		bo_state[i].handle = handle;
12962306a36Sopenharmony_ci		bo_state[i].paddr = vc4_bo->base.dma_addr;
13062306a36Sopenharmony_ci		bo_state[i].size = vc4_bo->base.base.size;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (copy_to_user(u64_to_user_ptr(get_state->bo),
13462306a36Sopenharmony_ci			 bo_state,
13562306a36Sopenharmony_ci			 state->bo_count * sizeof(*bo_state)))
13662306a36Sopenharmony_ci		ret = -EFAULT;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cierr_delete_handle:
13962306a36Sopenharmony_ci	if (ret) {
14062306a36Sopenharmony_ci		for (i = 0; i < state->bo_count; i++)
14162306a36Sopenharmony_ci			drm_gem_handle_delete(file_priv, bo_state[i].handle);
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cierr_free:
14562306a36Sopenharmony_ci	vc4_free_hang_state(dev, kernel_state);
14662306a36Sopenharmony_ci	kfree(bo_state);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return ret;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic void
15262306a36Sopenharmony_civc4_save_hang_state(struct drm_device *dev)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
15562306a36Sopenharmony_ci	struct drm_vc4_get_hang_state *state;
15662306a36Sopenharmony_ci	struct vc4_hang_state *kernel_state;
15762306a36Sopenharmony_ci	struct vc4_exec_info *exec[2];
15862306a36Sopenharmony_ci	struct vc4_bo *bo;
15962306a36Sopenharmony_ci	unsigned long irqflags;
16062306a36Sopenharmony_ci	unsigned int i, j, k, unref_list_count;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	kernel_state = kcalloc(1, sizeof(*kernel_state), GFP_KERNEL);
16362306a36Sopenharmony_ci	if (!kernel_state)
16462306a36Sopenharmony_ci		return;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	state = &kernel_state->user_state;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
16962306a36Sopenharmony_ci	exec[0] = vc4_first_bin_job(vc4);
17062306a36Sopenharmony_ci	exec[1] = vc4_first_render_job(vc4);
17162306a36Sopenharmony_ci	if (!exec[0] && !exec[1]) {
17262306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
17362306a36Sopenharmony_ci		return;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* Get the bos from both binner and renderer into hang state. */
17762306a36Sopenharmony_ci	state->bo_count = 0;
17862306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
17962306a36Sopenharmony_ci		if (!exec[i])
18062306a36Sopenharmony_ci			continue;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		unref_list_count = 0;
18362306a36Sopenharmony_ci		list_for_each_entry(bo, &exec[i]->unref_list, unref_head)
18462306a36Sopenharmony_ci			unref_list_count++;
18562306a36Sopenharmony_ci		state->bo_count += exec[i]->bo_count + unref_list_count;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	kernel_state->bo = kcalloc(state->bo_count,
18962306a36Sopenharmony_ci				   sizeof(*kernel_state->bo), GFP_ATOMIC);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (!kernel_state->bo) {
19262306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
19362306a36Sopenharmony_ci		return;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	k = 0;
19762306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
19862306a36Sopenharmony_ci		if (!exec[i])
19962306a36Sopenharmony_ci			continue;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		for (j = 0; j < exec[i]->bo_count; j++) {
20262306a36Sopenharmony_ci			bo = to_vc4_bo(exec[i]->bo[j]);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci			/* Retain BOs just in case they were marked purgeable.
20562306a36Sopenharmony_ci			 * This prevents the BO from being purged before
20662306a36Sopenharmony_ci			 * someone had a chance to dump the hang state.
20762306a36Sopenharmony_ci			 */
20862306a36Sopenharmony_ci			WARN_ON(!refcount_read(&bo->usecnt));
20962306a36Sopenharmony_ci			refcount_inc(&bo->usecnt);
21062306a36Sopenharmony_ci			drm_gem_object_get(exec[i]->bo[j]);
21162306a36Sopenharmony_ci			kernel_state->bo[k++] = exec[i]->bo[j];
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		list_for_each_entry(bo, &exec[i]->unref_list, unref_head) {
21562306a36Sopenharmony_ci			/* No need to retain BOs coming from the ->unref_list
21662306a36Sopenharmony_ci			 * because they are naturally unpurgeable.
21762306a36Sopenharmony_ci			 */
21862306a36Sopenharmony_ci			drm_gem_object_get(&bo->base.base);
21962306a36Sopenharmony_ci			kernel_state->bo[k++] = &bo->base.base;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	WARN_ON_ONCE(k != state->bo_count);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (exec[0])
22662306a36Sopenharmony_ci		state->start_bin = exec[0]->ct0ca;
22762306a36Sopenharmony_ci	if (exec[1])
22862306a36Sopenharmony_ci		state->start_render = exec[1]->ct1ca;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	state->ct0ca = V3D_READ(V3D_CTNCA(0));
23362306a36Sopenharmony_ci	state->ct0ea = V3D_READ(V3D_CTNEA(0));
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	state->ct1ca = V3D_READ(V3D_CTNCA(1));
23662306a36Sopenharmony_ci	state->ct1ea = V3D_READ(V3D_CTNEA(1));
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	state->ct0cs = V3D_READ(V3D_CTNCS(0));
23962306a36Sopenharmony_ci	state->ct1cs = V3D_READ(V3D_CTNCS(1));
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	state->ct0ra0 = V3D_READ(V3D_CT00RA0);
24262306a36Sopenharmony_ci	state->ct1ra0 = V3D_READ(V3D_CT01RA0);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	state->bpca = V3D_READ(V3D_BPCA);
24562306a36Sopenharmony_ci	state->bpcs = V3D_READ(V3D_BPCS);
24662306a36Sopenharmony_ci	state->bpoa = V3D_READ(V3D_BPOA);
24762306a36Sopenharmony_ci	state->bpos = V3D_READ(V3D_BPOS);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	state->vpmbase = V3D_READ(V3D_VPMBASE);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	state->dbge = V3D_READ(V3D_DBGE);
25262306a36Sopenharmony_ci	state->fdbgo = V3D_READ(V3D_FDBGO);
25362306a36Sopenharmony_ci	state->fdbgb = V3D_READ(V3D_FDBGB);
25462306a36Sopenharmony_ci	state->fdbgr = V3D_READ(V3D_FDBGR);
25562306a36Sopenharmony_ci	state->fdbgs = V3D_READ(V3D_FDBGS);
25662306a36Sopenharmony_ci	state->errstat = V3D_READ(V3D_ERRSTAT);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* We need to turn purgeable BOs into unpurgeable ones so that
25962306a36Sopenharmony_ci	 * userspace has a chance to dump the hang state before the kernel
26062306a36Sopenharmony_ci	 * decides to purge those BOs.
26162306a36Sopenharmony_ci	 * Note that BO consistency at dump time cannot be guaranteed. For
26262306a36Sopenharmony_ci	 * example, if the owner of these BOs decides to re-use them or mark
26362306a36Sopenharmony_ci	 * them purgeable again there's nothing we can do to prevent it.
26462306a36Sopenharmony_ci	 */
26562306a36Sopenharmony_ci	for (i = 0; i < kernel_state->user_state.bo_count; i++) {
26662306a36Sopenharmony_ci		struct vc4_bo *bo = to_vc4_bo(kernel_state->bo[i]);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		if (bo->madv == __VC4_MADV_NOTSUPP)
26962306a36Sopenharmony_ci			continue;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		mutex_lock(&bo->madv_lock);
27262306a36Sopenharmony_ci		if (!WARN_ON(bo->madv == __VC4_MADV_PURGED))
27362306a36Sopenharmony_ci			bo->madv = VC4_MADV_WILLNEED;
27462306a36Sopenharmony_ci		refcount_dec(&bo->usecnt);
27562306a36Sopenharmony_ci		mutex_unlock(&bo->madv_lock);
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
27962306a36Sopenharmony_ci	if (vc4->hang_state) {
28062306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
28162306a36Sopenharmony_ci		vc4_free_hang_state(dev, kernel_state);
28262306a36Sopenharmony_ci	} else {
28362306a36Sopenharmony_ci		vc4->hang_state = kernel_state;
28462306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void
28962306a36Sopenharmony_civc4_reset(struct drm_device *dev)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	DRM_INFO("Resetting GPU.\n");
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	mutex_lock(&vc4->power_lock);
29662306a36Sopenharmony_ci	if (vc4->power_refcount) {
29762306a36Sopenharmony_ci		/* Power the device off and back on the by dropping the
29862306a36Sopenharmony_ci		 * reference on runtime PM.
29962306a36Sopenharmony_ci		 */
30062306a36Sopenharmony_ci		pm_runtime_put_sync_suspend(&vc4->v3d->pdev->dev);
30162306a36Sopenharmony_ci		pm_runtime_get_sync(&vc4->v3d->pdev->dev);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci	mutex_unlock(&vc4->power_lock);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	vc4_irq_reset(dev);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* Rearm the hangcheck -- another job might have been waiting
30862306a36Sopenharmony_ci	 * for our hung one to get kicked off, and vc4_irq_reset()
30962306a36Sopenharmony_ci	 * would have started it.
31062306a36Sopenharmony_ci	 */
31162306a36Sopenharmony_ci	vc4_queue_hangcheck(dev);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void
31562306a36Sopenharmony_civc4_reset_work(struct work_struct *work)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct vc4_dev *vc4 =
31862306a36Sopenharmony_ci		container_of(work, struct vc4_dev, hangcheck.reset_work);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	vc4_save_hang_state(&vc4->base);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	vc4_reset(&vc4->base);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void
32662306a36Sopenharmony_civc4_hangcheck_elapsed(struct timer_list *t)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct vc4_dev *vc4 = from_timer(vc4, t, hangcheck.timer);
32962306a36Sopenharmony_ci	struct drm_device *dev = &vc4->base;
33062306a36Sopenharmony_ci	uint32_t ct0ca, ct1ca;
33162306a36Sopenharmony_ci	unsigned long irqflags;
33262306a36Sopenharmony_ci	struct vc4_exec_info *bin_exec, *render_exec;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	bin_exec = vc4_first_bin_job(vc4);
33762306a36Sopenharmony_ci	render_exec = vc4_first_render_job(vc4);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* If idle, we can stop watching for hangs. */
34062306a36Sopenharmony_ci	if (!bin_exec && !render_exec) {
34162306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
34262306a36Sopenharmony_ci		return;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	ct0ca = V3D_READ(V3D_CTNCA(0));
34662306a36Sopenharmony_ci	ct1ca = V3D_READ(V3D_CTNCA(1));
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* If we've made any progress in execution, rearm the timer
34962306a36Sopenharmony_ci	 * and wait.
35062306a36Sopenharmony_ci	 */
35162306a36Sopenharmony_ci	if ((bin_exec && ct0ca != bin_exec->last_ct0ca) ||
35262306a36Sopenharmony_ci	    (render_exec && ct1ca != render_exec->last_ct1ca)) {
35362306a36Sopenharmony_ci		if (bin_exec)
35462306a36Sopenharmony_ci			bin_exec->last_ct0ca = ct0ca;
35562306a36Sopenharmony_ci		if (render_exec)
35662306a36Sopenharmony_ci			render_exec->last_ct1ca = ct1ca;
35762306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
35862306a36Sopenharmony_ci		vc4_queue_hangcheck(dev);
35962306a36Sopenharmony_ci		return;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* We've gone too long with no progress, reset.  This has to
36562306a36Sopenharmony_ci	 * be done from a work struct, since resetting can sleep and
36662306a36Sopenharmony_ci	 * this timer hook isn't allowed to.
36762306a36Sopenharmony_ci	 */
36862306a36Sopenharmony_ci	schedule_work(&vc4->hangcheck.reset_work);
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic void
37262306a36Sopenharmony_cisubmit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* Set the current and end address of the control list.
37762306a36Sopenharmony_ci	 * Writing the end register is what starts the job.
37862306a36Sopenharmony_ci	 */
37962306a36Sopenharmony_ci	V3D_WRITE(V3D_CTNCA(thread), start);
38062306a36Sopenharmony_ci	V3D_WRITE(V3D_CTNEA(thread), end);
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ciint
38462306a36Sopenharmony_civc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns,
38562306a36Sopenharmony_ci		   bool interruptible)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
38862306a36Sopenharmony_ci	int ret = 0;
38962306a36Sopenharmony_ci	unsigned long timeout_expire;
39062306a36Sopenharmony_ci	DEFINE_WAIT(wait);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
39362306a36Sopenharmony_ci		return -ENODEV;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (vc4->finished_seqno >= seqno)
39662306a36Sopenharmony_ci		return 0;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if (timeout_ns == 0)
39962306a36Sopenharmony_ci		return -ETIME;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	timeout_expire = jiffies + nsecs_to_jiffies(timeout_ns);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	trace_vc4_wait_for_seqno_begin(dev, seqno, timeout_ns);
40462306a36Sopenharmony_ci	for (;;) {
40562306a36Sopenharmony_ci		prepare_to_wait(&vc4->job_wait_queue, &wait,
40662306a36Sopenharmony_ci				interruptible ? TASK_INTERRUPTIBLE :
40762306a36Sopenharmony_ci				TASK_UNINTERRUPTIBLE);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		if (interruptible && signal_pending(current)) {
41062306a36Sopenharmony_ci			ret = -ERESTARTSYS;
41162306a36Sopenharmony_ci			break;
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		if (vc4->finished_seqno >= seqno)
41562306a36Sopenharmony_ci			break;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		if (timeout_ns != ~0ull) {
41862306a36Sopenharmony_ci			if (time_after_eq(jiffies, timeout_expire)) {
41962306a36Sopenharmony_ci				ret = -ETIME;
42062306a36Sopenharmony_ci				break;
42162306a36Sopenharmony_ci			}
42262306a36Sopenharmony_ci			schedule_timeout(timeout_expire - jiffies);
42362306a36Sopenharmony_ci		} else {
42462306a36Sopenharmony_ci			schedule();
42562306a36Sopenharmony_ci		}
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	finish_wait(&vc4->job_wait_queue, &wait);
42962306a36Sopenharmony_ci	trace_vc4_wait_for_seqno_end(dev, seqno);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return ret;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic void
43562306a36Sopenharmony_civc4_flush_caches(struct drm_device *dev)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* Flush the GPU L2 caches.  These caches sit on top of system
44062306a36Sopenharmony_ci	 * L3 (the 128kb or so shared with the CPU), and are
44162306a36Sopenharmony_ci	 * non-allocating in the L3.
44262306a36Sopenharmony_ci	 */
44362306a36Sopenharmony_ci	V3D_WRITE(V3D_L2CACTL,
44462306a36Sopenharmony_ci		  V3D_L2CACTL_L2CCLR);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	V3D_WRITE(V3D_SLCACTL,
44762306a36Sopenharmony_ci		  VC4_SET_FIELD(0xf, V3D_SLCACTL_T1CC) |
44862306a36Sopenharmony_ci		  VC4_SET_FIELD(0xf, V3D_SLCACTL_T0CC) |
44962306a36Sopenharmony_ci		  VC4_SET_FIELD(0xf, V3D_SLCACTL_UCC) |
45062306a36Sopenharmony_ci		  VC4_SET_FIELD(0xf, V3D_SLCACTL_ICC));
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic void
45462306a36Sopenharmony_civc4_flush_texture_caches(struct drm_device *dev)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	V3D_WRITE(V3D_L2CACTL,
45962306a36Sopenharmony_ci		  V3D_L2CACTL_L2CCLR);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	V3D_WRITE(V3D_SLCACTL,
46262306a36Sopenharmony_ci		  VC4_SET_FIELD(0xf, V3D_SLCACTL_T1CC) |
46362306a36Sopenharmony_ci		  VC4_SET_FIELD(0xf, V3D_SLCACTL_T0CC));
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci/* Sets the registers for the next job to be actually be executed in
46762306a36Sopenharmony_ci * the hardware.
46862306a36Sopenharmony_ci *
46962306a36Sopenharmony_ci * The job_lock should be held during this.
47062306a36Sopenharmony_ci */
47162306a36Sopenharmony_civoid
47262306a36Sopenharmony_civc4_submit_next_bin_job(struct drm_device *dev)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
47562306a36Sopenharmony_ci	struct vc4_exec_info *exec;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
47862306a36Sopenharmony_ci		return;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ciagain:
48162306a36Sopenharmony_ci	exec = vc4_first_bin_job(vc4);
48262306a36Sopenharmony_ci	if (!exec)
48362306a36Sopenharmony_ci		return;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	vc4_flush_caches(dev);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* Only start the perfmon if it was not already started by a previous
48862306a36Sopenharmony_ci	 * job.
48962306a36Sopenharmony_ci	 */
49062306a36Sopenharmony_ci	if (exec->perfmon && vc4->active_perfmon != exec->perfmon)
49162306a36Sopenharmony_ci		vc4_perfmon_start(vc4, exec->perfmon);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* Either put the job in the binner if it uses the binner, or
49462306a36Sopenharmony_ci	 * immediately move it to the to-be-rendered queue.
49562306a36Sopenharmony_ci	 */
49662306a36Sopenharmony_ci	if (exec->ct0ca != exec->ct0ea) {
49762306a36Sopenharmony_ci		trace_vc4_submit_cl(dev, false, exec->seqno, exec->ct0ca,
49862306a36Sopenharmony_ci				    exec->ct0ea);
49962306a36Sopenharmony_ci		submit_cl(dev, 0, exec->ct0ca, exec->ct0ea);
50062306a36Sopenharmony_ci	} else {
50162306a36Sopenharmony_ci		struct vc4_exec_info *next;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		vc4_move_job_to_render(dev, exec);
50462306a36Sopenharmony_ci		next = vc4_first_bin_job(vc4);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		/* We can't start the next bin job if the previous job had a
50762306a36Sopenharmony_ci		 * different perfmon instance attached to it. The same goes
50862306a36Sopenharmony_ci		 * if one of them had a perfmon attached to it and the other
50962306a36Sopenharmony_ci		 * one doesn't.
51062306a36Sopenharmony_ci		 */
51162306a36Sopenharmony_ci		if (next && next->perfmon == exec->perfmon)
51262306a36Sopenharmony_ci			goto again;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_civoid
51762306a36Sopenharmony_civc4_submit_next_render_job(struct drm_device *dev)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
52062306a36Sopenharmony_ci	struct vc4_exec_info *exec = vc4_first_render_job(vc4);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (!exec)
52362306a36Sopenharmony_ci		return;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
52662306a36Sopenharmony_ci		return;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/* A previous RCL may have written to one of our textures, and
52962306a36Sopenharmony_ci	 * our full cache flush at bin time may have occurred before
53062306a36Sopenharmony_ci	 * that RCL completed.  Flush the texture cache now, but not
53162306a36Sopenharmony_ci	 * the instructions or uniforms (since we don't write those
53262306a36Sopenharmony_ci	 * from an RCL).
53362306a36Sopenharmony_ci	 */
53462306a36Sopenharmony_ci	vc4_flush_texture_caches(dev);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	trace_vc4_submit_cl(dev, true, exec->seqno, exec->ct1ca, exec->ct1ea);
53762306a36Sopenharmony_ci	submit_cl(dev, 1, exec->ct1ca, exec->ct1ea);
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_civoid
54162306a36Sopenharmony_civc4_move_job_to_render(struct drm_device *dev, struct vc4_exec_info *exec)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
54462306a36Sopenharmony_ci	bool was_empty = list_empty(&vc4->render_job_list);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
54762306a36Sopenharmony_ci		return;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	list_move_tail(&exec->head, &vc4->render_job_list);
55062306a36Sopenharmony_ci	if (was_empty)
55162306a36Sopenharmony_ci		vc4_submit_next_render_job(dev);
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic void
55562306a36Sopenharmony_civc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct vc4_bo *bo;
55862306a36Sopenharmony_ci	unsigned i;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	for (i = 0; i < exec->bo_count; i++) {
56162306a36Sopenharmony_ci		bo = to_vc4_bo(exec->bo[i]);
56262306a36Sopenharmony_ci		bo->seqno = seqno;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		dma_resv_add_fence(bo->base.base.resv, exec->fence,
56562306a36Sopenharmony_ci				   DMA_RESV_USAGE_READ);
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	list_for_each_entry(bo, &exec->unref_list, unref_head) {
56962306a36Sopenharmony_ci		bo->seqno = seqno;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	for (i = 0; i < exec->rcl_write_bo_count; i++) {
57362306a36Sopenharmony_ci		bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);
57462306a36Sopenharmony_ci		bo->write_seqno = seqno;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		dma_resv_add_fence(bo->base.base.resv, exec->fence,
57762306a36Sopenharmony_ci				   DMA_RESV_USAGE_WRITE);
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic void
58262306a36Sopenharmony_civc4_unlock_bo_reservations(struct drm_device *dev,
58362306a36Sopenharmony_ci			   struct vc4_exec_info *exec,
58462306a36Sopenharmony_ci			   struct ww_acquire_ctx *acquire_ctx)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	int i;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	for (i = 0; i < exec->bo_count; i++)
58962306a36Sopenharmony_ci		dma_resv_unlock(exec->bo[i]->resv);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	ww_acquire_fini(acquire_ctx);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci/* Takes the reservation lock on all the BOs being referenced, so that
59562306a36Sopenharmony_ci * at queue submit time we can update the reservations.
59662306a36Sopenharmony_ci *
59762306a36Sopenharmony_ci * We don't lock the RCL the tile alloc/state BOs, or overflow memory
59862306a36Sopenharmony_ci * (all of which are on exec->unref_list).  They're entirely private
59962306a36Sopenharmony_ci * to vc4, so we don't attach dma-buf fences to them.
60062306a36Sopenharmony_ci */
60162306a36Sopenharmony_cistatic int
60262306a36Sopenharmony_civc4_lock_bo_reservations(struct drm_device *dev,
60362306a36Sopenharmony_ci			 struct vc4_exec_info *exec,
60462306a36Sopenharmony_ci			 struct ww_acquire_ctx *acquire_ctx)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	int contended_lock = -1;
60762306a36Sopenharmony_ci	int i, ret;
60862306a36Sopenharmony_ci	struct drm_gem_object *bo;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	ww_acquire_init(acquire_ctx, &reservation_ww_class);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ciretry:
61362306a36Sopenharmony_ci	if (contended_lock != -1) {
61462306a36Sopenharmony_ci		bo = exec->bo[contended_lock];
61562306a36Sopenharmony_ci		ret = dma_resv_lock_slow_interruptible(bo->resv, acquire_ctx);
61662306a36Sopenharmony_ci		if (ret) {
61762306a36Sopenharmony_ci			ww_acquire_done(acquire_ctx);
61862306a36Sopenharmony_ci			return ret;
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	for (i = 0; i < exec->bo_count; i++) {
62362306a36Sopenharmony_ci		if (i == contended_lock)
62462306a36Sopenharmony_ci			continue;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		bo = exec->bo[i];
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		ret = dma_resv_lock_interruptible(bo->resv, acquire_ctx);
62962306a36Sopenharmony_ci		if (ret) {
63062306a36Sopenharmony_ci			int j;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci			for (j = 0; j < i; j++) {
63362306a36Sopenharmony_ci				bo = exec->bo[j];
63462306a36Sopenharmony_ci				dma_resv_unlock(bo->resv);
63562306a36Sopenharmony_ci			}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci			if (contended_lock != -1 && contended_lock >= i) {
63862306a36Sopenharmony_ci				bo = exec->bo[contended_lock];
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci				dma_resv_unlock(bo->resv);
64162306a36Sopenharmony_ci			}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci			if (ret == -EDEADLK) {
64462306a36Sopenharmony_ci				contended_lock = i;
64562306a36Sopenharmony_ci				goto retry;
64662306a36Sopenharmony_ci			}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci			ww_acquire_done(acquire_ctx);
64962306a36Sopenharmony_ci			return ret;
65062306a36Sopenharmony_ci		}
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	ww_acquire_done(acquire_ctx);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	/* Reserve space for our shared (read-only) fence references,
65662306a36Sopenharmony_ci	 * before we commit the CL to the hardware.
65762306a36Sopenharmony_ci	 */
65862306a36Sopenharmony_ci	for (i = 0; i < exec->bo_count; i++) {
65962306a36Sopenharmony_ci		bo = exec->bo[i];
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci		ret = dma_resv_reserve_fences(bo->resv, 1);
66262306a36Sopenharmony_ci		if (ret) {
66362306a36Sopenharmony_ci			vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
66462306a36Sopenharmony_ci			return ret;
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	return 0;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci/* Queues a struct vc4_exec_info for execution.  If no job is
67262306a36Sopenharmony_ci * currently executing, then submits it.
67362306a36Sopenharmony_ci *
67462306a36Sopenharmony_ci * Unlike most GPUs, our hardware only handles one command list at a
67562306a36Sopenharmony_ci * time.  To queue multiple jobs at once, we'd need to edit the
67662306a36Sopenharmony_ci * previous command list to have a jump to the new one at the end, and
67762306a36Sopenharmony_ci * then bump the end address.  That's a change for a later date,
67862306a36Sopenharmony_ci * though.
67962306a36Sopenharmony_ci */
68062306a36Sopenharmony_cistatic int
68162306a36Sopenharmony_civc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec,
68262306a36Sopenharmony_ci		 struct ww_acquire_ctx *acquire_ctx,
68362306a36Sopenharmony_ci		 struct drm_syncobj *out_sync)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
68662306a36Sopenharmony_ci	struct vc4_exec_info *renderjob;
68762306a36Sopenharmony_ci	uint64_t seqno;
68862306a36Sopenharmony_ci	unsigned long irqflags;
68962306a36Sopenharmony_ci	struct vc4_fence *fence;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
69262306a36Sopenharmony_ci	if (!fence)
69362306a36Sopenharmony_ci		return -ENOMEM;
69462306a36Sopenharmony_ci	fence->dev = dev;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	seqno = ++vc4->emit_seqno;
69962306a36Sopenharmony_ci	exec->seqno = seqno;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	dma_fence_init(&fence->base, &vc4_fence_ops, &vc4->job_lock,
70262306a36Sopenharmony_ci		       vc4->dma_fence_context, exec->seqno);
70362306a36Sopenharmony_ci	fence->seqno = exec->seqno;
70462306a36Sopenharmony_ci	exec->fence = &fence->base;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (out_sync)
70762306a36Sopenharmony_ci		drm_syncobj_replace_fence(out_sync, exec->fence);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	vc4_update_bo_seqnos(exec, seqno);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	list_add_tail(&exec->head, &vc4->bin_job_list);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	/* If no bin job was executing and if the render job (if any) has the
71662306a36Sopenharmony_ci	 * same perfmon as our job attached to it (or if both jobs don't have
71762306a36Sopenharmony_ci	 * perfmon activated), then kick ours off.  Otherwise, it'll get
71862306a36Sopenharmony_ci	 * started when the previous job's flush/render done interrupt occurs.
71962306a36Sopenharmony_ci	 */
72062306a36Sopenharmony_ci	renderjob = vc4_first_render_job(vc4);
72162306a36Sopenharmony_ci	if (vc4_first_bin_job(vc4) == exec &&
72262306a36Sopenharmony_ci	    (!renderjob || renderjob->perfmon == exec->perfmon)) {
72362306a36Sopenharmony_ci		vc4_submit_next_bin_job(dev);
72462306a36Sopenharmony_ci		vc4_queue_hangcheck(dev);
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return 0;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci/**
73362306a36Sopenharmony_ci * vc4_cl_lookup_bos() - Sets up exec->bo[] with the GEM objects
73462306a36Sopenharmony_ci * referenced by the job.
73562306a36Sopenharmony_ci * @dev: DRM device
73662306a36Sopenharmony_ci * @file_priv: DRM file for this fd
73762306a36Sopenharmony_ci * @exec: V3D job being set up
73862306a36Sopenharmony_ci *
73962306a36Sopenharmony_ci * The command validator needs to reference BOs by their index within
74062306a36Sopenharmony_ci * the submitted job's BO list.  This does the validation of the job's
74162306a36Sopenharmony_ci * BO list and reference counting for the lifetime of the job.
74262306a36Sopenharmony_ci */
74362306a36Sopenharmony_cistatic int
74462306a36Sopenharmony_civc4_cl_lookup_bos(struct drm_device *dev,
74562306a36Sopenharmony_ci		  struct drm_file *file_priv,
74662306a36Sopenharmony_ci		  struct vc4_exec_info *exec)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct drm_vc4_submit_cl *args = exec->args;
74962306a36Sopenharmony_ci	int ret = 0;
75062306a36Sopenharmony_ci	int i;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	exec->bo_count = args->bo_handle_count;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (!exec->bo_count) {
75562306a36Sopenharmony_ci		/* See comment on bo_index for why we have to check
75662306a36Sopenharmony_ci		 * this.
75762306a36Sopenharmony_ci		 */
75862306a36Sopenharmony_ci		DRM_DEBUG("Rendering requires BOs to validate\n");
75962306a36Sopenharmony_ci		return -EINVAL;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	ret = drm_gem_objects_lookup(file_priv, u64_to_user_ptr(args->bo_handles),
76362306a36Sopenharmony_ci				     exec->bo_count, &exec->bo);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (ret)
76662306a36Sopenharmony_ci		goto fail_put_bo;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	for (i = 0; i < exec->bo_count; i++) {
76962306a36Sopenharmony_ci		ret = vc4_bo_inc_usecnt(to_vc4_bo(exec->bo[i]));
77062306a36Sopenharmony_ci		if (ret)
77162306a36Sopenharmony_ci			goto fail_dec_usecnt;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	return 0;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cifail_dec_usecnt:
77762306a36Sopenharmony_ci	/* Decrease usecnt on acquired objects.
77862306a36Sopenharmony_ci	 * We cannot rely on  vc4_complete_exec() to release resources here,
77962306a36Sopenharmony_ci	 * because vc4_complete_exec() has no information about which BO has
78062306a36Sopenharmony_ci	 * had its ->usecnt incremented.
78162306a36Sopenharmony_ci	 * To make things easier we just free everything explicitly and set
78262306a36Sopenharmony_ci	 * exec->bo to NULL so that vc4_complete_exec() skips the 'BO release'
78362306a36Sopenharmony_ci	 * step.
78462306a36Sopenharmony_ci	 */
78562306a36Sopenharmony_ci	for (i-- ; i >= 0; i--)
78662306a36Sopenharmony_ci		vc4_bo_dec_usecnt(to_vc4_bo(exec->bo[i]));
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cifail_put_bo:
78962306a36Sopenharmony_ci	/* Release any reference to acquired objects. */
79062306a36Sopenharmony_ci	for (i = 0; i < exec->bo_count && exec->bo[i]; i++)
79162306a36Sopenharmony_ci		drm_gem_object_put(exec->bo[i]);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	kvfree(exec->bo);
79462306a36Sopenharmony_ci	exec->bo = NULL;
79562306a36Sopenharmony_ci	return ret;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic int
79962306a36Sopenharmony_civc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	struct drm_vc4_submit_cl *args = exec->args;
80262306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
80362306a36Sopenharmony_ci	void *temp = NULL;
80462306a36Sopenharmony_ci	void *bin;
80562306a36Sopenharmony_ci	int ret = 0;
80662306a36Sopenharmony_ci	uint32_t bin_offset = 0;
80762306a36Sopenharmony_ci	uint32_t shader_rec_offset = roundup(bin_offset + args->bin_cl_size,
80862306a36Sopenharmony_ci					     16);
80962306a36Sopenharmony_ci	uint32_t uniforms_offset = shader_rec_offset + args->shader_rec_size;
81062306a36Sopenharmony_ci	uint32_t exec_size = uniforms_offset + args->uniforms_size;
81162306a36Sopenharmony_ci	uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) *
81262306a36Sopenharmony_ci					  args->shader_rec_count);
81362306a36Sopenharmony_ci	struct vc4_bo *bo;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	if (shader_rec_offset < args->bin_cl_size ||
81662306a36Sopenharmony_ci	    uniforms_offset < shader_rec_offset ||
81762306a36Sopenharmony_ci	    exec_size < uniforms_offset ||
81862306a36Sopenharmony_ci	    args->shader_rec_count >= (UINT_MAX /
81962306a36Sopenharmony_ci					  sizeof(struct vc4_shader_state)) ||
82062306a36Sopenharmony_ci	    temp_size < exec_size) {
82162306a36Sopenharmony_ci		DRM_DEBUG("overflow in exec arguments\n");
82262306a36Sopenharmony_ci		ret = -EINVAL;
82362306a36Sopenharmony_ci		goto fail;
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* Allocate space where we'll store the copied in user command lists
82762306a36Sopenharmony_ci	 * and shader records.
82862306a36Sopenharmony_ci	 *
82962306a36Sopenharmony_ci	 * We don't just copy directly into the BOs because we need to
83062306a36Sopenharmony_ci	 * read the contents back for validation, and I think the
83162306a36Sopenharmony_ci	 * bo->vaddr is uncached access.
83262306a36Sopenharmony_ci	 */
83362306a36Sopenharmony_ci	temp = kvmalloc_array(temp_size, 1, GFP_KERNEL);
83462306a36Sopenharmony_ci	if (!temp) {
83562306a36Sopenharmony_ci		DRM_ERROR("Failed to allocate storage for copying "
83662306a36Sopenharmony_ci			  "in bin/render CLs.\n");
83762306a36Sopenharmony_ci		ret = -ENOMEM;
83862306a36Sopenharmony_ci		goto fail;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci	bin = temp + bin_offset;
84162306a36Sopenharmony_ci	exec->shader_rec_u = temp + shader_rec_offset;
84262306a36Sopenharmony_ci	exec->uniforms_u = temp + uniforms_offset;
84362306a36Sopenharmony_ci	exec->shader_state = temp + exec_size;
84462306a36Sopenharmony_ci	exec->shader_state_size = args->shader_rec_count;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (copy_from_user(bin,
84762306a36Sopenharmony_ci			   u64_to_user_ptr(args->bin_cl),
84862306a36Sopenharmony_ci			   args->bin_cl_size)) {
84962306a36Sopenharmony_ci		ret = -EFAULT;
85062306a36Sopenharmony_ci		goto fail;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (copy_from_user(exec->shader_rec_u,
85462306a36Sopenharmony_ci			   u64_to_user_ptr(args->shader_rec),
85562306a36Sopenharmony_ci			   args->shader_rec_size)) {
85662306a36Sopenharmony_ci		ret = -EFAULT;
85762306a36Sopenharmony_ci		goto fail;
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (copy_from_user(exec->uniforms_u,
86162306a36Sopenharmony_ci			   u64_to_user_ptr(args->uniforms),
86262306a36Sopenharmony_ci			   args->uniforms_size)) {
86362306a36Sopenharmony_ci		ret = -EFAULT;
86462306a36Sopenharmony_ci		goto fail;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	bo = vc4_bo_create(dev, exec_size, true, VC4_BO_TYPE_BCL);
86862306a36Sopenharmony_ci	if (IS_ERR(bo)) {
86962306a36Sopenharmony_ci		DRM_ERROR("Couldn't allocate BO for binning\n");
87062306a36Sopenharmony_ci		ret = PTR_ERR(bo);
87162306a36Sopenharmony_ci		goto fail;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci	exec->exec_bo = &bo->base;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	list_add_tail(&to_vc4_bo(&exec->exec_bo->base)->unref_head,
87662306a36Sopenharmony_ci		      &exec->unref_list);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	exec->ct0ca = exec->exec_bo->dma_addr + bin_offset;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	exec->bin_u = bin;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset;
88362306a36Sopenharmony_ci	exec->shader_rec_p = exec->exec_bo->dma_addr + shader_rec_offset;
88462306a36Sopenharmony_ci	exec->shader_rec_size = args->shader_rec_size;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	exec->uniforms_v = exec->exec_bo->vaddr + uniforms_offset;
88762306a36Sopenharmony_ci	exec->uniforms_p = exec->exec_bo->dma_addr + uniforms_offset;
88862306a36Sopenharmony_ci	exec->uniforms_size = args->uniforms_size;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	ret = vc4_validate_bin_cl(dev,
89162306a36Sopenharmony_ci				  exec->exec_bo->vaddr + bin_offset,
89262306a36Sopenharmony_ci				  bin,
89362306a36Sopenharmony_ci				  exec);
89462306a36Sopenharmony_ci	if (ret)
89562306a36Sopenharmony_ci		goto fail;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	ret = vc4_validate_shader_recs(dev, exec);
89862306a36Sopenharmony_ci	if (ret)
89962306a36Sopenharmony_ci		goto fail;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (exec->found_tile_binning_mode_config_packet) {
90262306a36Sopenharmony_ci		ret = vc4_v3d_bin_bo_get(vc4, &exec->bin_bo_used);
90362306a36Sopenharmony_ci		if (ret)
90462306a36Sopenharmony_ci			goto fail;
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* Block waiting on any previous rendering into the CS's VBO,
90862306a36Sopenharmony_ci	 * IB, or textures, so that pixels are actually written by the
90962306a36Sopenharmony_ci	 * time we try to read them.
91062306a36Sopenharmony_ci	 */
91162306a36Sopenharmony_ci	ret = vc4_wait_for_seqno(dev, exec->bin_dep_seqno, ~0ull, true);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cifail:
91462306a36Sopenharmony_ci	kvfree(temp);
91562306a36Sopenharmony_ci	return ret;
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cistatic void
91962306a36Sopenharmony_civc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
92262306a36Sopenharmony_ci	unsigned long irqflags;
92362306a36Sopenharmony_ci	unsigned i;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	/* If we got force-completed because of GPU reset rather than
92662306a36Sopenharmony_ci	 * through our IRQ handler, signal the fence now.
92762306a36Sopenharmony_ci	 */
92862306a36Sopenharmony_ci	if (exec->fence) {
92962306a36Sopenharmony_ci		dma_fence_signal(exec->fence);
93062306a36Sopenharmony_ci		dma_fence_put(exec->fence);
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (exec->bo) {
93462306a36Sopenharmony_ci		for (i = 0; i < exec->bo_count; i++) {
93562306a36Sopenharmony_ci			struct vc4_bo *bo = to_vc4_bo(exec->bo[i]);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci			vc4_bo_dec_usecnt(bo);
93862306a36Sopenharmony_ci			drm_gem_object_put(exec->bo[i]);
93962306a36Sopenharmony_ci		}
94062306a36Sopenharmony_ci		kvfree(exec->bo);
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	while (!list_empty(&exec->unref_list)) {
94462306a36Sopenharmony_ci		struct vc4_bo *bo = list_first_entry(&exec->unref_list,
94562306a36Sopenharmony_ci						     struct vc4_bo, unref_head);
94662306a36Sopenharmony_ci		list_del(&bo->unref_head);
94762306a36Sopenharmony_ci		drm_gem_object_put(&bo->base.base);
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/* Free up the allocation of any bin slots we used. */
95162306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
95262306a36Sopenharmony_ci	vc4->bin_alloc_used &= ~exec->bin_slots;
95362306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/* Release the reference on the binner BO if needed. */
95662306a36Sopenharmony_ci	if (exec->bin_bo_used)
95762306a36Sopenharmony_ci		vc4_v3d_bin_bo_put(vc4);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	/* Release the reference we had on the perf monitor. */
96062306a36Sopenharmony_ci	vc4_perfmon_put(exec->perfmon);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	vc4_v3d_pm_put(vc4);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	kfree(exec);
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_civoid
96862306a36Sopenharmony_civc4_job_handle_completed(struct vc4_dev *vc4)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	unsigned long irqflags;
97162306a36Sopenharmony_ci	struct vc4_seqno_cb *cb, *cb_temp;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
97462306a36Sopenharmony_ci		return;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
97762306a36Sopenharmony_ci	while (!list_empty(&vc4->job_done_list)) {
97862306a36Sopenharmony_ci		struct vc4_exec_info *exec =
97962306a36Sopenharmony_ci			list_first_entry(&vc4->job_done_list,
98062306a36Sopenharmony_ci					 struct vc4_exec_info, head);
98162306a36Sopenharmony_ci		list_del(&exec->head);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
98462306a36Sopenharmony_ci		vc4_complete_exec(&vc4->base, exec);
98562306a36Sopenharmony_ci		spin_lock_irqsave(&vc4->job_lock, irqflags);
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	list_for_each_entry_safe(cb, cb_temp, &vc4->seqno_cb_list, work.entry) {
98962306a36Sopenharmony_ci		if (cb->seqno <= vc4->finished_seqno) {
99062306a36Sopenharmony_ci			list_del_init(&cb->work.entry);
99162306a36Sopenharmony_ci			schedule_work(&cb->work);
99262306a36Sopenharmony_ci		}
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic void vc4_seqno_cb_work(struct work_struct *work)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	struct vc4_seqno_cb *cb = container_of(work, struct vc4_seqno_cb, work);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	cb->func(cb);
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ciint vc4_queue_seqno_cb(struct drm_device *dev,
100662306a36Sopenharmony_ci		       struct vc4_seqno_cb *cb, uint64_t seqno,
100762306a36Sopenharmony_ci		       void (*func)(struct vc4_seqno_cb *cb))
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
101062306a36Sopenharmony_ci	unsigned long irqflags;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
101362306a36Sopenharmony_ci		return -ENODEV;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	cb->func = func;
101662306a36Sopenharmony_ci	INIT_WORK(&cb->work, vc4_seqno_cb_work);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	spin_lock_irqsave(&vc4->job_lock, irqflags);
101962306a36Sopenharmony_ci	if (seqno > vc4->finished_seqno) {
102062306a36Sopenharmony_ci		cb->seqno = seqno;
102162306a36Sopenharmony_ci		list_add_tail(&cb->work.entry, &vc4->seqno_cb_list);
102262306a36Sopenharmony_ci	} else {
102362306a36Sopenharmony_ci		schedule_work(&cb->work);
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	return 0;
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci/* Scheduled when any job has been completed, this walks the list of
103162306a36Sopenharmony_ci * jobs that had completed and unrefs their BOs and frees their exec
103262306a36Sopenharmony_ci * structs.
103362306a36Sopenharmony_ci */
103462306a36Sopenharmony_cistatic void
103562306a36Sopenharmony_civc4_job_done_work(struct work_struct *work)
103662306a36Sopenharmony_ci{
103762306a36Sopenharmony_ci	struct vc4_dev *vc4 =
103862306a36Sopenharmony_ci		container_of(work, struct vc4_dev, job_done_work);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	vc4_job_handle_completed(vc4);
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_cistatic int
104462306a36Sopenharmony_civc4_wait_for_seqno_ioctl_helper(struct drm_device *dev,
104562306a36Sopenharmony_ci				uint64_t seqno,
104662306a36Sopenharmony_ci				uint64_t *timeout_ns)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	unsigned long start = jiffies;
104962306a36Sopenharmony_ci	int ret = vc4_wait_for_seqno(dev, seqno, *timeout_ns, true);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	if ((ret == -EINTR || ret == -ERESTARTSYS) && *timeout_ns != ~0ull) {
105262306a36Sopenharmony_ci		uint64_t delta = jiffies_to_nsecs(jiffies - start);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci		if (*timeout_ns >= delta)
105562306a36Sopenharmony_ci			*timeout_ns -= delta;
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	return ret;
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ciint
106262306a36Sopenharmony_civc4_wait_seqno_ioctl(struct drm_device *dev, void *data,
106362306a36Sopenharmony_ci		     struct drm_file *file_priv)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
106662306a36Sopenharmony_ci	struct drm_vc4_wait_seqno *args = data;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
106962306a36Sopenharmony_ci		return -ENODEV;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno,
107262306a36Sopenharmony_ci					       &args->timeout_ns);
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ciint
107662306a36Sopenharmony_civc4_wait_bo_ioctl(struct drm_device *dev, void *data,
107762306a36Sopenharmony_ci		  struct drm_file *file_priv)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
108062306a36Sopenharmony_ci	int ret;
108162306a36Sopenharmony_ci	struct drm_vc4_wait_bo *args = data;
108262306a36Sopenharmony_ci	struct drm_gem_object *gem_obj;
108362306a36Sopenharmony_ci	struct vc4_bo *bo;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
108662306a36Sopenharmony_ci		return -ENODEV;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (args->pad != 0)
108962306a36Sopenharmony_ci		return -EINVAL;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
109262306a36Sopenharmony_ci	if (!gem_obj) {
109362306a36Sopenharmony_ci		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
109462306a36Sopenharmony_ci		return -EINVAL;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci	bo = to_vc4_bo(gem_obj);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno,
109962306a36Sopenharmony_ci					      &args->timeout_ns);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	drm_gem_object_put(gem_obj);
110262306a36Sopenharmony_ci	return ret;
110362306a36Sopenharmony_ci}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci/**
110662306a36Sopenharmony_ci * vc4_submit_cl_ioctl() - Submits a job (frame) to the VC4.
110762306a36Sopenharmony_ci * @dev: DRM device
110862306a36Sopenharmony_ci * @data: ioctl argument
110962306a36Sopenharmony_ci * @file_priv: DRM file for this fd
111062306a36Sopenharmony_ci *
111162306a36Sopenharmony_ci * This is the main entrypoint for userspace to submit a 3D frame to
111262306a36Sopenharmony_ci * the GPU.  Userspace provides the binner command list (if
111362306a36Sopenharmony_ci * applicable), and the kernel sets up the render command list to draw
111462306a36Sopenharmony_ci * to the framebuffer described in the ioctl, using the command lists
111562306a36Sopenharmony_ci * that the 3D engine's binner will produce.
111662306a36Sopenharmony_ci */
111762306a36Sopenharmony_ciint
111862306a36Sopenharmony_civc4_submit_cl_ioctl(struct drm_device *dev, void *data,
111962306a36Sopenharmony_ci		    struct drm_file *file_priv)
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
112262306a36Sopenharmony_ci	struct vc4_file *vc4file = file_priv->driver_priv;
112362306a36Sopenharmony_ci	struct drm_vc4_submit_cl *args = data;
112462306a36Sopenharmony_ci	struct drm_syncobj *out_sync = NULL;
112562306a36Sopenharmony_ci	struct vc4_exec_info *exec;
112662306a36Sopenharmony_ci	struct ww_acquire_ctx acquire_ctx;
112762306a36Sopenharmony_ci	struct dma_fence *in_fence;
112862306a36Sopenharmony_ci	int ret = 0;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	trace_vc4_submit_cl_ioctl(dev, args->bin_cl_size,
113162306a36Sopenharmony_ci				  args->shader_rec_size,
113262306a36Sopenharmony_ci				  args->bo_handle_count);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
113562306a36Sopenharmony_ci		return -ENODEV;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	if (!vc4->v3d) {
113862306a36Sopenharmony_ci		DRM_DEBUG("VC4_SUBMIT_CL with no VC4 V3D probed\n");
113962306a36Sopenharmony_ci		return -ENODEV;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	if ((args->flags & ~(VC4_SUBMIT_CL_USE_CLEAR_COLOR |
114362306a36Sopenharmony_ci			     VC4_SUBMIT_CL_FIXED_RCL_ORDER |
114462306a36Sopenharmony_ci			     VC4_SUBMIT_CL_RCL_ORDER_INCREASING_X |
114562306a36Sopenharmony_ci			     VC4_SUBMIT_CL_RCL_ORDER_INCREASING_Y)) != 0) {
114662306a36Sopenharmony_ci		DRM_DEBUG("Unknown flags: 0x%02x\n", args->flags);
114762306a36Sopenharmony_ci		return -EINVAL;
114862306a36Sopenharmony_ci	}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	if (args->pad2 != 0) {
115162306a36Sopenharmony_ci		DRM_DEBUG("Invalid pad: 0x%08x\n", args->pad2);
115262306a36Sopenharmony_ci		return -EINVAL;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	exec = kcalloc(1, sizeof(*exec), GFP_KERNEL);
115662306a36Sopenharmony_ci	if (!exec) {
115762306a36Sopenharmony_ci		DRM_ERROR("malloc failure on exec struct\n");
115862306a36Sopenharmony_ci		return -ENOMEM;
115962306a36Sopenharmony_ci	}
116062306a36Sopenharmony_ci	exec->dev = vc4;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	ret = vc4_v3d_pm_get(vc4);
116362306a36Sopenharmony_ci	if (ret) {
116462306a36Sopenharmony_ci		kfree(exec);
116562306a36Sopenharmony_ci		return ret;
116662306a36Sopenharmony_ci	}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	exec->args = args;
116962306a36Sopenharmony_ci	INIT_LIST_HEAD(&exec->unref_list);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	ret = vc4_cl_lookup_bos(dev, file_priv, exec);
117262306a36Sopenharmony_ci	if (ret)
117362306a36Sopenharmony_ci		goto fail;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	if (args->perfmonid) {
117662306a36Sopenharmony_ci		exec->perfmon = vc4_perfmon_find(vc4file,
117762306a36Sopenharmony_ci						 args->perfmonid);
117862306a36Sopenharmony_ci		if (!exec->perfmon) {
117962306a36Sopenharmony_ci			ret = -ENOENT;
118062306a36Sopenharmony_ci			goto fail;
118162306a36Sopenharmony_ci		}
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	if (args->in_sync) {
118562306a36Sopenharmony_ci		ret = drm_syncobj_find_fence(file_priv, args->in_sync,
118662306a36Sopenharmony_ci					     0, 0, &in_fence);
118762306a36Sopenharmony_ci		if (ret)
118862306a36Sopenharmony_ci			goto fail;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci		/* When the fence (or fence array) is exclusively from our
119162306a36Sopenharmony_ci		 * context we can skip the wait since jobs are executed in
119262306a36Sopenharmony_ci		 * order of their submission through this ioctl and this can
119362306a36Sopenharmony_ci		 * only have fences from a prior job.
119462306a36Sopenharmony_ci		 */
119562306a36Sopenharmony_ci		if (!dma_fence_match_context(in_fence,
119662306a36Sopenharmony_ci					     vc4->dma_fence_context)) {
119762306a36Sopenharmony_ci			ret = dma_fence_wait(in_fence, true);
119862306a36Sopenharmony_ci			if (ret) {
119962306a36Sopenharmony_ci				dma_fence_put(in_fence);
120062306a36Sopenharmony_ci				goto fail;
120162306a36Sopenharmony_ci			}
120262306a36Sopenharmony_ci		}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci		dma_fence_put(in_fence);
120562306a36Sopenharmony_ci	}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	if (exec->args->bin_cl_size != 0) {
120862306a36Sopenharmony_ci		ret = vc4_get_bcl(dev, exec);
120962306a36Sopenharmony_ci		if (ret)
121062306a36Sopenharmony_ci			goto fail;
121162306a36Sopenharmony_ci	} else {
121262306a36Sopenharmony_ci		exec->ct0ca = 0;
121362306a36Sopenharmony_ci		exec->ct0ea = 0;
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	ret = vc4_get_rcl(dev, exec);
121762306a36Sopenharmony_ci	if (ret)
121862306a36Sopenharmony_ci		goto fail;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	ret = vc4_lock_bo_reservations(dev, exec, &acquire_ctx);
122162306a36Sopenharmony_ci	if (ret)
122262306a36Sopenharmony_ci		goto fail;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (args->out_sync) {
122562306a36Sopenharmony_ci		out_sync = drm_syncobj_find(file_priv, args->out_sync);
122662306a36Sopenharmony_ci		if (!out_sync) {
122762306a36Sopenharmony_ci			ret = -EINVAL;
122862306a36Sopenharmony_ci			goto fail;
122962306a36Sopenharmony_ci		}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci		/* We replace the fence in out_sync in vc4_queue_submit since
123262306a36Sopenharmony_ci		 * the render job could execute immediately after that call.
123362306a36Sopenharmony_ci		 * If it finishes before our ioctl processing resumes the
123462306a36Sopenharmony_ci		 * render job fence could already have been freed.
123562306a36Sopenharmony_ci		 */
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	/* Clear this out of the struct we'll be putting in the queue,
123962306a36Sopenharmony_ci	 * since it's part of our stack.
124062306a36Sopenharmony_ci	 */
124162306a36Sopenharmony_ci	exec->args = NULL;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	ret = vc4_queue_submit(dev, exec, &acquire_ctx, out_sync);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/* The syncobj isn't part of the exec data and we need to free our
124662306a36Sopenharmony_ci	 * reference even if job submission failed.
124762306a36Sopenharmony_ci	 */
124862306a36Sopenharmony_ci	if (out_sync)
124962306a36Sopenharmony_ci		drm_syncobj_put(out_sync);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	if (ret)
125262306a36Sopenharmony_ci		goto fail;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	/* Return the seqno for our job. */
125562306a36Sopenharmony_ci	args->seqno = vc4->emit_seqno;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	return 0;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_cifail:
126062306a36Sopenharmony_ci	vc4_complete_exec(&vc4->base, exec);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	return ret;
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic void vc4_gem_destroy(struct drm_device *dev, void *unused);
126662306a36Sopenharmony_ciint vc4_gem_init(struct drm_device *dev)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
126962306a36Sopenharmony_ci	int ret;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
127262306a36Sopenharmony_ci		return -ENODEV;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	vc4->dma_fence_context = dma_fence_context_alloc(1);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	INIT_LIST_HEAD(&vc4->bin_job_list);
127762306a36Sopenharmony_ci	INIT_LIST_HEAD(&vc4->render_job_list);
127862306a36Sopenharmony_ci	INIT_LIST_HEAD(&vc4->job_done_list);
127962306a36Sopenharmony_ci	INIT_LIST_HEAD(&vc4->seqno_cb_list);
128062306a36Sopenharmony_ci	spin_lock_init(&vc4->job_lock);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work);
128362306a36Sopenharmony_ci	timer_setup(&vc4->hangcheck.timer, vc4_hangcheck_elapsed, 0);
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	INIT_WORK(&vc4->job_done_work, vc4_job_done_work);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	ret = drmm_mutex_init(dev, &vc4->power_lock);
128862306a36Sopenharmony_ci	if (ret)
128962306a36Sopenharmony_ci		return ret;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	INIT_LIST_HEAD(&vc4->purgeable.list);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	ret = drmm_mutex_init(dev, &vc4->purgeable.lock);
129462306a36Sopenharmony_ci	if (ret)
129562306a36Sopenharmony_ci		return ret;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	return drmm_add_action_or_reset(dev, vc4_gem_destroy, NULL);
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_cistatic void vc4_gem_destroy(struct drm_device *dev, void *unused)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	/* Waiting for exec to finish would need to be done before
130562306a36Sopenharmony_ci	 * unregistering V3D.
130662306a36Sopenharmony_ci	 */
130762306a36Sopenharmony_ci	WARN_ON(vc4->emit_seqno != vc4->finished_seqno);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	/* V3D should already have disabled its interrupt and cleared
131062306a36Sopenharmony_ci	 * the overflow allocation registers.  Now free the object.
131162306a36Sopenharmony_ci	 */
131262306a36Sopenharmony_ci	if (vc4->bin_bo) {
131362306a36Sopenharmony_ci		drm_gem_object_put(&vc4->bin_bo->base.base);
131462306a36Sopenharmony_ci		vc4->bin_bo = NULL;
131562306a36Sopenharmony_ci	}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (vc4->hang_state)
131862306a36Sopenharmony_ci		vc4_free_hang_state(dev, vc4->hang_state);
131962306a36Sopenharmony_ci}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ciint vc4_gem_madvise_ioctl(struct drm_device *dev, void *data,
132262306a36Sopenharmony_ci			  struct drm_file *file_priv)
132362306a36Sopenharmony_ci{
132462306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
132562306a36Sopenharmony_ci	struct drm_vc4_gem_madvise *args = data;
132662306a36Sopenharmony_ci	struct drm_gem_object *gem_obj;
132762306a36Sopenharmony_ci	struct vc4_bo *bo;
132862306a36Sopenharmony_ci	int ret;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	if (WARN_ON_ONCE(vc4->is_vc5))
133162306a36Sopenharmony_ci		return -ENODEV;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	switch (args->madv) {
133462306a36Sopenharmony_ci	case VC4_MADV_DONTNEED:
133562306a36Sopenharmony_ci	case VC4_MADV_WILLNEED:
133662306a36Sopenharmony_ci		break;
133762306a36Sopenharmony_ci	default:
133862306a36Sopenharmony_ci		return -EINVAL;
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	if (args->pad != 0)
134262306a36Sopenharmony_ci		return -EINVAL;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
134562306a36Sopenharmony_ci	if (!gem_obj) {
134662306a36Sopenharmony_ci		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
134762306a36Sopenharmony_ci		return -ENOENT;
134862306a36Sopenharmony_ci	}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	bo = to_vc4_bo(gem_obj);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	/* Only BOs exposed to userspace can be purged. */
135362306a36Sopenharmony_ci	if (bo->madv == __VC4_MADV_NOTSUPP) {
135462306a36Sopenharmony_ci		DRM_DEBUG("madvise not supported on this BO\n");
135562306a36Sopenharmony_ci		ret = -EINVAL;
135662306a36Sopenharmony_ci		goto out_put_gem;
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	/* Not sure it's safe to purge imported BOs. Let's just assume it's
136062306a36Sopenharmony_ci	 * not until proven otherwise.
136162306a36Sopenharmony_ci	 */
136262306a36Sopenharmony_ci	if (gem_obj->import_attach) {
136362306a36Sopenharmony_ci		DRM_DEBUG("madvise not supported on imported BOs\n");
136462306a36Sopenharmony_ci		ret = -EINVAL;
136562306a36Sopenharmony_ci		goto out_put_gem;
136662306a36Sopenharmony_ci	}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	mutex_lock(&bo->madv_lock);
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	if (args->madv == VC4_MADV_DONTNEED && bo->madv == VC4_MADV_WILLNEED &&
137162306a36Sopenharmony_ci	    !refcount_read(&bo->usecnt)) {
137262306a36Sopenharmony_ci		/* If the BO is about to be marked as purgeable, is not used
137362306a36Sopenharmony_ci		 * and is not already purgeable or purged, add it to the
137462306a36Sopenharmony_ci		 * purgeable list.
137562306a36Sopenharmony_ci		 */
137662306a36Sopenharmony_ci		vc4_bo_add_to_purgeable_pool(bo);
137762306a36Sopenharmony_ci	} else if (args->madv == VC4_MADV_WILLNEED &&
137862306a36Sopenharmony_ci		   bo->madv == VC4_MADV_DONTNEED &&
137962306a36Sopenharmony_ci		   !refcount_read(&bo->usecnt)) {
138062306a36Sopenharmony_ci		/* The BO has not been purged yet, just remove it from
138162306a36Sopenharmony_ci		 * the purgeable list.
138262306a36Sopenharmony_ci		 */
138362306a36Sopenharmony_ci		vc4_bo_remove_from_purgeable_pool(bo);
138462306a36Sopenharmony_ci	}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	/* Save the purged state. */
138762306a36Sopenharmony_ci	args->retained = bo->madv != __VC4_MADV_PURGED;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	/* Update internal madv state only if the bo was not purged. */
139062306a36Sopenharmony_ci	if (bo->madv != __VC4_MADV_PURGED)
139162306a36Sopenharmony_ci		bo->madv = args->madv;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	mutex_unlock(&bo->madv_lock);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	ret = 0;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ciout_put_gem:
139862306a36Sopenharmony_ci	drm_gem_object_put(gem_obj);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	return ret;
140162306a36Sopenharmony_ci}
1402