Commit d9444325 authored by Russell King's avatar Russell King

dmaengine: sa11x0-dma: add cyclic DMA support

Add support for cyclic DMA on sa11x0 platforms.  This follows the
discussed behaviour that the callback will be called at some point
after period expires, and may coalesce multiple period expiries into
one callback (due to the tasklet behaviour.)
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 63fe23c3
...@@ -78,6 +78,8 @@ struct sa11x0_dma_desc { ...@@ -78,6 +78,8 @@ struct sa11x0_dma_desc {
u32 ddar; u32 ddar;
size_t size; size_t size;
unsigned period;
bool cyclic;
unsigned sglen; unsigned sglen;
struct sa11x0_dma_sg sg[0]; struct sa11x0_dma_sg sg[0];
...@@ -178,6 +180,7 @@ static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p, ...@@ -178,6 +180,7 @@ static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
return; return;
if (p->sg_load == txd->sglen) { if (p->sg_load == txd->sglen) {
if (!txd->cyclic) {
struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c); struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c);
/* /*
...@@ -192,6 +195,10 @@ static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p, ...@@ -192,6 +195,10 @@ static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
p->txd_load = NULL; p->txd_load = NULL;
return; return;
} }
} else {
/* Cyclic: reset back to beginning */
p->sg_load = 0;
}
} }
sg = &txd->sg[p->sg_load++]; sg = &txd->sg[p->sg_load++];
...@@ -224,6 +231,7 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p, ...@@ -224,6 +231,7 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p,
struct sa11x0_dma_desc *txd = p->txd_done; struct sa11x0_dma_desc *txd = p->txd_done;
if (++p->sg_done == txd->sglen) { if (++p->sg_done == txd->sglen) {
if (!txd->cyclic) {
vchan_cookie_complete(&txd->vd); vchan_cookie_complete(&txd->vd);
p->sg_done = 0; p->sg_done = 0;
...@@ -231,6 +239,13 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p, ...@@ -231,6 +239,13 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p,
if (!p->txd_done) if (!p->txd_done)
tasklet_schedule(&p->dev->task); tasklet_schedule(&p->dev->task);
} else {
if ((p->sg_done % txd->period) == 0)
vchan_cyclic_callback(&txd->vd);
/* Cyclic: reset back to beginning */
p->sg_done = 0;
}
} }
sa11x0_dma_start_sg(p, c); sa11x0_dma_start_sg(p, c);
...@@ -597,6 +612,65 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg( ...@@ -597,6 +612,65 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
return vchan_tx_prep(&c->vc, &txd->vd, flags); return vchan_tx_prep(&c->vc, &txd->vd, flags);
} }
static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period,
enum dma_transfer_direction dir, void *context)
{
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
struct sa11x0_dma_desc *txd;
unsigned i, j, k, sglen, sgperiod;
/* SA11x0 channels can only operate in their native direction */
if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) {
dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n",
&c->vc, c->ddar, dir);
return NULL;
}
sgperiod = DIV_ROUND_UP(period, DMA_MAX_SIZE & ~DMA_ALIGN);
sglen = size * sgperiod / period;
/* Do not allow zero-sized txds */
if (sglen == 0)
return NULL;
txd = kzalloc(sizeof(*txd) + sglen * sizeof(txd->sg[0]), GFP_ATOMIC);
if (!txd) {
dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc);
return NULL;
}
for (i = k = 0; i < size / period; i++) {
size_t tlen, len = period;
for (j = 0; j < sgperiod; j++, k++) {
tlen = len;
if (tlen > DMA_MAX_SIZE) {
unsigned mult = DIV_ROUND_UP(tlen, DMA_MAX_SIZE & ~DMA_ALIGN);
tlen = (tlen / mult) & ~DMA_ALIGN;
}
txd->sg[k].addr = addr;
txd->sg[k].len = tlen;
addr += tlen;
len -= tlen;
}
WARN_ON(len != 0);
}
WARN_ON(k != sglen);
txd->ddar = c->ddar;
txd->size = size;
txd->sglen = sglen;
txd->cyclic = 1;
txd->period = sgperiod;
return vchan_tx_prep(&c->vc, &txd->vd, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
}
static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg) static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg)
{ {
u32 ddar = c->ddar & ((0xf << 4) | DDAR_RW); u32 ddar = c->ddar & ((0xf << 4) | DDAR_RW);
...@@ -867,7 +941,9 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev) ...@@ -867,7 +941,9 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev)
} }
dma_cap_set(DMA_SLAVE, d->slave.cap_mask); dma_cap_set(DMA_SLAVE, d->slave.cap_mask);
dma_cap_set(DMA_CYCLIC, d->slave.cap_mask);
d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg; d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg;
d->slave.device_prep_dma_cyclic = sa11x0_dma_prep_dma_cyclic;
ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev); ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev);
if (ret) { if (ret) {
dev_warn(d->slave.dev, "failed to register slave async device: %d\n", dev_warn(d->slave.dev, "failed to register slave async device: %d\n",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment