diff options
-rw-r--r-- | drivers/dma/sa11x0-dma.c | 108 |
1 files changed, 92 insertions, 16 deletions
diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index db4fcbd80c7d..f5a73606217e 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c | |||
@@ -78,6 +78,8 @@ struct sa11x0_dma_desc { | |||
78 | 78 | ||
79 | u32 ddar; | 79 | u32 ddar; |
80 | size_t size; | 80 | size_t size; |
81 | unsigned period; | ||
82 | bool cyclic; | ||
81 | 83 | ||
82 | unsigned sglen; | 84 | unsigned sglen; |
83 | struct sa11x0_dma_sg sg[0]; | 85 | struct sa11x0_dma_sg sg[0]; |
@@ -178,19 +180,24 @@ static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p, | |||
178 | return; | 180 | return; |
179 | 181 | ||
180 | if (p->sg_load == txd->sglen) { | 182 | if (p->sg_load == txd->sglen) { |
181 | struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c); | 183 | if (!txd->cyclic) { |
184 | struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c); | ||
182 | 185 | ||
183 | /* | 186 | /* |
184 | * We have reached the end of the current descriptor. | 187 | * We have reached the end of the current descriptor. |
185 | * Peek at the next descriptor, and if compatible with | 188 | * Peek at the next descriptor, and if compatible with |
186 | * the current, start processing it. | 189 | * the current, start processing it. |
187 | */ | 190 | */ |
188 | if (txn && txn->ddar == txd->ddar) { | 191 | if (txn && txn->ddar == txd->ddar) { |
189 | txd = txn; | 192 | txd = txn; |
190 | sa11x0_dma_start_desc(p, txn); | 193 | sa11x0_dma_start_desc(p, txn); |
194 | } else { | ||
195 | p->txd_load = NULL; | ||
196 | return; | ||
197 | } | ||
191 | } else { | 198 | } else { |
192 | p->txd_load = NULL; | 199 | /* Cyclic: reset back to beginning */ |
193 | return; | 200 | p->sg_load = 0; |
194 | } | 201 | } |
195 | } | 202 | } |
196 | 203 | ||
@@ -224,13 +231,21 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p, | |||
224 | struct sa11x0_dma_desc *txd = p->txd_done; | 231 | struct sa11x0_dma_desc *txd = p->txd_done; |
225 | 232 | ||
226 | if (++p->sg_done == txd->sglen) { | 233 | if (++p->sg_done == txd->sglen) { |
227 | vchan_cookie_complete(&txd->vd); | 234 | if (!txd->cyclic) { |
235 | vchan_cookie_complete(&txd->vd); | ||
228 | 236 | ||
229 | p->sg_done = 0; | 237 | p->sg_done = 0; |
230 | p->txd_done = p->txd_load; | 238 | p->txd_done = p->txd_load; |
239 | |||
240 | if (!p->txd_done) | ||
241 | tasklet_schedule(&p->dev->task); | ||
242 | } else { | ||
243 | if ((p->sg_done % txd->period) == 0) | ||
244 | vchan_cyclic_callback(&txd->vd); | ||
231 | 245 | ||
232 | if (!p->txd_done) | 246 | /* Cyclic: reset back to beginning */ |
233 | tasklet_schedule(&p->dev->task); | 247 | p->sg_done = 0; |
248 | } | ||
234 | } | 249 | } |
235 | 250 | ||
236 | sa11x0_dma_start_sg(p, c); | 251 | sa11x0_dma_start_sg(p, c); |
@@ -597,6 +612,65 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg( | |||
597 | return vchan_tx_prep(&c->vc, &txd->vd, flags); | 612 | return vchan_tx_prep(&c->vc, &txd->vd, flags); |
598 | } | 613 | } |
599 | 614 | ||
615 | static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic( | ||
616 | struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period, | ||
617 | enum dma_transfer_direction dir, void *context) | ||
618 | { | ||
619 | struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); | ||
620 | struct sa11x0_dma_desc *txd; | ||
621 | unsigned i, j, k, sglen, sgperiod; | ||
622 | |||
623 | /* SA11x0 channels can only operate in their native direction */ | ||
624 | if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) { | ||
625 | dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n", | ||
626 | &c->vc, c->ddar, dir); | ||
627 | return NULL; | ||
628 | } | ||
629 | |||
630 | sgperiod = DIV_ROUND_UP(period, DMA_MAX_SIZE & ~DMA_ALIGN); | ||
631 | sglen = size * sgperiod / period; | ||
632 | |||
633 | /* Do not allow zero-sized txds */ | ||
634 | if (sglen == 0) | ||
635 | return NULL; | ||
636 | |||
637 | txd = kzalloc(sizeof(*txd) + sglen * sizeof(txd->sg[0]), GFP_ATOMIC); | ||
638 | if (!txd) { | ||
639 | dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc); | ||
640 | return NULL; | ||
641 | } | ||
642 | |||
643 | for (i = k = 0; i < size / period; i++) { | ||
644 | size_t tlen, len = period; | ||
645 | |||
646 | for (j = 0; j < sgperiod; j++, k++) { | ||
647 | tlen = len; | ||
648 | |||
649 | if (tlen > DMA_MAX_SIZE) { | ||
650 | unsigned mult = DIV_ROUND_UP(tlen, DMA_MAX_SIZE & ~DMA_ALIGN); | ||
651 | tlen = (tlen / mult) & ~DMA_ALIGN; | ||
652 | } | ||
653 | |||
654 | txd->sg[k].addr = addr; | ||
655 | txd->sg[k].len = tlen; | ||
656 | addr += tlen; | ||
657 | len -= tlen; | ||
658 | } | ||
659 | |||
660 | WARN_ON(len != 0); | ||
661 | } | ||
662 | |||
663 | WARN_ON(k != sglen); | ||
664 | |||
665 | txd->ddar = c->ddar; | ||
666 | txd->size = size; | ||
667 | txd->sglen = sglen; | ||
668 | txd->cyclic = 1; | ||
669 | txd->period = sgperiod; | ||
670 | |||
671 | return vchan_tx_prep(&c->vc, &txd->vd, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
672 | } | ||
673 | |||
600 | static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg) | 674 | static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg) |
601 | { | 675 | { |
602 | u32 ddar = c->ddar & ((0xf << 4) | DDAR_RW); | 676 | u32 ddar = c->ddar & ((0xf << 4) | DDAR_RW); |
@@ -867,7 +941,9 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev) | |||
867 | } | 941 | } |
868 | 942 | ||
869 | dma_cap_set(DMA_SLAVE, d->slave.cap_mask); | 943 | dma_cap_set(DMA_SLAVE, d->slave.cap_mask); |
944 | dma_cap_set(DMA_CYCLIC, d->slave.cap_mask); | ||
870 | d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg; | 945 | d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg; |
946 | d->slave.device_prep_dma_cyclic = sa11x0_dma_prep_dma_cyclic; | ||
871 | ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev); | 947 | ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev); |
872 | if (ret) { | 948 | if (ret) { |
873 | dev_warn(d->slave.dev, "failed to register slave async device: %d\n", | 949 | dev_warn(d->slave.dev, "failed to register slave async device: %d\n", |