aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma
diff options
context:
space:
mode:
authorQiao Zhou <zhouqiao@marvell.com>2015-03-02 20:16:08 -0500
committerVinod Koul <vinod.koul@intel.com>2015-03-04 07:33:15 -0500
commit1eed601a5b02a1f0bbabd155aeea7879fc3708eb (patch)
tree3fdb72af5a6723192d9a9e1832b0e49996519693 /drivers/dma
parentb6d1778bc5485c55c6f5194b8b2ea84c0ce5adad (diff)
dma: mmp-tdma: refine dma disable and dma-pos update
Below are the refinements. 1. Set DMA abort bit when disabling dma channel. This will clear the remaining data in dma FIFO, to fix channel-swap issue. 2. Read DMA HW pointer when updating DMA status. Previously dma position is calculated by adding one period size in dma interrupt. This is inaccurate/insufficient for some high-quality audio APP. Since interrupt bottom half handler has variable schedule delay, it causes big error when calculating sample delay. Read the actual HW pointer and feedback can improve the accuracy. 3. Do some minor code clean. Signed-off-by: Qiao Zhou <zhouqiao@marvell.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/mmp_tdma.c31
1 files changed, 25 insertions, 6 deletions
diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c
index 70c2fa9963cd..b6f4e1fc9c78 100644
--- a/drivers/dma/mmp_tdma.c
+++ b/drivers/dma/mmp_tdma.c
@@ -110,7 +110,7 @@ struct mmp_tdma_chan {
110 struct tasklet_struct tasklet; 110 struct tasklet_struct tasklet;
111 111
112 struct mmp_tdma_desc *desc_arr; 112 struct mmp_tdma_desc *desc_arr;
113 phys_addr_t desc_arr_phys; 113 dma_addr_t desc_arr_phys;
114 int desc_num; 114 int desc_num;
115 enum dma_transfer_direction dir; 115 enum dma_transfer_direction dir;
116 dma_addr_t dev_addr; 116 dma_addr_t dev_addr;
@@ -166,9 +166,12 @@ static void mmp_tdma_enable_chan(struct mmp_tdma_chan *tdmac)
166static int mmp_tdma_disable_chan(struct dma_chan *chan) 166static int mmp_tdma_disable_chan(struct dma_chan *chan)
167{ 167{
168 struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); 168 struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
169 u32 tdcr;
169 170
170 writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN, 171 tdcr = readl(tdmac->reg_base + TDCR);
171 tdmac->reg_base + TDCR); 172 tdcr |= TDCR_ABR;
173 tdcr &= ~TDCR_CHANEN;
174 writel(tdcr, tdmac->reg_base + TDCR);
172 175
173 tdmac->status = DMA_COMPLETE; 176 tdmac->status = DMA_COMPLETE;
174 177
@@ -296,12 +299,27 @@ static int mmp_tdma_clear_chan_irq(struct mmp_tdma_chan *tdmac)
296 return -EAGAIN; 299 return -EAGAIN;
297} 300}
298 301
302static size_t mmp_tdma_get_pos(struct mmp_tdma_chan *tdmac)
303{
304 size_t reg;
305
306 if (tdmac->idx == 0) {
307 reg = __raw_readl(tdmac->reg_base + TDSAR);
308 reg -= tdmac->desc_arr[0].src_addr;
309 } else if (tdmac->idx == 1) {
310 reg = __raw_readl(tdmac->reg_base + TDDAR);
311 reg -= tdmac->desc_arr[0].dst_addr;
312 } else
313 return -EINVAL;
314
315 return reg;
316}
317
299static irqreturn_t mmp_tdma_chan_handler(int irq, void *dev_id) 318static irqreturn_t mmp_tdma_chan_handler(int irq, void *dev_id)
300{ 319{
301 struct mmp_tdma_chan *tdmac = dev_id; 320 struct mmp_tdma_chan *tdmac = dev_id;
302 321
303 if (mmp_tdma_clear_chan_irq(tdmac) == 0) { 322 if (mmp_tdma_clear_chan_irq(tdmac) == 0) {
304 tdmac->pos = (tdmac->pos + tdmac->period_len) % tdmac->buf_len;
305 tasklet_schedule(&tdmac->tasklet); 323 tasklet_schedule(&tdmac->tasklet);
306 return IRQ_HANDLED; 324 return IRQ_HANDLED;
307 } else 325 } else
@@ -343,7 +361,7 @@ static void mmp_tdma_free_descriptor(struct mmp_tdma_chan *tdmac)
343 int size = tdmac->desc_num * sizeof(struct mmp_tdma_desc); 361 int size = tdmac->desc_num * sizeof(struct mmp_tdma_desc);
344 362
345 gpool = tdmac->pool; 363 gpool = tdmac->pool;
346 if (tdmac->desc_arr) 364 if (gpool && tdmac->desc_arr)
347 gen_pool_free(gpool, (unsigned long)tdmac->desc_arr, 365 gen_pool_free(gpool, (unsigned long)tdmac->desc_arr,
348 size); 366 size);
349 tdmac->desc_arr = NULL; 367 tdmac->desc_arr = NULL;
@@ -499,6 +517,7 @@ static enum dma_status mmp_tdma_tx_status(struct dma_chan *chan,
499{ 517{
500 struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); 518 struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
501 519
520 tdmac->pos = mmp_tdma_get_pos(tdmac);
502 dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, 521 dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie,
503 tdmac->buf_len - tdmac->pos); 522 tdmac->buf_len - tdmac->pos);
504 523
@@ -610,7 +629,7 @@ static int mmp_tdma_probe(struct platform_device *pdev)
610 int i, ret; 629 int i, ret;
611 int irq = 0, irq_num = 0; 630 int irq = 0, irq_num = 0;
612 int chan_num = TDMA_CHANNEL_NUM; 631 int chan_num = TDMA_CHANNEL_NUM;
613 struct gen_pool *pool; 632 struct gen_pool *pool = NULL;
614 633
615 of_id = of_match_device(mmp_tdma_dt_ids, &pdev->dev); 634 of_id = of_match_device(mmp_tdma_dt_ids, &pdev->dev);
616 if (of_id) 635 if (of_id)