dmaengine: sa11x0-dma: fix DMA residue support

The semantics now implemented are:

- If the cookie has completed successfully, the residue will be zero.
- If the cookie is in progress or the channel is paused, it will be the
  number of bytes yet to be transferred. [*]
- If the cookie is queued, it will be the number of bytes in the
  descriptor.

* - where this is the number of bytes yet to be transferred to/from
  RAM.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
Russell King 2012-05-10 23:45:24 +01:00
parent 571fa74034
commit 63fe23c34e
1 changed files with 29 additions and 16 deletions

View File

@ -416,27 +416,47 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device); struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
struct sa11x0_dma_phy *p; struct sa11x0_dma_phy *p;
struct sa11x0_dma_desc *txd; struct virt_dma_desc *vd;
unsigned long flags; unsigned long flags;
enum dma_status ret; enum dma_status ret;
size_t bytes = 0;
ret = dma_cookie_status(&c->vc.chan, cookie, state); ret = dma_cookie_status(&c->vc.chan, cookie, state);
if (ret == DMA_SUCCESS) if (ret == DMA_SUCCESS)
return ret; return ret;
if (!state)
return c->status;
spin_lock_irqsave(&c->vc.lock, flags); spin_lock_irqsave(&c->vc.lock, flags);
p = c->phy; p = c->phy;
ret = c->status;
if (p) {
dma_addr_t addr = sa11x0_dma_pos(p);
dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr); /*
* If the cookie is on our issue queue, then the residue is
* its total size.
*/
vd = vchan_find_desc(&c->vc, cookie);
if (vd) {
state->residue = container_of(vd, struct sa11x0_dma_desc, vd)->size;
} else if (!p) {
state->residue = 0;
} else {
struct sa11x0_dma_desc *txd;
size_t bytes = 0;
txd = p->txd_done; if (p->txd_done && p->txd_done->vd.tx.cookie == cookie)
txd = p->txd_done;
else if (p->txd_load && p->txd_load->vd.tx.cookie == cookie)
txd = p->txd_load;
else
txd = NULL;
ret = c->status;
if (txd) { if (txd) {
dma_addr_t addr = sa11x0_dma_pos(p);
unsigned i; unsigned i;
dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr);
for (i = 0; i < txd->sglen; i++) { for (i = 0; i < txd->sglen; i++) {
dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x\n", dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x\n",
i, txd->sg[i].addr, txd->sg[i].len); i, txd->sg[i].addr, txd->sg[i].len);
@ -459,18 +479,11 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
bytes += txd->sg[i].len; bytes += txd->sg[i].len;
} }
} }
if (txd != p->txd_load && p->txd_load) state->residue = bytes;
bytes += p->txd_load->size;
}
list_for_each_entry(txd, &c->vc.desc_issued, vd.node) {
bytes += txd->size;
} }
spin_unlock_irqrestore(&c->vc.lock, flags); spin_unlock_irqrestore(&c->vc.lock, flags);
if (state) dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", state->residue);
state->residue = bytes;
dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes);
return ret; return ret;
} }