xref: /openbmc/linux/drivers/gpu/drm/vc4/vc4_gem.c (revision 13cf8909)
1d5b1a78aSEric Anholt /*
2d5b1a78aSEric Anholt  * Copyright © 2014 Broadcom
3d5b1a78aSEric Anholt  *
4d5b1a78aSEric Anholt  * Permission is hereby granted, free of charge, to any person obtaining a
5d5b1a78aSEric Anholt  * copy of this software and associated documentation files (the "Software"),
6d5b1a78aSEric Anholt  * to deal in the Software without restriction, including without limitation
7d5b1a78aSEric Anholt  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8d5b1a78aSEric Anholt  * and/or sell copies of the Software, and to permit persons to whom the
9d5b1a78aSEric Anholt  * Software is furnished to do so, subject to the following conditions:
10d5b1a78aSEric Anholt  *
11d5b1a78aSEric Anholt  * The above copyright notice and this permission notice (including the next
12d5b1a78aSEric Anholt  * paragraph) shall be included in all copies or substantial portions of the
13d5b1a78aSEric Anholt  * Software.
14d5b1a78aSEric Anholt  *
15d5b1a78aSEric Anholt  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16d5b1a78aSEric Anholt  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17d5b1a78aSEric Anholt  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18d5b1a78aSEric Anholt  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19d5b1a78aSEric Anholt  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20d5b1a78aSEric Anholt  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21d5b1a78aSEric Anholt  * IN THE SOFTWARE.
22d5b1a78aSEric Anholt  */
23d5b1a78aSEric Anholt 
24d5b1a78aSEric Anholt #include <linux/module.h>
25d5b1a78aSEric Anholt #include <linux/platform_device.h>
26d5b1a78aSEric Anholt #include <linux/device.h>
27d5b1a78aSEric Anholt #include <linux/io.h>
28d5b1a78aSEric Anholt 
29d5b1a78aSEric Anholt #include "uapi/drm/vc4_drm.h"
30d5b1a78aSEric Anholt #include "vc4_drv.h"
31d5b1a78aSEric Anholt #include "vc4_regs.h"
32d5b1a78aSEric Anholt #include "vc4_trace.h"
33d5b1a78aSEric Anholt 
34d5b1a78aSEric Anholt static void
35d5b1a78aSEric Anholt vc4_queue_hangcheck(struct drm_device *dev)
36d5b1a78aSEric Anholt {
37d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
38d5b1a78aSEric Anholt 
39d5b1a78aSEric Anholt 	mod_timer(&vc4->hangcheck.timer,
40d5b1a78aSEric Anholt 		  round_jiffies_up(jiffies + msecs_to_jiffies(100)));
41d5b1a78aSEric Anholt }
42d5b1a78aSEric Anholt 
4321461365SEric Anholt struct vc4_hang_state {
4421461365SEric Anholt 	struct drm_vc4_get_hang_state user_state;
4521461365SEric Anholt 
4621461365SEric Anholt 	u32 bo_count;
4721461365SEric Anholt 	struct drm_gem_object **bo;
4821461365SEric Anholt };
4921461365SEric Anholt 
5021461365SEric Anholt static void
5121461365SEric Anholt vc4_free_hang_state(struct drm_device *dev, struct vc4_hang_state *state)
5221461365SEric Anholt {
5321461365SEric Anholt 	unsigned int i;
5421461365SEric Anholt 
5521461365SEric Anholt 	mutex_lock(&dev->struct_mutex);
5621461365SEric Anholt 	for (i = 0; i < state->user_state.bo_count; i++)
5721461365SEric Anholt 		drm_gem_object_unreference(state->bo[i]);
5821461365SEric Anholt 	mutex_unlock(&dev->struct_mutex);
5921461365SEric Anholt 
6021461365SEric Anholt 	kfree(state);
6121461365SEric Anholt }
6221461365SEric Anholt 
6321461365SEric Anholt int
6421461365SEric Anholt vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
6521461365SEric Anholt 			 struct drm_file *file_priv)
6621461365SEric Anholt {
6721461365SEric Anholt 	struct drm_vc4_get_hang_state *get_state = data;
6821461365SEric Anholt 	struct drm_vc4_get_hang_state_bo *bo_state;
6921461365SEric Anholt 	struct vc4_hang_state *kernel_state;
7021461365SEric Anholt 	struct drm_vc4_get_hang_state *state;
7121461365SEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
7221461365SEric Anholt 	unsigned long irqflags;
7321461365SEric Anholt 	u32 i;
7465c4777dSDan Carpenter 	int ret = 0;
7521461365SEric Anholt 
7621461365SEric Anholt 	spin_lock_irqsave(&vc4->job_lock, irqflags);
7721461365SEric Anholt 	kernel_state = vc4->hang_state;
7821461365SEric Anholt 	if (!kernel_state) {
7921461365SEric Anholt 		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
8021461365SEric Anholt 		return -ENOENT;
8121461365SEric Anholt 	}
8221461365SEric Anholt 	state = &kernel_state->user_state;
8321461365SEric Anholt 
8421461365SEric Anholt 	/* If the user's array isn't big enough, just return the
8521461365SEric Anholt 	 * required array size.
8621461365SEric Anholt 	 */
8721461365SEric Anholt 	if (get_state->bo_count < state->bo_count) {
8821461365SEric Anholt 		get_state->bo_count = state->bo_count;
8921461365SEric Anholt 		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
9021461365SEric Anholt 		return 0;
9121461365SEric Anholt 	}
9221461365SEric Anholt 
9321461365SEric Anholt 	vc4->hang_state = NULL;
9421461365SEric Anholt 	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
9521461365SEric Anholt 
9621461365SEric Anholt 	/* Save the user's BO pointer, so we don't stomp it with the memcpy. */
9721461365SEric Anholt 	state->bo = get_state->bo;
9821461365SEric Anholt 	memcpy(get_state, state, sizeof(*state));
9921461365SEric Anholt 
10021461365SEric Anholt 	bo_state = kcalloc(state->bo_count, sizeof(*bo_state), GFP_KERNEL);
10121461365SEric Anholt 	if (!bo_state) {
10221461365SEric Anholt 		ret = -ENOMEM;
10321461365SEric Anholt 		goto err_free;
10421461365SEric Anholt 	}
10521461365SEric Anholt 
10621461365SEric Anholt 	for (i = 0; i < state->bo_count; i++) {
10721461365SEric Anholt 		struct vc4_bo *vc4_bo = to_vc4_bo(kernel_state->bo[i]);
10821461365SEric Anholt 		u32 handle;
10921461365SEric Anholt 
11021461365SEric Anholt 		ret = drm_gem_handle_create(file_priv, kernel_state->bo[i],
11121461365SEric Anholt 					    &handle);
11221461365SEric Anholt 
11321461365SEric Anholt 		if (ret) {
11421461365SEric Anholt 			state->bo_count = i - 1;
11521461365SEric Anholt 			goto err;
11621461365SEric Anholt 		}
11721461365SEric Anholt 		bo_state[i].handle = handle;
11821461365SEric Anholt 		bo_state[i].paddr = vc4_bo->base.paddr;
11921461365SEric Anholt 		bo_state[i].size = vc4_bo->base.base.size;
12021461365SEric Anholt 	}
12121461365SEric Anholt 
12265c4777dSDan Carpenter 	if (copy_to_user((void __user *)(uintptr_t)get_state->bo,
12321461365SEric Anholt 			 bo_state,
12465c4777dSDan Carpenter 			 state->bo_count * sizeof(*bo_state)))
12565c4777dSDan Carpenter 		ret = -EFAULT;
12665c4777dSDan Carpenter 
12721461365SEric Anholt 	kfree(bo_state);
12821461365SEric Anholt 
12921461365SEric Anholt err_free:
13021461365SEric Anholt 
13121461365SEric Anholt 	vc4_free_hang_state(dev, kernel_state);
13221461365SEric Anholt 
13321461365SEric Anholt err:
13421461365SEric Anholt 	return ret;
13521461365SEric Anholt }
13621461365SEric Anholt 
13721461365SEric Anholt static void
13821461365SEric Anholt vc4_save_hang_state(struct drm_device *dev)
13921461365SEric Anholt {
14021461365SEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
14121461365SEric Anholt 	struct drm_vc4_get_hang_state *state;
14221461365SEric Anholt 	struct vc4_hang_state *kernel_state;
14321461365SEric Anholt 	struct vc4_exec_info *exec;
14421461365SEric Anholt 	struct vc4_bo *bo;
14521461365SEric Anholt 	unsigned long irqflags;
14621461365SEric Anholt 	unsigned int i, unref_list_count;
14721461365SEric Anholt 
1487e5082fbSDan Carpenter 	kernel_state = kcalloc(1, sizeof(*kernel_state), GFP_KERNEL);
14921461365SEric Anholt 	if (!kernel_state)
15021461365SEric Anholt 		return;
15121461365SEric Anholt 
15221461365SEric Anholt 	state = &kernel_state->user_state;
15321461365SEric Anholt 
15421461365SEric Anholt 	spin_lock_irqsave(&vc4->job_lock, irqflags);
15521461365SEric Anholt 	exec = vc4_first_job(vc4);
15621461365SEric Anholt 	if (!exec) {
15721461365SEric Anholt 		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
15821461365SEric Anholt 		return;
15921461365SEric Anholt 	}
16021461365SEric Anholt 
16121461365SEric Anholt 	unref_list_count = 0;
16221461365SEric Anholt 	list_for_each_entry(bo, &exec->unref_list, unref_head)
16321461365SEric Anholt 		unref_list_count++;
16421461365SEric Anholt 
16521461365SEric Anholt 	state->bo_count = exec->bo_count + unref_list_count;
16621461365SEric Anholt 	kernel_state->bo = kcalloc(state->bo_count, sizeof(*kernel_state->bo),
16721461365SEric Anholt 				   GFP_ATOMIC);
16821461365SEric Anholt 	if (!kernel_state->bo) {
16921461365SEric Anholt 		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
17021461365SEric Anholt 		return;
17121461365SEric Anholt 	}
17221461365SEric Anholt 
17321461365SEric Anholt 	for (i = 0; i < exec->bo_count; i++) {
17421461365SEric Anholt 		drm_gem_object_reference(&exec->bo[i]->base);
17521461365SEric Anholt 		kernel_state->bo[i] = &exec->bo[i]->base;
17621461365SEric Anholt 	}
17721461365SEric Anholt 
17821461365SEric Anholt 	list_for_each_entry(bo, &exec->unref_list, unref_head) {
17921461365SEric Anholt 		drm_gem_object_reference(&bo->base.base);
18021461365SEric Anholt 		kernel_state->bo[i] = &bo->base.base;
18121461365SEric Anholt 		i++;
18221461365SEric Anholt 	}
18321461365SEric Anholt 
18421461365SEric Anholt 	state->start_bin = exec->ct0ca;
18521461365SEric Anholt 	state->start_render = exec->ct1ca;
18621461365SEric Anholt 
18721461365SEric Anholt 	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
18821461365SEric Anholt 
18921461365SEric Anholt 	state->ct0ca = V3D_READ(V3D_CTNCA(0));
19021461365SEric Anholt 	state->ct0ea = V3D_READ(V3D_CTNEA(0));
19121461365SEric Anholt 
19221461365SEric Anholt 	state->ct1ca = V3D_READ(V3D_CTNCA(1));
19321461365SEric Anholt 	state->ct1ea = V3D_READ(V3D_CTNEA(1));
19421461365SEric Anholt 
19521461365SEric Anholt 	state->ct0cs = V3D_READ(V3D_CTNCS(0));
19621461365SEric Anholt 	state->ct1cs = V3D_READ(V3D_CTNCS(1));
19721461365SEric Anholt 
19821461365SEric Anholt 	state->ct0ra0 = V3D_READ(V3D_CT00RA0);
19921461365SEric Anholt 	state->ct1ra0 = V3D_READ(V3D_CT01RA0);
20021461365SEric Anholt 
20121461365SEric Anholt 	state->bpca = V3D_READ(V3D_BPCA);
20221461365SEric Anholt 	state->bpcs = V3D_READ(V3D_BPCS);
20321461365SEric Anholt 	state->bpoa = V3D_READ(V3D_BPOA);
20421461365SEric Anholt 	state->bpos = V3D_READ(V3D_BPOS);
20521461365SEric Anholt 
20621461365SEric Anholt 	state->vpmbase = V3D_READ(V3D_VPMBASE);
20721461365SEric Anholt 
20821461365SEric Anholt 	state->dbge = V3D_READ(V3D_DBGE);
20921461365SEric Anholt 	state->fdbgo = V3D_READ(V3D_FDBGO);
21021461365SEric Anholt 	state->fdbgb = V3D_READ(V3D_FDBGB);
21121461365SEric Anholt 	state->fdbgr = V3D_READ(V3D_FDBGR);
21221461365SEric Anholt 	state->fdbgs = V3D_READ(V3D_FDBGS);
21321461365SEric Anholt 	state->errstat = V3D_READ(V3D_ERRSTAT);
21421461365SEric Anholt 
21521461365SEric Anholt 	spin_lock_irqsave(&vc4->job_lock, irqflags);
21621461365SEric Anholt 	if (vc4->hang_state) {
21721461365SEric Anholt 		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
21821461365SEric Anholt 		vc4_free_hang_state(dev, kernel_state);
21921461365SEric Anholt 	} else {
22021461365SEric Anholt 		vc4->hang_state = kernel_state;
22121461365SEric Anholt 		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
22221461365SEric Anholt 	}
22321461365SEric Anholt }
22421461365SEric Anholt 
225d5b1a78aSEric Anholt static void
226d5b1a78aSEric Anholt vc4_reset(struct drm_device *dev)
227d5b1a78aSEric Anholt {
228d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
229d5b1a78aSEric Anholt 
230d5b1a78aSEric Anholt 	DRM_INFO("Resetting GPU.\n");
231d5b1a78aSEric Anholt 	vc4_v3d_set_power(vc4, false);
232d5b1a78aSEric Anholt 	vc4_v3d_set_power(vc4, true);
233d5b1a78aSEric Anholt 
234d5b1a78aSEric Anholt 	vc4_irq_reset(dev);
235d5b1a78aSEric Anholt 
236d5b1a78aSEric Anholt 	/* Rearm the hangcheck -- another job might have been waiting
237d5b1a78aSEric Anholt 	 * for our hung one to get kicked off, and vc4_irq_reset()
238d5b1a78aSEric Anholt 	 * would have started it.
239d5b1a78aSEric Anholt 	 */
240d5b1a78aSEric Anholt 	vc4_queue_hangcheck(dev);
241d5b1a78aSEric Anholt }
242d5b1a78aSEric Anholt 
243d5b1a78aSEric Anholt static void
244d5b1a78aSEric Anholt vc4_reset_work(struct work_struct *work)
245d5b1a78aSEric Anholt {
246d5b1a78aSEric Anholt 	struct vc4_dev *vc4 =
247d5b1a78aSEric Anholt 		container_of(work, struct vc4_dev, hangcheck.reset_work);
248d5b1a78aSEric Anholt 
24921461365SEric Anholt 	vc4_save_hang_state(vc4->dev);
25021461365SEric Anholt 
251d5b1a78aSEric Anholt 	vc4_reset(vc4->dev);
252d5b1a78aSEric Anholt }
253d5b1a78aSEric Anholt 
254d5b1a78aSEric Anholt static void
255d5b1a78aSEric Anholt vc4_hangcheck_elapsed(unsigned long data)
256d5b1a78aSEric Anholt {
257d5b1a78aSEric Anholt 	struct drm_device *dev = (struct drm_device *)data;
258d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
259d5b1a78aSEric Anholt 	uint32_t ct0ca, ct1ca;
260d5b1a78aSEric Anholt 
261d5b1a78aSEric Anholt 	/* If idle, we can stop watching for hangs. */
262d5b1a78aSEric Anholt 	if (list_empty(&vc4->job_list))
263d5b1a78aSEric Anholt 		return;
264d5b1a78aSEric Anholt 
265d5b1a78aSEric Anholt 	ct0ca = V3D_READ(V3D_CTNCA(0));
266d5b1a78aSEric Anholt 	ct1ca = V3D_READ(V3D_CTNCA(1));
267d5b1a78aSEric Anholt 
268d5b1a78aSEric Anholt 	/* If we've made any progress in execution, rearm the timer
269d5b1a78aSEric Anholt 	 * and wait.
270d5b1a78aSEric Anholt 	 */
271d5b1a78aSEric Anholt 	if (ct0ca != vc4->hangcheck.last_ct0ca ||
272d5b1a78aSEric Anholt 	    ct1ca != vc4->hangcheck.last_ct1ca) {
273d5b1a78aSEric Anholt 		vc4->hangcheck.last_ct0ca = ct0ca;
274d5b1a78aSEric Anholt 		vc4->hangcheck.last_ct1ca = ct1ca;
275d5b1a78aSEric Anholt 		vc4_queue_hangcheck(dev);
276d5b1a78aSEric Anholt 		return;
277d5b1a78aSEric Anholt 	}
278d5b1a78aSEric Anholt 
279d5b1a78aSEric Anholt 	/* We've gone too long with no progress, reset.  This has to
280d5b1a78aSEric Anholt 	 * be done from a work struct, since resetting can sleep and
281d5b1a78aSEric Anholt 	 * this timer hook isn't allowed to.
282d5b1a78aSEric Anholt 	 */
283d5b1a78aSEric Anholt 	schedule_work(&vc4->hangcheck.reset_work);
284d5b1a78aSEric Anholt }
285d5b1a78aSEric Anholt 
286d5b1a78aSEric Anholt static void
287d5b1a78aSEric Anholt submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end)
288d5b1a78aSEric Anholt {
289d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
290d5b1a78aSEric Anholt 
291d5b1a78aSEric Anholt 	/* Set the current and end address of the control list.
292d5b1a78aSEric Anholt 	 * Writing the end register is what starts the job.
293d5b1a78aSEric Anholt 	 */
294d5b1a78aSEric Anholt 	V3D_WRITE(V3D_CTNCA(thread), start);
295d5b1a78aSEric Anholt 	V3D_WRITE(V3D_CTNEA(thread), end);
296d5b1a78aSEric Anholt }
297d5b1a78aSEric Anholt 
298d5b1a78aSEric Anholt int
299d5b1a78aSEric Anholt vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns,
300d5b1a78aSEric Anholt 		   bool interruptible)
301d5b1a78aSEric Anholt {
302d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
303d5b1a78aSEric Anholt 	int ret = 0;
304d5b1a78aSEric Anholt 	unsigned long timeout_expire;
305d5b1a78aSEric Anholt 	DEFINE_WAIT(wait);
306d5b1a78aSEric Anholt 
307d5b1a78aSEric Anholt 	if (vc4->finished_seqno >= seqno)
308d5b1a78aSEric Anholt 		return 0;
309d5b1a78aSEric Anholt 
310d5b1a78aSEric Anholt 	if (timeout_ns == 0)
311d5b1a78aSEric Anholt 		return -ETIME;
312d5b1a78aSEric Anholt 
313d5b1a78aSEric Anholt 	timeout_expire = jiffies + nsecs_to_jiffies(timeout_ns);
314d5b1a78aSEric Anholt 
315d5b1a78aSEric Anholt 	trace_vc4_wait_for_seqno_begin(dev, seqno, timeout_ns);
316d5b1a78aSEric Anholt 	for (;;) {
317d5b1a78aSEric Anholt 		prepare_to_wait(&vc4->job_wait_queue, &wait,
318d5b1a78aSEric Anholt 				interruptible ? TASK_INTERRUPTIBLE :
319d5b1a78aSEric Anholt 				TASK_UNINTERRUPTIBLE);
320d5b1a78aSEric Anholt 
321d5b1a78aSEric Anholt 		if (interruptible && signal_pending(current)) {
322d5b1a78aSEric Anholt 			ret = -ERESTARTSYS;
323d5b1a78aSEric Anholt 			break;
324d5b1a78aSEric Anholt 		}
325d5b1a78aSEric Anholt 
326d5b1a78aSEric Anholt 		if (vc4->finished_seqno >= seqno)
327d5b1a78aSEric Anholt 			break;
328d5b1a78aSEric Anholt 
329d5b1a78aSEric Anholt 		if (timeout_ns != ~0ull) {
330d5b1a78aSEric Anholt 			if (time_after_eq(jiffies, timeout_expire)) {
331d5b1a78aSEric Anholt 				ret = -ETIME;
332d5b1a78aSEric Anholt 				break;
333d5b1a78aSEric Anholt 			}
334d5b1a78aSEric Anholt 			schedule_timeout(timeout_expire - jiffies);
335d5b1a78aSEric Anholt 		} else {
336d5b1a78aSEric Anholt 			schedule();
337d5b1a78aSEric Anholt 		}
338d5b1a78aSEric Anholt 	}
339d5b1a78aSEric Anholt 
340d5b1a78aSEric Anholt 	finish_wait(&vc4->job_wait_queue, &wait);
341d5b1a78aSEric Anholt 	trace_vc4_wait_for_seqno_end(dev, seqno);
342d5b1a78aSEric Anholt 
34313cf8909SEric Anholt 	if (ret && ret != -ERESTARTSYS)
344d5b1a78aSEric Anholt 		DRM_ERROR("timeout waiting for render thread idle\n");
345d5b1a78aSEric Anholt 
34613cf8909SEric Anholt 	return ret;
347d5b1a78aSEric Anholt }
348d5b1a78aSEric Anholt 
349d5b1a78aSEric Anholt static void
350d5b1a78aSEric Anholt vc4_flush_caches(struct drm_device *dev)
351d5b1a78aSEric Anholt {
352d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
353d5b1a78aSEric Anholt 
354d5b1a78aSEric Anholt 	/* Flush the GPU L2 caches.  These caches sit on top of system
355d5b1a78aSEric Anholt 	 * L3 (the 128kb or so shared with the CPU), and are
356d5b1a78aSEric Anholt 	 * non-allocating in the L3.
357d5b1a78aSEric Anholt 	 */
358d5b1a78aSEric Anholt 	V3D_WRITE(V3D_L2CACTL,
359d5b1a78aSEric Anholt 		  V3D_L2CACTL_L2CCLR);
360d5b1a78aSEric Anholt 
361d5b1a78aSEric Anholt 	V3D_WRITE(V3D_SLCACTL,
362d5b1a78aSEric Anholt 		  VC4_SET_FIELD(0xf, V3D_SLCACTL_T1CC) |
363d5b1a78aSEric Anholt 		  VC4_SET_FIELD(0xf, V3D_SLCACTL_T0CC) |
364d5b1a78aSEric Anholt 		  VC4_SET_FIELD(0xf, V3D_SLCACTL_UCC) |
365d5b1a78aSEric Anholt 		  VC4_SET_FIELD(0xf, V3D_SLCACTL_ICC));
366d5b1a78aSEric Anholt }
367d5b1a78aSEric Anholt 
368d5b1a78aSEric Anholt /* Sets the registers for the next job to be actually be executed in
369d5b1a78aSEric Anholt  * the hardware.
370d5b1a78aSEric Anholt  *
371d5b1a78aSEric Anholt  * The job_lock should be held during this.
372d5b1a78aSEric Anholt  */
373d5b1a78aSEric Anholt void
374d5b1a78aSEric Anholt vc4_submit_next_job(struct drm_device *dev)
375d5b1a78aSEric Anholt {
376d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
377d5b1a78aSEric Anholt 	struct vc4_exec_info *exec = vc4_first_job(vc4);
378d5b1a78aSEric Anholt 
379d5b1a78aSEric Anholt 	if (!exec)
380d5b1a78aSEric Anholt 		return;
381d5b1a78aSEric Anholt 
382d5b1a78aSEric Anholt 	vc4_flush_caches(dev);
383d5b1a78aSEric Anholt 
384d5b1a78aSEric Anholt 	/* Disable the binner's pre-loaded overflow memory address */
385d5b1a78aSEric Anholt 	V3D_WRITE(V3D_BPOA, 0);
386d5b1a78aSEric Anholt 	V3D_WRITE(V3D_BPOS, 0);
387d5b1a78aSEric Anholt 
388d5b1a78aSEric Anholt 	if (exec->ct0ca != exec->ct0ea)
389d5b1a78aSEric Anholt 		submit_cl(dev, 0, exec->ct0ca, exec->ct0ea);
390d5b1a78aSEric Anholt 	submit_cl(dev, 1, exec->ct1ca, exec->ct1ea);
391d5b1a78aSEric Anholt }
392d5b1a78aSEric Anholt 
393d5b1a78aSEric Anholt static void
394d5b1a78aSEric Anholt vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
395d5b1a78aSEric Anholt {
396d5b1a78aSEric Anholt 	struct vc4_bo *bo;
397d5b1a78aSEric Anholt 	unsigned i;
398d5b1a78aSEric Anholt 
399d5b1a78aSEric Anholt 	for (i = 0; i < exec->bo_count; i++) {
400d5b1a78aSEric Anholt 		bo = to_vc4_bo(&exec->bo[i]->base);
401d5b1a78aSEric Anholt 		bo->seqno = seqno;
402d5b1a78aSEric Anholt 	}
403d5b1a78aSEric Anholt 
404d5b1a78aSEric Anholt 	list_for_each_entry(bo, &exec->unref_list, unref_head) {
405d5b1a78aSEric Anholt 		bo->seqno = seqno;
406d5b1a78aSEric Anholt 	}
407d5b1a78aSEric Anholt }
408d5b1a78aSEric Anholt 
409d5b1a78aSEric Anholt /* Queues a struct vc4_exec_info for execution.  If no job is
410d5b1a78aSEric Anholt  * currently executing, then submits it.
411d5b1a78aSEric Anholt  *
412d5b1a78aSEric Anholt  * Unlike most GPUs, our hardware only handles one command list at a
413d5b1a78aSEric Anholt  * time.  To queue multiple jobs at once, we'd need to edit the
414d5b1a78aSEric Anholt  * previous command list to have a jump to the new one at the end, and
415d5b1a78aSEric Anholt  * then bump the end address.  That's a change for a later date,
416d5b1a78aSEric Anholt  * though.
417d5b1a78aSEric Anholt  */
418d5b1a78aSEric Anholt static void
419d5b1a78aSEric Anholt vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
420d5b1a78aSEric Anholt {
421d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
422d5b1a78aSEric Anholt 	uint64_t seqno;
423d5b1a78aSEric Anholt 	unsigned long irqflags;
424d5b1a78aSEric Anholt 
425d5b1a78aSEric Anholt 	spin_lock_irqsave(&vc4->job_lock, irqflags);
426d5b1a78aSEric Anholt 
427d5b1a78aSEric Anholt 	seqno = ++vc4->emit_seqno;
428d5b1a78aSEric Anholt 	exec->seqno = seqno;
429d5b1a78aSEric Anholt 	vc4_update_bo_seqnos(exec, seqno);
430d5b1a78aSEric Anholt 
431d5b1a78aSEric Anholt 	list_add_tail(&exec->head, &vc4->job_list);
432d5b1a78aSEric Anholt 
433d5b1a78aSEric Anholt 	/* If no job was executing, kick ours off.  Otherwise, it'll
434d5b1a78aSEric Anholt 	 * get started when the previous job's frame done interrupt
435d5b1a78aSEric Anholt 	 * occurs.
436d5b1a78aSEric Anholt 	 */
437d5b1a78aSEric Anholt 	if (vc4_first_job(vc4) == exec) {
438d5b1a78aSEric Anholt 		vc4_submit_next_job(dev);
439d5b1a78aSEric Anholt 		vc4_queue_hangcheck(dev);
440d5b1a78aSEric Anholt 	}
441d5b1a78aSEric Anholt 
442d5b1a78aSEric Anholt 	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
443d5b1a78aSEric Anholt }
444d5b1a78aSEric Anholt 
445d5b1a78aSEric Anholt /**
446d5b1a78aSEric Anholt  * Looks up a bunch of GEM handles for BOs and stores the array for
447d5b1a78aSEric Anholt  * use in the command validator that actually writes relocated
448d5b1a78aSEric Anholt  * addresses pointing to them.
449d5b1a78aSEric Anholt  */
450d5b1a78aSEric Anholt static int
451d5b1a78aSEric Anholt vc4_cl_lookup_bos(struct drm_device *dev,
452d5b1a78aSEric Anholt 		  struct drm_file *file_priv,
453d5b1a78aSEric Anholt 		  struct vc4_exec_info *exec)
454d5b1a78aSEric Anholt {
455d5b1a78aSEric Anholt 	struct drm_vc4_submit_cl *args = exec->args;
456d5b1a78aSEric Anholt 	uint32_t *handles;
457d5b1a78aSEric Anholt 	int ret = 0;
458d5b1a78aSEric Anholt 	int i;
459d5b1a78aSEric Anholt 
460d5b1a78aSEric Anholt 	exec->bo_count = args->bo_handle_count;
461d5b1a78aSEric Anholt 
462d5b1a78aSEric Anholt 	if (!exec->bo_count) {
463d5b1a78aSEric Anholt 		/* See comment on bo_index for why we have to check
464d5b1a78aSEric Anholt 		 * this.
465d5b1a78aSEric Anholt 		 */
466d5b1a78aSEric Anholt 		DRM_ERROR("Rendering requires BOs to validate\n");
467d5b1a78aSEric Anholt 		return -EINVAL;
468d5b1a78aSEric Anholt 	}
469d5b1a78aSEric Anholt 
470d5b1a78aSEric Anholt 	exec->bo = kcalloc(exec->bo_count, sizeof(struct drm_gem_cma_object *),
471d5b1a78aSEric Anholt 			   GFP_KERNEL);
472d5b1a78aSEric Anholt 	if (!exec->bo) {
473d5b1a78aSEric Anholt 		DRM_ERROR("Failed to allocate validated BO pointers\n");
474d5b1a78aSEric Anholt 		return -ENOMEM;
475d5b1a78aSEric Anholt 	}
476d5b1a78aSEric Anholt 
477d5b1a78aSEric Anholt 	handles = drm_malloc_ab(exec->bo_count, sizeof(uint32_t));
478d5b1a78aSEric Anholt 	if (!handles) {
479d5b1a78aSEric Anholt 		DRM_ERROR("Failed to allocate incoming GEM handles\n");
480d5b1a78aSEric Anholt 		goto fail;
481d5b1a78aSEric Anholt 	}
482d5b1a78aSEric Anholt 
483d5b1a78aSEric Anholt 	ret = copy_from_user(handles,
484d5b1a78aSEric Anholt 			     (void __user *)(uintptr_t)args->bo_handles,
485d5b1a78aSEric Anholt 			     exec->bo_count * sizeof(uint32_t));
486d5b1a78aSEric Anholt 	if (ret) {
487d5b1a78aSEric Anholt 		DRM_ERROR("Failed to copy in GEM handles\n");
488d5b1a78aSEric Anholt 		goto fail;
489d5b1a78aSEric Anholt 	}
490d5b1a78aSEric Anholt 
491d5b1a78aSEric Anholt 	spin_lock(&file_priv->table_lock);
492d5b1a78aSEric Anholt 	for (i = 0; i < exec->bo_count; i++) {
493d5b1a78aSEric Anholt 		struct drm_gem_object *bo = idr_find(&file_priv->object_idr,
494d5b1a78aSEric Anholt 						     handles[i]);
495d5b1a78aSEric Anholt 		if (!bo) {
496d5b1a78aSEric Anholt 			DRM_ERROR("Failed to look up GEM BO %d: %d\n",
497d5b1a78aSEric Anholt 				  i, handles[i]);
498d5b1a78aSEric Anholt 			ret = -EINVAL;
499d5b1a78aSEric Anholt 			spin_unlock(&file_priv->table_lock);
500d5b1a78aSEric Anholt 			goto fail;
501d5b1a78aSEric Anholt 		}
502d5b1a78aSEric Anholt 		drm_gem_object_reference(bo);
503d5b1a78aSEric Anholt 		exec->bo[i] = (struct drm_gem_cma_object *)bo;
504d5b1a78aSEric Anholt 	}
505d5b1a78aSEric Anholt 	spin_unlock(&file_priv->table_lock);
506d5b1a78aSEric Anholt 
507d5b1a78aSEric Anholt fail:
508d5b1a78aSEric Anholt 	kfree(handles);
509d5b1a78aSEric Anholt 	return 0;
510d5b1a78aSEric Anholt }
511d5b1a78aSEric Anholt 
512d5b1a78aSEric Anholt static int
513d5b1a78aSEric Anholt vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec)
514d5b1a78aSEric Anholt {
515d5b1a78aSEric Anholt 	struct drm_vc4_submit_cl *args = exec->args;
516d5b1a78aSEric Anholt 	void *temp = NULL;
517d5b1a78aSEric Anholt 	void *bin;
518d5b1a78aSEric Anholt 	int ret = 0;
519d5b1a78aSEric Anholt 	uint32_t bin_offset = 0;
520d5b1a78aSEric Anholt 	uint32_t shader_rec_offset = roundup(bin_offset + args->bin_cl_size,
521d5b1a78aSEric Anholt 					     16);
522d5b1a78aSEric Anholt 	uint32_t uniforms_offset = shader_rec_offset + args->shader_rec_size;
523d5b1a78aSEric Anholt 	uint32_t exec_size = uniforms_offset + args->uniforms_size;
524d5b1a78aSEric Anholt 	uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) *
525d5b1a78aSEric Anholt 					  args->shader_rec_count);
526d5b1a78aSEric Anholt 	struct vc4_bo *bo;
527d5b1a78aSEric Anholt 
528d5b1a78aSEric Anholt 	if (uniforms_offset < shader_rec_offset ||
529d5b1a78aSEric Anholt 	    exec_size < uniforms_offset ||
530d5b1a78aSEric Anholt 	    args->shader_rec_count >= (UINT_MAX /
531d5b1a78aSEric Anholt 					  sizeof(struct vc4_shader_state)) ||
532d5b1a78aSEric Anholt 	    temp_size < exec_size) {
533d5b1a78aSEric Anholt 		DRM_ERROR("overflow in exec arguments\n");
534d5b1a78aSEric Anholt 		goto fail;
535d5b1a78aSEric Anholt 	}
536d5b1a78aSEric Anholt 
537d5b1a78aSEric Anholt 	/* Allocate space where we'll store the copied in user command lists
538d5b1a78aSEric Anholt 	 * and shader records.
539d5b1a78aSEric Anholt 	 *
540d5b1a78aSEric Anholt 	 * We don't just copy directly into the BOs because we need to
541d5b1a78aSEric Anholt 	 * read the contents back for validation, and I think the
542d5b1a78aSEric Anholt 	 * bo->vaddr is uncached access.
543d5b1a78aSEric Anholt 	 */
544d5b1a78aSEric Anholt 	temp = kmalloc(temp_size, GFP_KERNEL);
545d5b1a78aSEric Anholt 	if (!temp) {
546d5b1a78aSEric Anholt 		DRM_ERROR("Failed to allocate storage for copying "
547d5b1a78aSEric Anholt 			  "in bin/render CLs.\n");
548d5b1a78aSEric Anholt 		ret = -ENOMEM;
549d5b1a78aSEric Anholt 		goto fail;
550d5b1a78aSEric Anholt 	}
551d5b1a78aSEric Anholt 	bin = temp + bin_offset;
552d5b1a78aSEric Anholt 	exec->shader_rec_u = temp + shader_rec_offset;
553d5b1a78aSEric Anholt 	exec->uniforms_u = temp + uniforms_offset;
554d5b1a78aSEric Anholt 	exec->shader_state = temp + exec_size;
555d5b1a78aSEric Anholt 	exec->shader_state_size = args->shader_rec_count;
556d5b1a78aSEric Anholt 
55765c4777dSDan Carpenter 	if (copy_from_user(bin,
558d5b1a78aSEric Anholt 			   (void __user *)(uintptr_t)args->bin_cl,
55965c4777dSDan Carpenter 			   args->bin_cl_size)) {
56065c4777dSDan Carpenter 		ret = -EFAULT;
561d5b1a78aSEric Anholt 		goto fail;
562d5b1a78aSEric Anholt 	}
563d5b1a78aSEric Anholt 
56465c4777dSDan Carpenter 	if (copy_from_user(exec->shader_rec_u,
565d5b1a78aSEric Anholt 			   (void __user *)(uintptr_t)args->shader_rec,
56665c4777dSDan Carpenter 			   args->shader_rec_size)) {
56765c4777dSDan Carpenter 		ret = -EFAULT;
568d5b1a78aSEric Anholt 		goto fail;
569d5b1a78aSEric Anholt 	}
570d5b1a78aSEric Anholt 
57165c4777dSDan Carpenter 	if (copy_from_user(exec->uniforms_u,
572d5b1a78aSEric Anholt 			   (void __user *)(uintptr_t)args->uniforms,
57365c4777dSDan Carpenter 			   args->uniforms_size)) {
57465c4777dSDan Carpenter 		ret = -EFAULT;
575d5b1a78aSEric Anholt 		goto fail;
576d5b1a78aSEric Anholt 	}
577d5b1a78aSEric Anholt 
578d5b1a78aSEric Anholt 	bo = vc4_bo_create(dev, exec_size, true);
5792c68f1fcSEric Anholt 	if (IS_ERR(bo)) {
580d5b1a78aSEric Anholt 		DRM_ERROR("Couldn't allocate BO for binning\n");
5812c68f1fcSEric Anholt 		ret = PTR_ERR(bo);
582d5b1a78aSEric Anholt 		goto fail;
583d5b1a78aSEric Anholt 	}
584d5b1a78aSEric Anholt 	exec->exec_bo = &bo->base;
585d5b1a78aSEric Anholt 
586d5b1a78aSEric Anholt 	list_add_tail(&to_vc4_bo(&exec->exec_bo->base)->unref_head,
587d5b1a78aSEric Anholt 		      &exec->unref_list);
588d5b1a78aSEric Anholt 
589d5b1a78aSEric Anholt 	exec->ct0ca = exec->exec_bo->paddr + bin_offset;
590d5b1a78aSEric Anholt 
591d5b1a78aSEric Anholt 	exec->bin_u = bin;
592d5b1a78aSEric Anholt 
593d5b1a78aSEric Anholt 	exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset;
594d5b1a78aSEric Anholt 	exec->shader_rec_p = exec->exec_bo->paddr + shader_rec_offset;
595d5b1a78aSEric Anholt 	exec->shader_rec_size = args->shader_rec_size;
596d5b1a78aSEric Anholt 
597d5b1a78aSEric Anholt 	exec->uniforms_v = exec->exec_bo->vaddr + uniforms_offset;
598d5b1a78aSEric Anholt 	exec->uniforms_p = exec->exec_bo->paddr + uniforms_offset;
599d5b1a78aSEric Anholt 	exec->uniforms_size = args->uniforms_size;
600d5b1a78aSEric Anholt 
601d5b1a78aSEric Anholt 	ret = vc4_validate_bin_cl(dev,
602d5b1a78aSEric Anholt 				  exec->exec_bo->vaddr + bin_offset,
603d5b1a78aSEric Anholt 				  bin,
604d5b1a78aSEric Anholt 				  exec);
605d5b1a78aSEric Anholt 	if (ret)
606d5b1a78aSEric Anholt 		goto fail;
607d5b1a78aSEric Anholt 
608d5b1a78aSEric Anholt 	ret = vc4_validate_shader_recs(dev, exec);
609d5b1a78aSEric Anholt 
610d5b1a78aSEric Anholt fail:
611d5b1a78aSEric Anholt 	kfree(temp);
612d5b1a78aSEric Anholt 	return ret;
613d5b1a78aSEric Anholt }
614d5b1a78aSEric Anholt 
615d5b1a78aSEric Anholt static void
616d5b1a78aSEric Anholt vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
617d5b1a78aSEric Anholt {
618d5b1a78aSEric Anholt 	unsigned i;
619d5b1a78aSEric Anholt 
620d5b1a78aSEric Anholt 	/* Need the struct lock for drm_gem_object_unreference(). */
621d5b1a78aSEric Anholt 	mutex_lock(&dev->struct_mutex);
622d5b1a78aSEric Anholt 	if (exec->bo) {
623d5b1a78aSEric Anholt 		for (i = 0; i < exec->bo_count; i++)
624d5b1a78aSEric Anholt 			drm_gem_object_unreference(&exec->bo[i]->base);
625d5b1a78aSEric Anholt 		kfree(exec->bo);
626d5b1a78aSEric Anholt 	}
627d5b1a78aSEric Anholt 
628d5b1a78aSEric Anholt 	while (!list_empty(&exec->unref_list)) {
629d5b1a78aSEric Anholt 		struct vc4_bo *bo = list_first_entry(&exec->unref_list,
630d5b1a78aSEric Anholt 						     struct vc4_bo, unref_head);
631d5b1a78aSEric Anholt 		list_del(&bo->unref_head);
632d5b1a78aSEric Anholt 		drm_gem_object_unreference(&bo->base.base);
633d5b1a78aSEric Anholt 	}
634d5b1a78aSEric Anholt 	mutex_unlock(&dev->struct_mutex);
635d5b1a78aSEric Anholt 
636d5b1a78aSEric Anholt 	kfree(exec);
637d5b1a78aSEric Anholt }
638d5b1a78aSEric Anholt 
639d5b1a78aSEric Anholt void
640d5b1a78aSEric Anholt vc4_job_handle_completed(struct vc4_dev *vc4)
641d5b1a78aSEric Anholt {
642d5b1a78aSEric Anholt 	unsigned long irqflags;
643b501baccSEric Anholt 	struct vc4_seqno_cb *cb, *cb_temp;
644d5b1a78aSEric Anholt 
645d5b1a78aSEric Anholt 	spin_lock_irqsave(&vc4->job_lock, irqflags);
646d5b1a78aSEric Anholt 	while (!list_empty(&vc4->job_done_list)) {
647d5b1a78aSEric Anholt 		struct vc4_exec_info *exec =
648d5b1a78aSEric Anholt 			list_first_entry(&vc4->job_done_list,
649d5b1a78aSEric Anholt 					 struct vc4_exec_info, head);
650d5b1a78aSEric Anholt 		list_del(&exec->head);
651d5b1a78aSEric Anholt 
652d5b1a78aSEric Anholt 		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
653d5b1a78aSEric Anholt 		vc4_complete_exec(vc4->dev, exec);
654d5b1a78aSEric Anholt 		spin_lock_irqsave(&vc4->job_lock, irqflags);
655d5b1a78aSEric Anholt 	}
656b501baccSEric Anholt 
657b501baccSEric Anholt 	list_for_each_entry_safe(cb, cb_temp, &vc4->seqno_cb_list, work.entry) {
658b501baccSEric Anholt 		if (cb->seqno <= vc4->finished_seqno) {
659b501baccSEric Anholt 			list_del_init(&cb->work.entry);
660b501baccSEric Anholt 			schedule_work(&cb->work);
661b501baccSEric Anholt 		}
662b501baccSEric Anholt 	}
663b501baccSEric Anholt 
664d5b1a78aSEric Anholt 	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
665d5b1a78aSEric Anholt }
666d5b1a78aSEric Anholt 
667b501baccSEric Anholt static void vc4_seqno_cb_work(struct work_struct *work)
668b501baccSEric Anholt {
669b501baccSEric Anholt 	struct vc4_seqno_cb *cb = container_of(work, struct vc4_seqno_cb, work);
670b501baccSEric Anholt 
671b501baccSEric Anholt 	cb->func(cb);
672b501baccSEric Anholt }
673b501baccSEric Anholt 
674b501baccSEric Anholt int vc4_queue_seqno_cb(struct drm_device *dev,
675b501baccSEric Anholt 		       struct vc4_seqno_cb *cb, uint64_t seqno,
676b501baccSEric Anholt 		       void (*func)(struct vc4_seqno_cb *cb))
677b501baccSEric Anholt {
678b501baccSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
679b501baccSEric Anholt 	int ret = 0;
680b501baccSEric Anholt 	unsigned long irqflags;
681b501baccSEric Anholt 
682b501baccSEric Anholt 	cb->func = func;
683b501baccSEric Anholt 	INIT_WORK(&cb->work, vc4_seqno_cb_work);
684b501baccSEric Anholt 
685b501baccSEric Anholt 	spin_lock_irqsave(&vc4->job_lock, irqflags);
686b501baccSEric Anholt 	if (seqno > vc4->finished_seqno) {
687b501baccSEric Anholt 		cb->seqno = seqno;
688b501baccSEric Anholt 		list_add_tail(&cb->work.entry, &vc4->seqno_cb_list);
689b501baccSEric Anholt 	} else {
690b501baccSEric Anholt 		schedule_work(&cb->work);
691b501baccSEric Anholt 	}
692b501baccSEric Anholt 	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
693b501baccSEric Anholt 
694b501baccSEric Anholt 	return ret;
695b501baccSEric Anholt }
696b501baccSEric Anholt 
697d5b1a78aSEric Anholt /* Scheduled when any job has been completed, this walks the list of
698d5b1a78aSEric Anholt  * jobs that had completed and unrefs their BOs and frees their exec
699d5b1a78aSEric Anholt  * structs.
700d5b1a78aSEric Anholt  */
701d5b1a78aSEric Anholt static void
702d5b1a78aSEric Anholt vc4_job_done_work(struct work_struct *work)
703d5b1a78aSEric Anholt {
704d5b1a78aSEric Anholt 	struct vc4_dev *vc4 =
705d5b1a78aSEric Anholt 		container_of(work, struct vc4_dev, job_done_work);
706d5b1a78aSEric Anholt 
707d5b1a78aSEric Anholt 	vc4_job_handle_completed(vc4);
708d5b1a78aSEric Anholt }
709d5b1a78aSEric Anholt 
710d5b1a78aSEric Anholt static int
711d5b1a78aSEric Anholt vc4_wait_for_seqno_ioctl_helper(struct drm_device *dev,
712d5b1a78aSEric Anholt 				uint64_t seqno,
713d5b1a78aSEric Anholt 				uint64_t *timeout_ns)
714d5b1a78aSEric Anholt {
715d5b1a78aSEric Anholt 	unsigned long start = jiffies;
716d5b1a78aSEric Anholt 	int ret = vc4_wait_for_seqno(dev, seqno, *timeout_ns, true);
717d5b1a78aSEric Anholt 
718d5b1a78aSEric Anholt 	if ((ret == -EINTR || ret == -ERESTARTSYS) && *timeout_ns != ~0ull) {
719d5b1a78aSEric Anholt 		uint64_t delta = jiffies_to_nsecs(jiffies - start);
720d5b1a78aSEric Anholt 
721d5b1a78aSEric Anholt 		if (*timeout_ns >= delta)
722d5b1a78aSEric Anholt 			*timeout_ns -= delta;
723d5b1a78aSEric Anholt 	}
724d5b1a78aSEric Anholt 
725d5b1a78aSEric Anholt 	return ret;
726d5b1a78aSEric Anholt }
727d5b1a78aSEric Anholt 
728d5b1a78aSEric Anholt int
729d5b1a78aSEric Anholt vc4_wait_seqno_ioctl(struct drm_device *dev, void *data,
730d5b1a78aSEric Anholt 		     struct drm_file *file_priv)
731d5b1a78aSEric Anholt {
732d5b1a78aSEric Anholt 	struct drm_vc4_wait_seqno *args = data;
733d5b1a78aSEric Anholt 
734d5b1a78aSEric Anholt 	return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno,
735d5b1a78aSEric Anholt 					       &args->timeout_ns);
736d5b1a78aSEric Anholt }
737d5b1a78aSEric Anholt 
738d5b1a78aSEric Anholt int
739d5b1a78aSEric Anholt vc4_wait_bo_ioctl(struct drm_device *dev, void *data,
740d5b1a78aSEric Anholt 		  struct drm_file *file_priv)
741d5b1a78aSEric Anholt {
742d5b1a78aSEric Anholt 	int ret;
743d5b1a78aSEric Anholt 	struct drm_vc4_wait_bo *args = data;
744d5b1a78aSEric Anholt 	struct drm_gem_object *gem_obj;
745d5b1a78aSEric Anholt 	struct vc4_bo *bo;
746d5b1a78aSEric Anholt 
747e0015236SEric Anholt 	if (args->pad != 0)
748e0015236SEric Anholt 		return -EINVAL;
749e0015236SEric Anholt 
750d5b1a78aSEric Anholt 	gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle);
751d5b1a78aSEric Anholt 	if (!gem_obj) {
752d5b1a78aSEric Anholt 		DRM_ERROR("Failed to look up GEM BO %d\n", args->handle);
753d5b1a78aSEric Anholt 		return -EINVAL;
754d5b1a78aSEric Anholt 	}
755d5b1a78aSEric Anholt 	bo = to_vc4_bo(gem_obj);
756d5b1a78aSEric Anholt 
757d5b1a78aSEric Anholt 	ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno,
758d5b1a78aSEric Anholt 					      &args->timeout_ns);
759d5b1a78aSEric Anholt 
760d5b1a78aSEric Anholt 	drm_gem_object_unreference_unlocked(gem_obj);
761d5b1a78aSEric Anholt 	return ret;
762d5b1a78aSEric Anholt }
763d5b1a78aSEric Anholt 
764d5b1a78aSEric Anholt /**
765d5b1a78aSEric Anholt  * Submits a command list to the VC4.
766d5b1a78aSEric Anholt  *
767d5b1a78aSEric Anholt  * This is what is called batchbuffer emitting on other hardware.
768d5b1a78aSEric Anholt  */
769d5b1a78aSEric Anholt int
770d5b1a78aSEric Anholt vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
771d5b1a78aSEric Anholt 		    struct drm_file *file_priv)
772d5b1a78aSEric Anholt {
773d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
774d5b1a78aSEric Anholt 	struct drm_vc4_submit_cl *args = data;
775d5b1a78aSEric Anholt 	struct vc4_exec_info *exec;
776d5b1a78aSEric Anholt 	int ret;
777d5b1a78aSEric Anholt 
778d5b1a78aSEric Anholt 	if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) {
779d5b1a78aSEric Anholt 		DRM_ERROR("Unknown flags: 0x%02x\n", args->flags);
780d5b1a78aSEric Anholt 		return -EINVAL;
781d5b1a78aSEric Anholt 	}
782d5b1a78aSEric Anholt 
783d5b1a78aSEric Anholt 	exec = kcalloc(1, sizeof(*exec), GFP_KERNEL);
784d5b1a78aSEric Anholt 	if (!exec) {
785d5b1a78aSEric Anholt 		DRM_ERROR("malloc failure on exec struct\n");
786d5b1a78aSEric Anholt 		return -ENOMEM;
787d5b1a78aSEric Anholt 	}
788d5b1a78aSEric Anholt 
789d5b1a78aSEric Anholt 	exec->args = args;
790d5b1a78aSEric Anholt 	INIT_LIST_HEAD(&exec->unref_list);
791d5b1a78aSEric Anholt 
792d5b1a78aSEric Anholt 	ret = vc4_cl_lookup_bos(dev, file_priv, exec);
793d5b1a78aSEric Anholt 	if (ret)
794d5b1a78aSEric Anholt 		goto fail;
795d5b1a78aSEric Anholt 
796d5b1a78aSEric Anholt 	if (exec->args->bin_cl_size != 0) {
797d5b1a78aSEric Anholt 		ret = vc4_get_bcl(dev, exec);
798d5b1a78aSEric Anholt 		if (ret)
799d5b1a78aSEric Anholt 			goto fail;
800d5b1a78aSEric Anholt 	} else {
801d5b1a78aSEric Anholt 		exec->ct0ca = 0;
802d5b1a78aSEric Anholt 		exec->ct0ea = 0;
803d5b1a78aSEric Anholt 	}
804d5b1a78aSEric Anholt 
805d5b1a78aSEric Anholt 	ret = vc4_get_rcl(dev, exec);
806d5b1a78aSEric Anholt 	if (ret)
807d5b1a78aSEric Anholt 		goto fail;
808d5b1a78aSEric Anholt 
809d5b1a78aSEric Anholt 	/* Clear this out of the struct we'll be putting in the queue,
810d5b1a78aSEric Anholt 	 * since it's part of our stack.
811d5b1a78aSEric Anholt 	 */
812d5b1a78aSEric Anholt 	exec->args = NULL;
813d5b1a78aSEric Anholt 
814d5b1a78aSEric Anholt 	vc4_queue_submit(dev, exec);
815d5b1a78aSEric Anholt 
816d5b1a78aSEric Anholt 	/* Return the seqno for our job. */
817d5b1a78aSEric Anholt 	args->seqno = vc4->emit_seqno;
818d5b1a78aSEric Anholt 
819d5b1a78aSEric Anholt 	return 0;
820d5b1a78aSEric Anholt 
821d5b1a78aSEric Anholt fail:
822d5b1a78aSEric Anholt 	vc4_complete_exec(vc4->dev, exec);
823d5b1a78aSEric Anholt 
824d5b1a78aSEric Anholt 	return ret;
825d5b1a78aSEric Anholt }
826d5b1a78aSEric Anholt 
827d5b1a78aSEric Anholt void
828d5b1a78aSEric Anholt vc4_gem_init(struct drm_device *dev)
829d5b1a78aSEric Anholt {
830d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
831d5b1a78aSEric Anholt 
832d5b1a78aSEric Anholt 	INIT_LIST_HEAD(&vc4->job_list);
833d5b1a78aSEric Anholt 	INIT_LIST_HEAD(&vc4->job_done_list);
834b501baccSEric Anholt 	INIT_LIST_HEAD(&vc4->seqno_cb_list);
835d5b1a78aSEric Anholt 	spin_lock_init(&vc4->job_lock);
836d5b1a78aSEric Anholt 
837d5b1a78aSEric Anholt 	INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work);
838d5b1a78aSEric Anholt 	setup_timer(&vc4->hangcheck.timer,
839d5b1a78aSEric Anholt 		    vc4_hangcheck_elapsed,
840d5b1a78aSEric Anholt 		    (unsigned long)dev);
841d5b1a78aSEric Anholt 
842d5b1a78aSEric Anholt 	INIT_WORK(&vc4->job_done_work, vc4_job_done_work);
843d5b1a78aSEric Anholt }
844d5b1a78aSEric Anholt 
845d5b1a78aSEric Anholt void
846d5b1a78aSEric Anholt vc4_gem_destroy(struct drm_device *dev)
847d5b1a78aSEric Anholt {
848d5b1a78aSEric Anholt 	struct vc4_dev *vc4 = to_vc4_dev(dev);
849d5b1a78aSEric Anholt 
850d5b1a78aSEric Anholt 	/* Waiting for exec to finish would need to be done before
851d5b1a78aSEric Anholt 	 * unregistering V3D.
852d5b1a78aSEric Anholt 	 */
853d5b1a78aSEric Anholt 	WARN_ON(vc4->emit_seqno != vc4->finished_seqno);
854d5b1a78aSEric Anholt 
855d5b1a78aSEric Anholt 	/* V3D should already have disabled its interrupt and cleared
856d5b1a78aSEric Anholt 	 * the overflow allocation registers.  Now free the object.
857d5b1a78aSEric Anholt 	 */
858d5b1a78aSEric Anholt 	if (vc4->overflow_mem) {
859d5b1a78aSEric Anholt 		drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
860d5b1a78aSEric Anholt 		vc4->overflow_mem = NULL;
861d5b1a78aSEric Anholt 	}
862d5b1a78aSEric Anholt 
863d5b1a78aSEric Anholt 	vc4_bo_cache_destroy(dev);
86421461365SEric Anholt 
86521461365SEric Anholt 	if (vc4->hang_state)
86621461365SEric Anholt 		vc4_free_hang_state(dev, vc4->hang_state);
867d5b1a78aSEric Anholt }
868