It makes the code cleaner and allows developer not to worry about cache coherency issues. The price for this is a bit slower access to the descriptor structures.
Signed-off-by: Andy Shevchenko <andriy.shevche...@linux.intel.com> Tested-by: Mika Westerberg <mika.westerb...@linux.intel.com> --- drivers/dma/dw_dmac.c | 81 ++++++++++---------------------------------- drivers/dma/dw_dmac_regs.h | 1 + 2 files changed, 18 insertions(+), 64 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 561bb9a..8f4dac5 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -14,6 +14,7 @@ #include <linux/delay.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> +#include <linux/dmapool.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -98,14 +99,6 @@ static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) /*----------------------------------------------------------------------*/ -/* - * Because we're not relying on writeback from the controller (it may not - * even be configured into the core!) we don't need to use dma_pool. These - * descriptors -- and associated data -- are cacheable. We do need to make - * sure their dcache entries are written back before handing them off to - * the controller, though. - */ - static struct device *chan2dev(struct dma_chan *chan) { return &chan->dev->device; @@ -144,19 +137,6 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) return ret; } -static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) -{ - struct dw_desc *child; - - list_for_each_entry(child, &desc->tx_list, desc_node) - dma_sync_single_for_cpu(chan2parent(&dwc->chan), - child->txd.phys, sizeof(child->lli), - DMA_TO_DEVICE); - dma_sync_single_for_cpu(chan2parent(&dwc->chan), - desc->txd.phys, sizeof(desc->lli), - DMA_TO_DEVICE); -} - /* * Move a descriptor, including any children, to the free list. * `desc' must not be on any lists. @@ -168,8 +148,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) if (desc) { struct dw_desc *child; - dwc_sync_desc_for_cpu(dwc, desc); - spin_lock_irqsave(&dwc->lock, flags); list_for_each_entry(child, &desc->tx_list, desc_node) dev_vdbg(chan2dev(&dwc->chan), @@ -342,8 +320,6 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, param = txd->callback_param; } - dwc_sync_desc_for_cpu(dwc, desc); - /* async_tx_ack */ list_for_each_entry(child, &desc->tx_list, desc_node) async_tx_ack(&child->txd); @@ -777,24 +753,17 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } prev = desc; } - if (flags & DMA_PREP_INTERRUPT) /* Trigger interrupt after last block */ prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); first->txd.flags = flags; first->len = len; @@ -883,10 +852,6 @@ slave_sg_todev_fill_desc: first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, - sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } @@ -945,10 +910,6 @@ slave_sg_fromdev_fill_desc: first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, - sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } @@ -968,9 +929,6 @@ slave_sg_fromdev_fill_desc: prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); first->len = total_len; @@ -1125,7 +1083,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) struct dw_desc *desc; int i; unsigned long flags; - int ret; dev_vdbg(chan2dev(chan), "%s\n", __func__); @@ -1146,21 +1103,21 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&dwc->lock, flags); i = dwc->descs_allocated; while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { + dma_addr_t phys; + spin_unlock_irqrestore(&dwc->lock, flags); - desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); + desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); if (!desc) goto err_desc_alloc; + memset(desc, 0, sizeof(struct dw_desc)); + INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, chan); desc->txd.tx_submit = dwc_tx_submit; desc->txd.flags = DMA_CTRL_ACK; - desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, - sizeof(desc->lli), DMA_TO_DEVICE); - ret = dma_mapping_error(chan2parent(chan), desc->txd.phys); - if (ret) - goto err_desc_alloc; + desc->txd.phys = phys; dwc_desc_put(dwc, desc); @@ -1171,14 +1128,10 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) spin_unlock_irqrestore(&dwc->lock, flags); dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); - return i; err_desc_alloc: - kfree(desc); - dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); - return i; } @@ -1211,9 +1164,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) list_for_each_entry_safe(desc, _desc, &list, desc_node) { dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); - dma_unmap_single(chan2parent(chan), desc->txd.phys, - sizeof(desc->lli), DMA_TO_DEVICE); - kfree(desc); + dma_pool_free(dw->desc_pool, desc, desc->txd.phys); } dev_vdbg(chan2dev(chan), "%s: done\n", __func__); @@ -1458,20 +1409,14 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, desc->lli.ctlhi = (period_len >> reg_width); cdesc->desc[i] = desc; - if (last) { + if (last) last->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - last->txd.phys, sizeof(last->lli), - DMA_TO_DEVICE); - } last = desc; } /* lets make a cyclic list */ last->lli.llp = cdesc->desc[0]->txd.phys; - dma_sync_single_for_device(chan2parent(chan), last->txd.phys, - sizeof(last->lli), DMA_TO_DEVICE); dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " "period %zu periods %d\n", (unsigned long long)buf_addr, @@ -1729,6 +1674,14 @@ static int dw_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dw); + /* create a pool of consistent memory blocks for hardware descriptors */ + dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev, + sizeof(struct dw_desc), 4, 0); + if (!dw->desc_pool) { + dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); + return -ENOMEM; + } + tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); INIT_LIST_HEAD(&dw->dma.channels); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 577f2dd..fef296d 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -235,6 +235,7 @@ static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) struct dw_dma { struct dma_device dma; void __iomem *regs; + struct dma_pool *desc_pool; struct tasklet_struct tasklet; struct clk *clk; -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/