Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 84c8447c authored by Linus Walleij's avatar Linus Walleij Committed by Dan Williams
Browse files

DMAENGINE: COH 901 318 fix bytesleft



This makes the function to get the number of bytes left in the
ongoing DMA transaction actually work: the old code did not take
neither lli:s nor queued jobs into account. Also fix a missing
spinlock while we're at it.

Signed-off-by: default avatarLinus Walleij <linus.walleij@stericsson.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent cecd87da
Loading
Loading
Loading
Loading
+88 −8
Original line number Diff line number Diff line
@@ -408,25 +408,100 @@ coh901318_first_queued(struct coh901318_chan *cohc)
	return d;
}

static inline u32 coh901318_get_bytes_in_lli(struct coh901318_lli *in_lli)
{
	struct coh901318_lli *lli = in_lli;
	u32 bytes = 0;

	while (lli) {
		bytes += lli->control & COH901318_CX_CTRL_TC_VALUE_MASK;
		lli = lli->virt_link_addr;
	}
	return bytes;
}

/*
 * DMA start/stop controls
 * Get the number of bytes left to transfer on this channel,
 * it is unwise to call this before stopping the channel for
 * absolute measures, but for a rough guess you can still call
 * it.
 */
u32 coh901318_get_bytes_left(struct dma_chan *chan)
{
	unsigned long flags;
	u32 ret;
	struct coh901318_chan *cohc = to_coh901318_chan(chan);
	struct coh901318_desc *cohd;
	struct list_head *pos;
	unsigned long flags;
	u32 left = 0;
	int i = 0;

	spin_lock_irqsave(&cohc->lock, flags);

	/* Read transfer count value */
	ret = readl(cohc->base->virtbase +
		    COH901318_CX_CTRL+COH901318_CX_CTRL_SPACING *
		    cohc->id) & COH901318_CX_CTRL_TC_VALUE_MASK;
	/*
	 * If there are many queued jobs, we iterate and add the
	 * size of them all. We take a special look on the first
	 * job though, since it is probably active.
	 */
	list_for_each(pos, &cohc->active) {
		/*
		 * The first job in the list will be working on the
		 * hardware. The job can be stopped but still active,
		 * so that the transfer counter is somewhere inside
		 * the buffer.
		 */
		cohd = list_entry(pos, struct coh901318_desc, node);

		if (i == 0) {
			struct coh901318_lli *lli;
			dma_addr_t ladd;

			/* Read current transfer count value */
			left = readl(cohc->base->virtbase +
				     COH901318_CX_CTRL +
				     COH901318_CX_CTRL_SPACING * cohc->id) &
				COH901318_CX_CTRL_TC_VALUE_MASK;

			/* See if the transfer is linked... */
			ladd = readl(cohc->base->virtbase +
				     COH901318_CX_LNK_ADDR +
				     COH901318_CX_LNK_ADDR_SPACING *
				     cohc->id) &
				~COH901318_CX_LNK_LINK_IMMEDIATE;
			/* Single transaction */
			if (!ladd)
				continue;

			/*
			 * Linked transaction, follow the lli, find the
			 * currently processing lli, and proceed to the next
			 */
			lli = cohd->lli;
			while (lli && lli->link_addr != ladd)
				lli = lli->virt_link_addr;

			if (lli)
				lli = lli->virt_link_addr;

			/*
			 * Follow remaining lli links around to count the total
			 * number of bytes left
			 */
			left += coh901318_get_bytes_in_lli(lli);
		} else {
			left += coh901318_get_bytes_in_lli(cohd->lli);
		}
		i++;
	}

	/* Also count bytes in the queued jobs */
	list_for_each(pos, &cohc->queue) {
		cohd = list_entry(pos, struct coh901318_desc, node);
		left += coh901318_get_bytes_in_lli(cohd->lli);
	}

	spin_unlock_irqrestore(&cohc->lock, flags);

	return ret;
	return left;
}
EXPORT_SYMBOL(coh901318_get_bytes_left);

@@ -831,6 +906,7 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id)
static int coh901318_alloc_chan_resources(struct dma_chan *chan)
{
	struct coh901318_chan	*cohc = to_coh901318_chan(chan);
	unsigned long flags;

	dev_vdbg(COHC_2_DEV(cohc), "[%s] DMA channel %d\n",
		 __func__, cohc->id);
@@ -838,11 +914,15 @@ static int coh901318_alloc_chan_resources(struct dma_chan *chan)
	if (chan->client_count > 1)
		return -EBUSY;

	spin_lock_irqsave(&cohc->lock, flags);

	coh901318_config(cohc, NULL);

	cohc->allocated = 1;
	cohc->completed = chan->cookie = 1;

	spin_unlock_irqrestore(&cohc->lock, flags);

	return 1;
}