diff options
author | Martin Sperl <kernel@martin.sperl.org> | 2016-03-16 15:25:01 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2016-04-15 00:27:22 -0400 |
commit | 388cc7a281c06e484afcc3c5125b3271316209ef (patch) | |
tree | dc56a3a11f76a30547dd9c04a99026aeb6672a80 /drivers/dma/bcm2835-dma.c | |
parent | 4087412258276be37c5660fc6caf5d4e08920193 (diff) |
dmaengine: bcm2835: add slave_sg support to bcm2835-dma
Add slave_sg support to bcm2835-dma using shared allocation
code for bcm2835_desc and DMA-control blocks already used by
dma_cyclic.
Note that bcm2835_dma_callback had to get modified to support
both modes of operation (cyclic and non-cyclic).
Tested using:
* Hifiberry I2S card (using cyclic DMA)
* fb_st7735r SPI-framebuffer (using slave_sg DMA via spi-bcm2835)
playing BigBuckBunny for audio and video.
Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma/bcm2835-dma.c')
-rw-r--r-- | drivers/dma/bcm2835-dma.c | 113 |
1 files changed, 108 insertions, 5 deletions
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 59c5ef36d970..b46b12f66f38 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c | |||
@@ -260,6 +260,23 @@ static void bcm2835_dma_create_cb_set_length( | |||
260 | control_block->info |= finalextrainfo; | 260 | control_block->info |= finalextrainfo; |
261 | } | 261 | } |
262 | 262 | ||
263 | static inline size_t bcm2835_dma_count_frames_for_sg( | ||
264 | struct bcm2835_chan *c, | ||
265 | struct scatterlist *sgl, | ||
266 | unsigned int sg_len) | ||
267 | { | ||
268 | size_t frames = 0; | ||
269 | struct scatterlist *sgent; | ||
270 | unsigned int i; | ||
271 | size_t plength = bcm2835_dma_max_frame_length(c); | ||
272 | |||
273 | for_each_sg(sgl, sgent, sg_len, i) | ||
274 | frames += bcm2835_dma_frames_for_length( | ||
275 | sg_dma_len(sgent), plength); | ||
276 | |||
277 | return frames; | ||
278 | } | ||
279 | |||
263 | /** | 280 | /** |
264 | * bcm2835_dma_create_cb_chain - create a control block and fills data in | 281 | * bcm2835_dma_create_cb_chain - create a control block and fills data in |
265 | * | 282 | * |
@@ -361,6 +378,32 @@ error_cb: | |||
361 | return NULL; | 378 | return NULL; |
362 | } | 379 | } |
363 | 380 | ||
381 | static void bcm2835_dma_fill_cb_chain_with_sg( | ||
382 | struct dma_chan *chan, | ||
383 | enum dma_transfer_direction direction, | ||
384 | struct bcm2835_cb_entry *cb, | ||
385 | struct scatterlist *sgl, | ||
386 | unsigned int sg_len) | ||
387 | { | ||
388 | struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); | ||
389 | size_t max_len = bcm2835_dma_max_frame_length(c); | ||
390 | unsigned int i, len; | ||
391 | dma_addr_t addr; | ||
392 | struct scatterlist *sgent; | ||
393 | |||
394 | for_each_sg(sgl, sgent, sg_len, i) { | ||
395 | for (addr = sg_dma_address(sgent), len = sg_dma_len(sgent); | ||
396 | len > 0; | ||
397 | addr += cb->cb->length, len -= cb->cb->length, cb++) { | ||
398 | if (direction == DMA_DEV_TO_MEM) | ||
399 | cb->cb->dst = addr; | ||
400 | else | ||
401 | cb->cb->src = addr; | ||
402 | cb->cb->length = min(len, max_len); | ||
403 | } | ||
404 | } | ||
405 | } | ||
406 | |||
364 | static int bcm2835_dma_abort(void __iomem *chan_base) | 407 | static int bcm2835_dma_abort(void __iomem *chan_base) |
365 | { | 408 | { |
366 | unsigned long cs; | 409 | unsigned long cs; |
@@ -428,13 +471,19 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data) | |||
428 | d = c->desc; | 471 | d = c->desc; |
429 | 472 | ||
430 | if (d) { | 473 | if (d) { |
431 | /* TODO Only works for cyclic DMA */ | 474 | if (d->cyclic) { |
432 | vchan_cyclic_callback(&d->vd); | 475 | /* call the cyclic callback */ |
476 | vchan_cyclic_callback(&d->vd); | ||
477 | |||
478 | /* Keep the DMA engine running */ | ||
479 | writel(BCM2835_DMA_ACTIVE, | ||
480 | c->chan_base + BCM2835_DMA_CS); | ||
481 | } else { | ||
482 | vchan_cookie_complete(&c->desc->vd); | ||
483 | bcm2835_dma_start_desc(c); | ||
484 | } | ||
433 | } | 485 | } |
434 | 486 | ||
435 | /* Keep the DMA engine running */ | ||
436 | writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS); | ||
437 | |||
438 | spin_unlock_irqrestore(&c->vc.lock, flags); | 487 | spin_unlock_irqrestore(&c->vc.lock, flags); |
439 | 488 | ||
440 | return IRQ_HANDLED; | 489 | return IRQ_HANDLED; |
@@ -548,6 +597,58 @@ static void bcm2835_dma_issue_pending(struct dma_chan *chan) | |||
548 | spin_unlock_irqrestore(&c->vc.lock, flags); | 597 | spin_unlock_irqrestore(&c->vc.lock, flags); |
549 | } | 598 | } |
550 | 599 | ||
600 | static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg( | ||
601 | struct dma_chan *chan, | ||
602 | struct scatterlist *sgl, unsigned int sg_len, | ||
603 | enum dma_transfer_direction direction, | ||
604 | unsigned long flags, void *context) | ||
605 | { | ||
606 | struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); | ||
607 | struct bcm2835_desc *d; | ||
608 | dma_addr_t src = 0, dst = 0; | ||
609 | u32 info = BCM2835_DMA_WAIT_RESP; | ||
610 | u32 extra = BCM2835_DMA_INT_EN; | ||
611 | size_t frames; | ||
612 | |||
613 | if (!is_slave_direction(direction)) { | ||
614 | dev_err(chan->device->dev, | ||
615 | "%s: bad direction?\n", __func__); | ||
616 | return NULL; | ||
617 | } | ||
618 | |||
619 | if (c->dreq != 0) | ||
620 | info |= BCM2835_DMA_PER_MAP(c->dreq); | ||
621 | |||
622 | if (direction == DMA_DEV_TO_MEM) { | ||
623 | if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) | ||
624 | return NULL; | ||
625 | src = c->cfg.src_addr; | ||
626 | info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC; | ||
627 | } else { | ||
628 | if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) | ||
629 | return NULL; | ||
630 | dst = c->cfg.dst_addr; | ||
631 | info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC; | ||
632 | } | ||
633 | |||
634 | /* count frames in sg list */ | ||
635 | frames = bcm2835_dma_count_frames_for_sg(c, sgl, sg_len); | ||
636 | |||
637 | /* allocate the CB chain */ | ||
638 | d = bcm2835_dma_create_cb_chain(chan, direction, false, | ||
639 | info, extra, | ||
640 | frames, src, dst, 0, 0, | ||
641 | GFP_KERNEL); | ||
642 | if (!d) | ||
643 | return NULL; | ||
644 | |||
645 | /* fill in frames with scatterlist pointers */ | ||
646 | bcm2835_dma_fill_cb_chain_with_sg(chan, direction, d->cb_list, | ||
647 | sgl, sg_len); | ||
648 | |||
649 | return vchan_tx_prep(&c->vc, &d->vd, flags); | ||
650 | } | ||
651 | |||
551 | static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( | 652 | static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( |
552 | struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, | 653 | struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, |
553 | size_t period_len, enum dma_transfer_direction direction, | 654 | size_t period_len, enum dma_transfer_direction direction, |
@@ -778,11 +879,13 @@ static int bcm2835_dma_probe(struct platform_device *pdev) | |||
778 | dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); | 879 | dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); |
779 | dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); | 880 | dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); |
780 | dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); | 881 | dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); |
882 | dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); | ||
781 | od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; | 883 | od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; |
782 | od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; | 884 | od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; |
783 | od->ddev.device_tx_status = bcm2835_dma_tx_status; | 885 | od->ddev.device_tx_status = bcm2835_dma_tx_status; |
784 | od->ddev.device_issue_pending = bcm2835_dma_issue_pending; | 886 | od->ddev.device_issue_pending = bcm2835_dma_issue_pending; |
785 | od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; | 887 | od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; |
888 | od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; | ||
786 | od->ddev.device_config = bcm2835_dma_slave_config; | 889 | od->ddev.device_config = bcm2835_dma_slave_config; |
787 | od->ddev.device_terminate_all = bcm2835_dma_terminate_all; | 890 | od->ddev.device_terminate_all = bcm2835_dma_terminate_all; |
788 | od->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); | 891 | od->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); |