162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Tegra host1x Command DMA
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2010-2013, NVIDIA Corporation.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <asm/cacheflush.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/host1x.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/kfifo.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <trace/events/host1x.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "cdma.h"
2062306a36Sopenharmony_ci#include "channel.h"
2162306a36Sopenharmony_ci#include "dev.h"
2262306a36Sopenharmony_ci#include "debug.h"
2362306a36Sopenharmony_ci#include "job.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * push_buffer
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * The push buffer is a circular array of words to be fetched by command DMA.
2962306a36Sopenharmony_ci * Note that it works slightly differently to the sync queue; fence == pos
3062306a36Sopenharmony_ci * means that the push buffer is full, not empty.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * Typically the commands written into the push buffer are a pair of words. We
3562306a36Sopenharmony_ci * use slots to represent each of these pairs and to simplify things. Note the
3662306a36Sopenharmony_ci * strange number of slots allocated here. 512 slots will fit exactly within a
3762306a36Sopenharmony_ci * single memory page. We also need one additional word at the end of the push
3862306a36Sopenharmony_ci * buffer for the RESTART opcode that will instruct the CDMA to jump back to
3962306a36Sopenharmony_ci * the beginning of the push buffer. With 512 slots, this means that we'll use
4062306a36Sopenharmony_ci * 2 memory pages and waste 4092 bytes of the second page that will never be
4162306a36Sopenharmony_ci * used.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci#define HOST1X_PUSHBUFFER_SLOTS	511
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * Clean up push buffer resources
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic void host1x_pushbuffer_destroy(struct push_buffer *pb)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct host1x_cdma *cdma = pb_to_cdma(pb);
5162306a36Sopenharmony_ci	struct host1x *host1x = cdma_to_host1x(cdma);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (!pb->mapped)
5462306a36Sopenharmony_ci		return;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (host1x->domain) {
5762306a36Sopenharmony_ci		iommu_unmap(host1x->domain, pb->dma, pb->alloc_size);
5862306a36Sopenharmony_ci		free_iova(&host1x->iova, iova_pfn(&host1x->iova, pb->dma));
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	dma_free_wc(host1x->dev, pb->alloc_size, pb->mapped, pb->phys);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	pb->mapped = NULL;
6462306a36Sopenharmony_ci	pb->phys = 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/*
6862306a36Sopenharmony_ci * Init push buffer resources
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistatic int host1x_pushbuffer_init(struct push_buffer *pb)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct host1x_cdma *cdma = pb_to_cdma(pb);
7362306a36Sopenharmony_ci	struct host1x *host1x = cdma_to_host1x(cdma);
7462306a36Sopenharmony_ci	struct iova *alloc;
7562306a36Sopenharmony_ci	u32 size;
7662306a36Sopenharmony_ci	int err;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	pb->mapped = NULL;
7962306a36Sopenharmony_ci	pb->phys = 0;
8062306a36Sopenharmony_ci	pb->size = HOST1X_PUSHBUFFER_SLOTS * 8;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	size = pb->size + 4;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* initialize buffer pointers */
8562306a36Sopenharmony_ci	pb->fence = pb->size - 8;
8662306a36Sopenharmony_ci	pb->pos = 0;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (host1x->domain) {
8962306a36Sopenharmony_ci		unsigned long shift;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		size = iova_align(&host1x->iova, size);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		pb->mapped = dma_alloc_wc(host1x->dev, size, &pb->phys,
9462306a36Sopenharmony_ci					  GFP_KERNEL);
9562306a36Sopenharmony_ci		if (!pb->mapped)
9662306a36Sopenharmony_ci			return -ENOMEM;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		shift = iova_shift(&host1x->iova);
9962306a36Sopenharmony_ci		alloc = alloc_iova(&host1x->iova, size >> shift,
10062306a36Sopenharmony_ci				   host1x->iova_end >> shift, true);
10162306a36Sopenharmony_ci		if (!alloc) {
10262306a36Sopenharmony_ci			err = -ENOMEM;
10362306a36Sopenharmony_ci			goto iommu_free_mem;
10462306a36Sopenharmony_ci		}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		pb->dma = iova_dma_addr(&host1x->iova, alloc);
10762306a36Sopenharmony_ci		err = iommu_map(host1x->domain, pb->dma, pb->phys, size,
10862306a36Sopenharmony_ci				IOMMU_READ, GFP_KERNEL);
10962306a36Sopenharmony_ci		if (err)
11062306a36Sopenharmony_ci			goto iommu_free_iova;
11162306a36Sopenharmony_ci	} else {
11262306a36Sopenharmony_ci		pb->mapped = dma_alloc_wc(host1x->dev, size, &pb->phys,
11362306a36Sopenharmony_ci					  GFP_KERNEL);
11462306a36Sopenharmony_ci		if (!pb->mapped)
11562306a36Sopenharmony_ci			return -ENOMEM;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		pb->dma = pb->phys;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	pb->alloc_size = size;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	host1x_hw_pushbuffer_init(host1x, pb);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return 0;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciiommu_free_iova:
12762306a36Sopenharmony_ci	__free_iova(&host1x->iova, alloc);
12862306a36Sopenharmony_ciiommu_free_mem:
12962306a36Sopenharmony_ci	dma_free_wc(host1x->dev, size, pb->mapped, pb->phys);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return err;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/*
13562306a36Sopenharmony_ci * Push two words to the push buffer
13662306a36Sopenharmony_ci * Caller must ensure push buffer is not full
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistatic void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	u32 *p = (u32 *)((void *)pb->mapped + pb->pos);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	WARN_ON(pb->pos == pb->fence);
14362306a36Sopenharmony_ci	*(p++) = op1;
14462306a36Sopenharmony_ci	*(p++) = op2;
14562306a36Sopenharmony_ci	pb->pos += 8;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (pb->pos >= pb->size)
14862306a36Sopenharmony_ci		pb->pos -= pb->size;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/*
15262306a36Sopenharmony_ci * Pop a number of two word slots from the push buffer
15362306a36Sopenharmony_ci * Caller must ensure push buffer is not empty
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistatic void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	/* Advance the next write position */
15862306a36Sopenharmony_ci	pb->fence += slots * 8;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (pb->fence >= pb->size)
16162306a36Sopenharmony_ci		pb->fence -= pb->size;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/*
16562306a36Sopenharmony_ci * Return the number of two word slots free in the push buffer
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_cistatic u32 host1x_pushbuffer_space(struct push_buffer *pb)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	unsigned int fence = pb->fence;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (pb->fence < pb->pos)
17262306a36Sopenharmony_ci		fence += pb->size;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return (fence - pb->pos) / 8;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/*
17862306a36Sopenharmony_ci * Sleep (if necessary) until the requested event happens
17962306a36Sopenharmony_ci *   - CDMA_EVENT_SYNC_QUEUE_EMPTY : sync queue is completely empty.
18062306a36Sopenharmony_ci *     - Returns 1
18162306a36Sopenharmony_ci *   - CDMA_EVENT_PUSH_BUFFER_SPACE : there is space in the push buffer
18262306a36Sopenharmony_ci *     - Return the amount of space (> 0)
18362306a36Sopenharmony_ci * Must be called with the cdma lock held.
18462306a36Sopenharmony_ci */
18562306a36Sopenharmony_ciunsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma,
18662306a36Sopenharmony_ci				     enum cdma_event event)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	for (;;) {
18962306a36Sopenharmony_ci		struct push_buffer *pb = &cdma->push_buffer;
19062306a36Sopenharmony_ci		unsigned int space;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		switch (event) {
19362306a36Sopenharmony_ci		case CDMA_EVENT_SYNC_QUEUE_EMPTY:
19462306a36Sopenharmony_ci			space = list_empty(&cdma->sync_queue) ? 1 : 0;
19562306a36Sopenharmony_ci			break;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		case CDMA_EVENT_PUSH_BUFFER_SPACE:
19862306a36Sopenharmony_ci			space = host1x_pushbuffer_space(pb);
19962306a36Sopenharmony_ci			break;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		default:
20262306a36Sopenharmony_ci			WARN_ON(1);
20362306a36Sopenharmony_ci			return -EINVAL;
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		if (space)
20762306a36Sopenharmony_ci			return space;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		trace_host1x_wait_cdma(dev_name(cdma_to_channel(cdma)->dev),
21062306a36Sopenharmony_ci				       event);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		/* If somebody has managed to already start waiting, yield */
21362306a36Sopenharmony_ci		if (cdma->event != CDMA_EVENT_NONE) {
21462306a36Sopenharmony_ci			mutex_unlock(&cdma->lock);
21562306a36Sopenharmony_ci			schedule();
21662306a36Sopenharmony_ci			mutex_lock(&cdma->lock);
21762306a36Sopenharmony_ci			continue;
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		cdma->event = event;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		mutex_unlock(&cdma->lock);
22362306a36Sopenharmony_ci		wait_for_completion(&cdma->complete);
22462306a36Sopenharmony_ci		mutex_lock(&cdma->lock);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/*
23162306a36Sopenharmony_ci * Sleep (if necessary) until the push buffer has enough free space.
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci * Must be called with the cdma lock held.
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_cistatic int host1x_cdma_wait_pushbuffer_space(struct host1x *host1x,
23662306a36Sopenharmony_ci					     struct host1x_cdma *cdma,
23762306a36Sopenharmony_ci					     unsigned int needed)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	while (true) {
24062306a36Sopenharmony_ci		struct push_buffer *pb = &cdma->push_buffer;
24162306a36Sopenharmony_ci		unsigned int space;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		space = host1x_pushbuffer_space(pb);
24462306a36Sopenharmony_ci		if (space >= needed)
24562306a36Sopenharmony_ci			break;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		trace_host1x_wait_cdma(dev_name(cdma_to_channel(cdma)->dev),
24862306a36Sopenharmony_ci				       CDMA_EVENT_PUSH_BUFFER_SPACE);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		host1x_hw_cdma_flush(host1x, cdma);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		/* If somebody has managed to already start waiting, yield */
25362306a36Sopenharmony_ci		if (cdma->event != CDMA_EVENT_NONE) {
25462306a36Sopenharmony_ci			mutex_unlock(&cdma->lock);
25562306a36Sopenharmony_ci			schedule();
25662306a36Sopenharmony_ci			mutex_lock(&cdma->lock);
25762306a36Sopenharmony_ci			continue;
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		cdma->event = CDMA_EVENT_PUSH_BUFFER_SPACE;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		mutex_unlock(&cdma->lock);
26362306a36Sopenharmony_ci		wait_for_completion(&cdma->complete);
26462306a36Sopenharmony_ci		mutex_lock(&cdma->lock);
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return 0;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci/*
27062306a36Sopenharmony_ci * Start timer that tracks the time spent by the job.
27162306a36Sopenharmony_ci * Must be called with the cdma lock held.
27262306a36Sopenharmony_ci */
27362306a36Sopenharmony_cistatic void cdma_start_timer_locked(struct host1x_cdma *cdma,
27462306a36Sopenharmony_ci				    struct host1x_job *job)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	if (cdma->timeout.client) {
27762306a36Sopenharmony_ci		/* timer already started */
27862306a36Sopenharmony_ci		return;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	cdma->timeout.client = job->client;
28262306a36Sopenharmony_ci	cdma->timeout.syncpt = job->syncpt;
28362306a36Sopenharmony_ci	cdma->timeout.syncpt_val = job->syncpt_end;
28462306a36Sopenharmony_ci	cdma->timeout.start_ktime = ktime_get();
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	schedule_delayed_work(&cdma->timeout.wq,
28762306a36Sopenharmony_ci			      msecs_to_jiffies(job->timeout));
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci/*
29162306a36Sopenharmony_ci * Stop timer when a buffer submission completes.
29262306a36Sopenharmony_ci * Must be called with the cdma lock held.
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_cistatic void stop_cdma_timer_locked(struct host1x_cdma *cdma)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	cancel_delayed_work(&cdma->timeout.wq);
29762306a36Sopenharmony_ci	cdma->timeout.client = NULL;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/*
30162306a36Sopenharmony_ci * For all sync queue entries that have already finished according to the
30262306a36Sopenharmony_ci * current sync point registers:
30362306a36Sopenharmony_ci *  - unpin & unref their mems
30462306a36Sopenharmony_ci *  - pop their push buffer slots
30562306a36Sopenharmony_ci *  - remove them from the sync queue
30662306a36Sopenharmony_ci * This is normally called from the host code's worker thread, but can be
30762306a36Sopenharmony_ci * called manually if necessary.
30862306a36Sopenharmony_ci * Must be called with the cdma lock held.
30962306a36Sopenharmony_ci */
31062306a36Sopenharmony_cistatic void update_cdma_locked(struct host1x_cdma *cdma)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	bool signal = false;
31362306a36Sopenharmony_ci	struct host1x_job *job, *n;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/*
31662306a36Sopenharmony_ci	 * Walk the sync queue, reading the sync point registers as necessary,
31762306a36Sopenharmony_ci	 * to consume as many sync queue entries as possible without blocking
31862306a36Sopenharmony_ci	 */
31962306a36Sopenharmony_ci	list_for_each_entry_safe(job, n, &cdma->sync_queue, list) {
32062306a36Sopenharmony_ci		struct host1x_syncpt *sp = job->syncpt;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		/* Check whether this syncpt has completed, and bail if not */
32362306a36Sopenharmony_ci		if (!host1x_syncpt_is_expired(sp, job->syncpt_end) &&
32462306a36Sopenharmony_ci		    !job->cancelled) {
32562306a36Sopenharmony_ci			/* Start timer on next pending syncpt */
32662306a36Sopenharmony_ci			if (job->timeout)
32762306a36Sopenharmony_ci				cdma_start_timer_locked(cdma, job);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci			break;
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		/* Cancel timeout, when a buffer completes */
33362306a36Sopenharmony_ci		if (cdma->timeout.client)
33462306a36Sopenharmony_ci			stop_cdma_timer_locked(cdma);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		/* Unpin the memory */
33762306a36Sopenharmony_ci		host1x_job_unpin(job);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		/* Pop push buffer slots */
34062306a36Sopenharmony_ci		if (job->num_slots) {
34162306a36Sopenharmony_ci			struct push_buffer *pb = &cdma->push_buffer;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci			host1x_pushbuffer_pop(pb, job->num_slots);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci			if (cdma->event == CDMA_EVENT_PUSH_BUFFER_SPACE)
34662306a36Sopenharmony_ci				signal = true;
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		list_del(&job->list);
35062306a36Sopenharmony_ci		host1x_job_put(job);
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (cdma->event == CDMA_EVENT_SYNC_QUEUE_EMPTY &&
35462306a36Sopenharmony_ci	    list_empty(&cdma->sync_queue))
35562306a36Sopenharmony_ci		signal = true;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (signal) {
35862306a36Sopenharmony_ci		cdma->event = CDMA_EVENT_NONE;
35962306a36Sopenharmony_ci		complete(&cdma->complete);
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_civoid host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
36462306a36Sopenharmony_ci				   struct device *dev)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct host1x *host1x = cdma_to_host1x(cdma);
36762306a36Sopenharmony_ci	u32 restart_addr, syncpt_incrs, syncpt_val;
36862306a36Sopenharmony_ci	struct host1x_job *job, *next_job = NULL;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	syncpt_val = host1x_syncpt_load(cdma->timeout.syncpt);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	dev_dbg(dev, "%s: starting cleanup (thresh %d)\n",
37362306a36Sopenharmony_ci		__func__, syncpt_val);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/*
37662306a36Sopenharmony_ci	 * Move the sync_queue read pointer to the first entry that hasn't
37762306a36Sopenharmony_ci	 * completed based on the current HW syncpt value. It's likely there
37862306a36Sopenharmony_ci	 * won't be any (i.e. we're still at the head), but covers the case
37962306a36Sopenharmony_ci	 * where a syncpt incr happens just prior/during the teardown.
38062306a36Sopenharmony_ci	 */
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	dev_dbg(dev, "%s: skip completed buffers still in sync_queue\n",
38362306a36Sopenharmony_ci		__func__);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	list_for_each_entry(job, &cdma->sync_queue, list) {
38662306a36Sopenharmony_ci		if (syncpt_val < job->syncpt_end) {
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci			if (!list_is_last(&job->list, &cdma->sync_queue))
38962306a36Sopenharmony_ci				next_job = list_next_entry(job, list);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci			goto syncpt_incr;
39262306a36Sopenharmony_ci		}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		host1x_job_dump(dev, job);
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/* all jobs have been completed */
39862306a36Sopenharmony_ci	job = NULL;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cisyncpt_incr:
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/*
40362306a36Sopenharmony_ci	 * Increment with CPU the remaining syncpts of a partially executed job.
40462306a36Sopenharmony_ci	 *
40562306a36Sopenharmony_ci	 * CDMA will continue execution starting with the next job or will get
40662306a36Sopenharmony_ci	 * into idle state.
40762306a36Sopenharmony_ci	 */
40862306a36Sopenharmony_ci	if (next_job)
40962306a36Sopenharmony_ci		restart_addr = next_job->first_get;
41062306a36Sopenharmony_ci	else
41162306a36Sopenharmony_ci		restart_addr = cdma->last_pos;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (!job)
41462306a36Sopenharmony_ci		goto resume;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* do CPU increments for the remaining syncpts */
41762306a36Sopenharmony_ci	if (job->syncpt_recovery) {
41862306a36Sopenharmony_ci		dev_dbg(dev, "%s: perform CPU incr on pending buffers\n",
41962306a36Sopenharmony_ci			__func__);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		/* won't need a timeout when replayed */
42262306a36Sopenharmony_ci		job->timeout = 0;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		syncpt_incrs = job->syncpt_end - syncpt_val;
42562306a36Sopenharmony_ci		dev_dbg(dev, "%s: CPU incr (%d)\n", __func__, syncpt_incrs);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		host1x_job_dump(dev, job);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		/* safe to use CPU to incr syncpts */
43062306a36Sopenharmony_ci		host1x_hw_cdma_timeout_cpu_incr(host1x, cdma, job->first_get,
43162306a36Sopenharmony_ci						syncpt_incrs, job->syncpt_end,
43262306a36Sopenharmony_ci						job->num_slots);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		dev_dbg(dev, "%s: finished sync_queue modification\n",
43562306a36Sopenharmony_ci			__func__);
43662306a36Sopenharmony_ci	} else {
43762306a36Sopenharmony_ci		struct host1x_job *failed_job = job;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		host1x_job_dump(dev, job);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		host1x_syncpt_set_locked(job->syncpt);
44262306a36Sopenharmony_ci		failed_job->cancelled = true;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		list_for_each_entry_continue(job, &cdma->sync_queue, list) {
44562306a36Sopenharmony_ci			unsigned int i;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci			if (job->syncpt != failed_job->syncpt)
44862306a36Sopenharmony_ci				continue;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci			for (i = 0; i < job->num_slots; i++) {
45162306a36Sopenharmony_ci				unsigned int slot = (job->first_get/8 + i) %
45262306a36Sopenharmony_ci						    HOST1X_PUSHBUFFER_SLOTS;
45362306a36Sopenharmony_ci				u32 *mapped = cdma->push_buffer.mapped;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci				/*
45662306a36Sopenharmony_ci				 * Overwrite opcodes with 0 word writes
45762306a36Sopenharmony_ci				 * to offset 0xbad. This does nothing but
45862306a36Sopenharmony_ci				 * has a easily detected signature in debug
45962306a36Sopenharmony_ci				 * traces.
46062306a36Sopenharmony_ci				 *
46162306a36Sopenharmony_ci				 * On systems with MLOCK enforcement enabled,
46262306a36Sopenharmony_ci				 * the above 0 word writes would fall foul of
46362306a36Sopenharmony_ci				 * the enforcement. As such, in the first slot
46462306a36Sopenharmony_ci				 * put a RESTART_W opcode to the beginning
46562306a36Sopenharmony_ci				 * of the next job. We don't use this for older
46662306a36Sopenharmony_ci				 * chips since those only support the RESTART
46762306a36Sopenharmony_ci				 * opcode with inconvenient alignment requirements.
46862306a36Sopenharmony_ci				 */
46962306a36Sopenharmony_ci				if (i == 0 && host1x->info->has_wide_gather) {
47062306a36Sopenharmony_ci					unsigned int next_job = (job->first_get/8 + job->num_slots)
47162306a36Sopenharmony_ci						% HOST1X_PUSHBUFFER_SLOTS;
47262306a36Sopenharmony_ci					mapped[2*slot+0] = (0xd << 28) | (next_job * 2);
47362306a36Sopenharmony_ci					mapped[2*slot+1] = 0x0;
47462306a36Sopenharmony_ci				} else {
47562306a36Sopenharmony_ci					mapped[2*slot+0] = 0x1bad0000;
47662306a36Sopenharmony_ci					mapped[2*slot+1] = 0x1bad0000;
47762306a36Sopenharmony_ci				}
47862306a36Sopenharmony_ci			}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci			job->cancelled = true;
48162306a36Sopenharmony_ci		}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		wmb();
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		update_cdma_locked(cdma);
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ciresume:
48962306a36Sopenharmony_ci	/* roll back DMAGET and start up channel again */
49062306a36Sopenharmony_ci	host1x_hw_cdma_resume(host1x, cdma, restart_addr);
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic void cdma_update_work(struct work_struct *work)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct host1x_cdma *cdma = container_of(work, struct host1x_cdma, update_work);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	mutex_lock(&cdma->lock);
49862306a36Sopenharmony_ci	update_cdma_locked(cdma);
49962306a36Sopenharmony_ci	mutex_unlock(&cdma->lock);
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci/*
50362306a36Sopenharmony_ci * Create a cdma
50462306a36Sopenharmony_ci */
50562306a36Sopenharmony_ciint host1x_cdma_init(struct host1x_cdma *cdma)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	int err;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	mutex_init(&cdma->lock);
51062306a36Sopenharmony_ci	init_completion(&cdma->complete);
51162306a36Sopenharmony_ci	INIT_WORK(&cdma->update_work, cdma_update_work);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	INIT_LIST_HEAD(&cdma->sync_queue);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	cdma->event = CDMA_EVENT_NONE;
51662306a36Sopenharmony_ci	cdma->running = false;
51762306a36Sopenharmony_ci	cdma->torndown = false;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	err = host1x_pushbuffer_init(&cdma->push_buffer);
52062306a36Sopenharmony_ci	if (err)
52162306a36Sopenharmony_ci		return err;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return 0;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci/*
52762306a36Sopenharmony_ci * Destroy a cdma
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_ciint host1x_cdma_deinit(struct host1x_cdma *cdma)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct push_buffer *pb = &cdma->push_buffer;
53262306a36Sopenharmony_ci	struct host1x *host1x = cdma_to_host1x(cdma);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (cdma->running) {
53562306a36Sopenharmony_ci		pr_warn("%s: CDMA still running\n", __func__);
53662306a36Sopenharmony_ci		return -EBUSY;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	host1x_pushbuffer_destroy(pb);
54062306a36Sopenharmony_ci	host1x_hw_cdma_timeout_destroy(host1x, cdma);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return 0;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci/*
54662306a36Sopenharmony_ci * Begin a cdma submit
54762306a36Sopenharmony_ci */
54862306a36Sopenharmony_ciint host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct host1x *host1x = cdma_to_host1x(cdma);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	mutex_lock(&cdma->lock);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/*
55562306a36Sopenharmony_ci	 * Check if syncpoint was locked due to previous job timeout.
55662306a36Sopenharmony_ci	 * This needs to be done within the cdma lock to avoid a race
55762306a36Sopenharmony_ci	 * with the timeout handler.
55862306a36Sopenharmony_ci	 */
55962306a36Sopenharmony_ci	if (job->syncpt->locked) {
56062306a36Sopenharmony_ci		mutex_unlock(&cdma->lock);
56162306a36Sopenharmony_ci		return -EPERM;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (job->timeout) {
56562306a36Sopenharmony_ci		/* init state on first submit with timeout value */
56662306a36Sopenharmony_ci		if (!cdma->timeout.initialized) {
56762306a36Sopenharmony_ci			int err;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci			err = host1x_hw_cdma_timeout_init(host1x, cdma);
57062306a36Sopenharmony_ci			if (err) {
57162306a36Sopenharmony_ci				mutex_unlock(&cdma->lock);
57262306a36Sopenharmony_ci				return err;
57362306a36Sopenharmony_ci			}
57462306a36Sopenharmony_ci		}
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (!cdma->running)
57862306a36Sopenharmony_ci		host1x_hw_cdma_start(host1x, cdma);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	cdma->slots_free = 0;
58162306a36Sopenharmony_ci	cdma->slots_used = 0;
58262306a36Sopenharmony_ci	cdma->first_get = cdma->push_buffer.pos;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	trace_host1x_cdma_begin(dev_name(job->channel->dev));
58562306a36Sopenharmony_ci	return 0;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci/*
58962306a36Sopenharmony_ci * Push two words into a push buffer slot
59062306a36Sopenharmony_ci * Blocks as necessary if the push buffer is full.
59162306a36Sopenharmony_ci */
59262306a36Sopenharmony_civoid host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	struct host1x *host1x = cdma_to_host1x(cdma);
59562306a36Sopenharmony_ci	struct push_buffer *pb = &cdma->push_buffer;
59662306a36Sopenharmony_ci	u32 slots_free = cdma->slots_free;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (host1x_debug_trace_cmdbuf)
59962306a36Sopenharmony_ci		trace_host1x_cdma_push(dev_name(cdma_to_channel(cdma)->dev),
60062306a36Sopenharmony_ci				       op1, op2);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if (slots_free == 0) {
60362306a36Sopenharmony_ci		host1x_hw_cdma_flush(host1x, cdma);
60462306a36Sopenharmony_ci		slots_free = host1x_cdma_wait_locked(cdma,
60562306a36Sopenharmony_ci						CDMA_EVENT_PUSH_BUFFER_SPACE);
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	cdma->slots_free = slots_free - 1;
60962306a36Sopenharmony_ci	cdma->slots_used++;
61062306a36Sopenharmony_ci	host1x_pushbuffer_push(pb, op1, op2);
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci/*
61462306a36Sopenharmony_ci * Push four words into two consecutive push buffer slots. Note that extra
61562306a36Sopenharmony_ci * care needs to be taken not to split the two slots across the end of the
61662306a36Sopenharmony_ci * push buffer. Otherwise the RESTART opcode at the end of the push buffer
61762306a36Sopenharmony_ci * that ensures processing will restart at the beginning will break up the
61862306a36Sopenharmony_ci * four words.
61962306a36Sopenharmony_ci *
62062306a36Sopenharmony_ci * Blocks as necessary if the push buffer is full.
62162306a36Sopenharmony_ci */
62262306a36Sopenharmony_civoid host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2,
62362306a36Sopenharmony_ci			   u32 op3, u32 op4)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	struct host1x_channel *channel = cdma_to_channel(cdma);
62662306a36Sopenharmony_ci	struct host1x *host1x = cdma_to_host1x(cdma);
62762306a36Sopenharmony_ci	struct push_buffer *pb = &cdma->push_buffer;
62862306a36Sopenharmony_ci	unsigned int space = cdma->slots_free;
62962306a36Sopenharmony_ci	unsigned int needed = 2, extra = 0;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (host1x_debug_trace_cmdbuf)
63262306a36Sopenharmony_ci		trace_host1x_cdma_push_wide(dev_name(channel->dev), op1, op2,
63362306a36Sopenharmony_ci					    op3, op4);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	/* compute number of extra slots needed for padding */
63662306a36Sopenharmony_ci	if (pb->pos + 16 > pb->size) {
63762306a36Sopenharmony_ci		extra = (pb->size - pb->pos) / 8;
63862306a36Sopenharmony_ci		needed += extra;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	host1x_cdma_wait_pushbuffer_space(host1x, cdma, needed);
64262306a36Sopenharmony_ci	space = host1x_pushbuffer_space(pb);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	cdma->slots_free = space - needed;
64562306a36Sopenharmony_ci	cdma->slots_used += needed;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (extra > 0) {
64862306a36Sopenharmony_ci		/*
64962306a36Sopenharmony_ci		 * If there isn't enough space at the tail of the pushbuffer,
65062306a36Sopenharmony_ci		 * insert a RESTART(0) here to go back to the beginning.
65162306a36Sopenharmony_ci		 * The code above adjusted the indexes appropriately.
65262306a36Sopenharmony_ci		 */
65362306a36Sopenharmony_ci		host1x_pushbuffer_push(pb, (0x5 << 28), 0xdead0000);
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	host1x_pushbuffer_push(pb, op1, op2);
65762306a36Sopenharmony_ci	host1x_pushbuffer_push(pb, op3, op4);
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci/*
66162306a36Sopenharmony_ci * End a cdma submit
66262306a36Sopenharmony_ci * Kick off DMA, add job to the sync queue, and a number of slots to be freed
66362306a36Sopenharmony_ci * from the pushbuffer. The handles for a submit must all be pinned at the same
66462306a36Sopenharmony_ci * time, but they can be unpinned in smaller chunks.
66562306a36Sopenharmony_ci */
66662306a36Sopenharmony_civoid host1x_cdma_end(struct host1x_cdma *cdma,
66762306a36Sopenharmony_ci		     struct host1x_job *job)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct host1x *host1x = cdma_to_host1x(cdma);
67062306a36Sopenharmony_ci	bool idle = list_empty(&cdma->sync_queue);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	host1x_hw_cdma_flush(host1x, cdma);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	job->first_get = cdma->first_get;
67562306a36Sopenharmony_ci	job->num_slots = cdma->slots_used;
67662306a36Sopenharmony_ci	host1x_job_get(job);
67762306a36Sopenharmony_ci	list_add_tail(&job->list, &cdma->sync_queue);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* start timer on idle -> active transitions */
68062306a36Sopenharmony_ci	if (job->timeout && idle)
68162306a36Sopenharmony_ci		cdma_start_timer_locked(cdma, job);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	trace_host1x_cdma_end(dev_name(job->channel->dev));
68462306a36Sopenharmony_ci	mutex_unlock(&cdma->lock);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci/*
68862306a36Sopenharmony_ci * Update cdma state according to current sync point values
68962306a36Sopenharmony_ci */
69062306a36Sopenharmony_civoid host1x_cdma_update(struct host1x_cdma *cdma)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	schedule_work(&cdma->update_work);
69362306a36Sopenharmony_ci}
694