diff options
author | Jean-Francois Moine <moinejf@free.fr> | 2016-04-28 11:13:46 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2016-05-02 06:29:02 -0400 |
commit | a90e173f3faf29d290bc72b957d94765a163b470 (patch) | |
tree | 58322e51b607d75f073cf961a304729342c788ae | |
parent | 3435fb18533c27b288c19cfb7fe783e5433e36aa (diff) |
dmaengine: sun6i: Add cyclic capability
DMA cyclic transfers are required by audio streaming.
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
-rw-r--r-- | drivers/dma/sun6i-dma.c | 129 |
1 files changed, 122 insertions, 7 deletions
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 80e426d9da5d..5065ca43face 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c | |||
@@ -146,6 +146,8 @@ struct sun6i_vchan { | |||
146 | struct dma_slave_config cfg; | 146 | struct dma_slave_config cfg; |
147 | struct sun6i_pchan *phy; | 147 | struct sun6i_pchan *phy; |
148 | u8 port; | 148 | u8 port; |
149 | u8 irq_type; | ||
150 | bool cyclic; | ||
149 | }; | 151 | }; |
150 | 152 | ||
151 | struct sun6i_dma_dev { | 153 | struct sun6i_dma_dev { |
@@ -254,6 +256,30 @@ static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) | |||
254 | return addr_width >> 1; | 256 | return addr_width >> 1; |
255 | } | 257 | } |
256 | 258 | ||
259 | static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan) | ||
260 | { | ||
261 | struct sun6i_desc *txd = pchan->desc; | ||
262 | struct sun6i_dma_lli *lli; | ||
263 | size_t bytes; | ||
264 | dma_addr_t pos; | ||
265 | |||
266 | pos = readl(pchan->base + DMA_CHAN_LLI_ADDR); | ||
267 | bytes = readl(pchan->base + DMA_CHAN_CUR_CNT); | ||
268 | |||
269 | if (pos == LLI_LAST_ITEM) | ||
270 | return bytes; | ||
271 | |||
272 | for (lli = txd->v_lli; lli; lli = lli->v_lli_next) { | ||
273 | if (lli->p_lli_next == pos) { | ||
274 | for (lli = lli->v_lli_next; lli; lli = lli->v_lli_next) | ||
275 | bytes += lli->len; | ||
276 | break; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | return bytes; | ||
281 | } | ||
282 | |||
257 | static void *sun6i_dma_lli_add(struct sun6i_dma_lli *prev, | 283 | static void *sun6i_dma_lli_add(struct sun6i_dma_lli *prev, |
258 | struct sun6i_dma_lli *next, | 284 | struct sun6i_dma_lli *next, |
259 | dma_addr_t next_phy, | 285 | dma_addr_t next_phy, |
@@ -342,8 +368,12 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan) | |||
342 | irq_reg = pchan->idx / DMA_IRQ_CHAN_NR; | 368 | irq_reg = pchan->idx / DMA_IRQ_CHAN_NR; |
343 | irq_offset = pchan->idx % DMA_IRQ_CHAN_NR; | 369 | irq_offset = pchan->idx % DMA_IRQ_CHAN_NR; |
344 | 370 | ||
371 | vchan->irq_type = vchan->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE; | ||
372 | |||
345 | irq_val = readl(sdev->base + DMA_IRQ_EN(irq_reg)); | 373 | irq_val = readl(sdev->base + DMA_IRQ_EN(irq_reg)); |
346 | irq_val |= DMA_IRQ_QUEUE << (irq_offset * DMA_IRQ_CHAN_WIDTH); | 374 | irq_val &= ~((DMA_IRQ_HALF | DMA_IRQ_PKG | DMA_IRQ_QUEUE) << |
375 | (irq_offset * DMA_IRQ_CHAN_WIDTH)); | ||
376 | irq_val |= vchan->irq_type << (irq_offset * DMA_IRQ_CHAN_WIDTH); | ||
347 | writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg)); | 377 | writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg)); |
348 | 378 | ||
349 | writel(pchan->desc->p_lli, pchan->base + DMA_CHAN_LLI_ADDR); | 379 | writel(pchan->desc->p_lli, pchan->base + DMA_CHAN_LLI_ADDR); |
@@ -440,11 +470,12 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id) | |||
440 | writel(status, sdev->base + DMA_IRQ_STAT(i)); | 470 | writel(status, sdev->base + DMA_IRQ_STAT(i)); |
441 | 471 | ||
442 | for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) { | 472 | for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) { |
443 | if (status & DMA_IRQ_QUEUE) { | 473 | pchan = sdev->pchans + j; |
444 | pchan = sdev->pchans + j; | 474 | vchan = pchan->vchan; |
445 | vchan = pchan->vchan; | 475 | if (vchan && (status & vchan->irq_type)) { |
446 | 476 | if (vchan->cyclic) { | |
447 | if (vchan) { | 477 | vchan_cyclic_callback(&pchan->desc->vd); |
478 | } else { | ||
448 | spin_lock(&vchan->vc.lock); | 479 | spin_lock(&vchan->vc.lock); |
449 | vchan_cookie_complete(&pchan->desc->vd); | 480 | vchan_cookie_complete(&pchan->desc->vd); |
450 | pchan->done = pchan->desc; | 481 | pchan->done = pchan->desc; |
@@ -650,6 +681,78 @@ err_lli_free: | |||
650 | return NULL; | 681 | return NULL; |
651 | } | 682 | } |
652 | 683 | ||
684 | static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic( | ||
685 | struct dma_chan *chan, | ||
686 | dma_addr_t buf_addr, | ||
687 | size_t buf_len, | ||
688 | size_t period_len, | ||
689 | enum dma_transfer_direction dir, | ||
690 | unsigned long flags) | ||
691 | { | ||
692 | struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device); | ||
693 | struct sun6i_vchan *vchan = to_sun6i_vchan(chan); | ||
694 | struct dma_slave_config *sconfig = &vchan->cfg; | ||
695 | struct sun6i_dma_lli *v_lli, *prev = NULL; | ||
696 | struct sun6i_desc *txd; | ||
697 | dma_addr_t p_lli; | ||
698 | u32 lli_cfg; | ||
699 | unsigned int i, periods = buf_len / period_len; | ||
700 | int ret; | ||
701 | |||
702 | ret = set_config(sdev, sconfig, dir, &lli_cfg); | ||
703 | if (ret) { | ||
704 | dev_err(chan2dev(chan), "Invalid DMA configuration\n"); | ||
705 | return NULL; | ||
706 | } | ||
707 | |||
708 | txd = kzalloc(sizeof(*txd), GFP_NOWAIT); | ||
709 | if (!txd) | ||
710 | return NULL; | ||
711 | |||
712 | for (i = 0; i < periods; i++) { | ||
713 | v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli); | ||
714 | if (!v_lli) { | ||
715 | dev_err(sdev->slave.dev, "Failed to alloc lli memory\n"); | ||
716 | goto err_lli_free; | ||
717 | } | ||
718 | |||
719 | v_lli->len = period_len; | ||
720 | v_lli->para = NORMAL_WAIT; | ||
721 | |||
722 | if (dir == DMA_MEM_TO_DEV) { | ||
723 | v_lli->src = buf_addr + period_len * i; | ||
724 | v_lli->dst = sconfig->dst_addr; | ||
725 | v_lli->cfg = lli_cfg | | ||
726 | DMA_CHAN_CFG_DST_IO_MODE | | ||
727 | DMA_CHAN_CFG_SRC_LINEAR_MODE | | ||
728 | DMA_CHAN_CFG_SRC_DRQ(DRQ_SDRAM) | | ||
729 | DMA_CHAN_CFG_DST_DRQ(vchan->port); | ||
730 | } else { | ||
731 | v_lli->src = sconfig->src_addr; | ||
732 | v_lli->dst = buf_addr + period_len * i; | ||
733 | v_lli->cfg = lli_cfg | | ||
734 | DMA_CHAN_CFG_DST_LINEAR_MODE | | ||
735 | DMA_CHAN_CFG_SRC_IO_MODE | | ||
736 | DMA_CHAN_CFG_DST_DRQ(DRQ_SDRAM) | | ||
737 | DMA_CHAN_CFG_SRC_DRQ(vchan->port); | ||
738 | } | ||
739 | |||
740 | prev = sun6i_dma_lli_add(prev, v_lli, p_lli, txd); | ||
741 | } | ||
742 | |||
743 | prev->p_lli_next = txd->p_lli; /* cyclic list */ | ||
744 | |||
745 | vchan->cyclic = true; | ||
746 | |||
747 | return vchan_tx_prep(&vchan->vc, &txd->vd, flags); | ||
748 | |||
749 | err_lli_free: | ||
750 | for (prev = txd->v_lli; prev; prev = prev->v_lli_next) | ||
751 | dma_pool_free(sdev->pool, prev, virt_to_phys(prev)); | ||
752 | kfree(txd); | ||
753 | return NULL; | ||
754 | } | ||
755 | |||
653 | static int sun6i_dma_config(struct dma_chan *chan, | 756 | static int sun6i_dma_config(struct dma_chan *chan, |
654 | struct dma_slave_config *config) | 757 | struct dma_slave_config *config) |
655 | { | 758 | { |
@@ -719,6 +822,16 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) | |||
719 | 822 | ||
720 | spin_lock_irqsave(&vchan->vc.lock, flags); | 823 | spin_lock_irqsave(&vchan->vc.lock, flags); |
721 | 824 | ||
825 | if (vchan->cyclic) { | ||
826 | vchan->cyclic = false; | ||
827 | if (pchan && pchan->desc) { | ||
828 | struct virt_dma_desc *vd = &pchan->desc->vd; | ||
829 | struct virt_dma_chan *vc = &vchan->vc; | ||
830 | |||
831 | list_add_tail(&vd->node, &vc->desc_completed); | ||
832 | } | ||
833 | } | ||
834 | |||
722 | vchan_get_all_descriptors(&vchan->vc, &head); | 835 | vchan_get_all_descriptors(&vchan->vc, &head); |
723 | 836 | ||
724 | if (pchan) { | 837 | if (pchan) { |
@@ -766,7 +879,7 @@ static enum dma_status sun6i_dma_tx_status(struct dma_chan *chan, | |||
766 | } else if (!pchan || !pchan->desc) { | 879 | } else if (!pchan || !pchan->desc) { |
767 | bytes = 0; | 880 | bytes = 0; |
768 | } else { | 881 | } else { |
769 | bytes = readl(pchan->base + DMA_CHAN_CUR_CNT); | 882 | bytes = sun6i_get_chan_size(pchan); |
770 | } | 883 | } |
771 | 884 | ||
772 | spin_unlock_irqrestore(&vchan->vc.lock, flags); | 885 | spin_unlock_irqrestore(&vchan->vc.lock, flags); |
@@ -970,6 +1083,7 @@ static int sun6i_dma_probe(struct platform_device *pdev) | |||
970 | dma_cap_set(DMA_PRIVATE, sdc->slave.cap_mask); | 1083 | dma_cap_set(DMA_PRIVATE, sdc->slave.cap_mask); |
971 | dma_cap_set(DMA_MEMCPY, sdc->slave.cap_mask); | 1084 | dma_cap_set(DMA_MEMCPY, sdc->slave.cap_mask); |
972 | dma_cap_set(DMA_SLAVE, sdc->slave.cap_mask); | 1085 | dma_cap_set(DMA_SLAVE, sdc->slave.cap_mask); |
1086 | dma_cap_set(DMA_CYCLIC, sdc->slave.cap_mask); | ||
973 | 1087 | ||
974 | INIT_LIST_HEAD(&sdc->slave.channels); | 1088 | INIT_LIST_HEAD(&sdc->slave.channels); |
975 | sdc->slave.device_free_chan_resources = sun6i_dma_free_chan_resources; | 1089 | sdc->slave.device_free_chan_resources = sun6i_dma_free_chan_resources; |
@@ -977,6 +1091,7 @@ static int sun6i_dma_probe(struct platform_device *pdev) | |||
977 | sdc->slave.device_issue_pending = sun6i_dma_issue_pending; | 1091 | sdc->slave.device_issue_pending = sun6i_dma_issue_pending; |
978 | sdc->slave.device_prep_slave_sg = sun6i_dma_prep_slave_sg; | 1092 | sdc->slave.device_prep_slave_sg = sun6i_dma_prep_slave_sg; |
979 | sdc->slave.device_prep_dma_memcpy = sun6i_dma_prep_dma_memcpy; | 1093 | sdc->slave.device_prep_dma_memcpy = sun6i_dma_prep_dma_memcpy; |
1094 | sdc->slave.device_prep_dma_cyclic = sun6i_dma_prep_dma_cyclic; | ||
980 | sdc->slave.copy_align = DMAENGINE_ALIGN_4_BYTES; | 1095 | sdc->slave.copy_align = DMAENGINE_ALIGN_4_BYTES; |
981 | sdc->slave.device_config = sun6i_dma_config; | 1096 | sdc->slave.device_config = sun6i_dma_config; |
982 | sdc->slave.device_pause = sun6i_dma_pause; | 1097 | sdc->slave.device_pause = sun6i_dma_pause; |