From 127d33d1366fbcffddac301563af9c1c8eaca33a Mon Sep 17 00:00:00 2001 From: qjb Date: Wed, 4 Jun 2014 18:17:08 +0800 Subject: [PATCH] dma soc audio : add support dma infiniteloop mode soc audio rockchip default use infiniteloop mode --- drivers/dma/pl330.c | 173 ++++++++++++++++++++++++++++++---- include/linux/dmaengine.h | 14 ++- sound/soc/soc-dmaengine-pcm.c | 17 +++- 3 files changed, 184 insertions(+), 20 deletions(-) mode change 100644 => 100755 drivers/dma/pl330.c mode change 100644 => 100755 include/linux/dmaengine.h diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c old mode 100644 new mode 100755 index 9ca6f7df9ec1..6cebbd10c9b1 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -396,6 +396,7 @@ struct pl330_req { struct pl330_xfer *x; /* Hook to attach to DMAC's list of reqs with due callback */ struct list_head rqd; + unsigned int infiniteloop; }; /* @@ -1349,6 +1350,77 @@ static int _bursts(unsigned dry_run, u8 buf[], return off; } +/* Returns bytes consumed */ +static inline int _loop_infiniteloop(unsigned dry_run, u8 buf[], + unsigned long bursts, const struct _xfer_spec *pxs, int ev) +{ + int cyc, off; + unsigned lcnt0, lcnt1, ljmp0, ljmp1, ljmpfe; + struct _arg_LPEND lpend; + + off = 0; + ljmpfe = off; + lcnt0 = pxs->r->infiniteloop; + + if (bursts > 256) { + lcnt1 = 256; + cyc = bursts / 256; + } else { + lcnt1 = bursts; + cyc = 1; + } + + /* forever loop */ + off += _emit_MOV(dry_run, &buf[off], SAR, pxs->x->src_addr); + off += _emit_MOV(dry_run, &buf[off], DAR, pxs->x->dst_addr); + if (pxs->r->rqtype != MEMTOMEM) + off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri); + + /* loop0 */ + off += _emit_LP(dry_run, &buf[off], 0, lcnt0); + ljmp0 = off; + + /* loop1 */ + off += _emit_LP(dry_run, &buf[off], 1, lcnt1); + ljmp1 = off; + off += _bursts(dry_run, &buf[off], pxs, cyc); + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 1; + lpend.bjump = off - ljmp1; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + + /* remainder */ + lcnt1 = bursts - (lcnt1 * cyc); + + if (lcnt1) { + off += _emit_LP(dry_run, &buf[off], 1, lcnt1); + ljmp1 = off; + off += _bursts(dry_run, &buf[off], pxs, 1); + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 1; + lpend.bjump = off - ljmp1; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + } + + off += _emit_SEV(dry_run, &buf[off], ev); + + lpend.cond = ALWAYS; + lpend.forever = false; + lpend.loop = 0; + lpend.bjump = off - ljmp0; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + + lpend.cond = ALWAYS; + lpend.forever = true; + lpend.loop = 1; + lpend.bjump = off - ljmpfe; + off += _emit_LPEND(dry_run, &buf[off], &lpend); + + return off; +} + /* Returns bytes consumed and updates bursts */ static inline int _loop(unsigned dry_run, u8 buf[], unsigned long *bursts, const struct _xfer_spec *pxs) @@ -1428,6 +1500,20 @@ static inline int _loop(unsigned dry_run, u8 buf[], return off; } +static inline int _setup_xfer_infiniteloop(unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int ev) +{ + struct pl330_xfer *x = pxs->x; + u32 ccr = pxs->ccr; + unsigned long bursts = BYTE_TO_BURST(x->bytes, ccr); + int off = 0; + + /* Setup Loop(s) */ + off += _loop_infiniteloop(dry_run, &buf[off], bursts, pxs, ev); + + return off; +} + static inline int _setup_loops(unsigned dry_run, u8 buf[], const struct _xfer_spec *pxs) { @@ -1480,21 +1566,32 @@ static int _setup_req(unsigned dry_run, struct pl330_thread *thrd, off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); x = pxs->r->x; - do { + if (!pxs->r->infiniteloop) { + do { + /* Error if xfer length is not aligned at burst size */ + if (x->bytes % (BRST_SIZE(pxs->ccr) * + BRST_LEN(pxs->ccr))) + return -EINVAL; + + pxs->x = x; + off += _setup_xfer(dry_run, &buf[off], pxs); + + x = x->next; + } while (x); + + /* DMASEV peripheral/event */ + off += _emit_SEV(dry_run, &buf[off], thrd->ev); + /* DMAEND */ + off += _emit_END(dry_run, &buf[off]); + } else { /* Error if xfer length is not aligned at burst size */ if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) return -EINVAL; pxs->x = x; - off += _setup_xfer(dry_run, &buf[off], pxs); - - x = x->next; - } while (x); - - /* DMASEV peripheral/event */ - off += _emit_SEV(dry_run, &buf[off], thrd->ev); - /* DMAEND */ - off += _emit_END(dry_run, &buf[off]); + off += _setup_xfer_infiniteloop(dry_run, &buf[off], + pxs, thrd->ev); + } return off; } @@ -1772,6 +1869,9 @@ static int pl330_update(const struct pl330_info *pi) id = pl330->events[ev]; + if (id == -1) + continue; + thrd = &pl330->channels[id]; active = thrd->req_running; @@ -1780,12 +1880,16 @@ static int pl330_update(const struct pl330_info *pi) /* Detach the req */ rqdone = thrd->req[active].r; - thrd->req[active].r = NULL; + if (!rqdone->infiniteloop) { - mark_free(thrd, active); + /* Detach the req */ + thrd->req[active].r = NULL; - /* Get going again ASAP */ - _start(thrd); + mark_free(thrd, active); + + /* Get going again ASAP */ + _start(thrd); + } /* For now, just make a list of callbacks to be done */ list_add_tail(&rqdone->rqd, &pl330->req_done); @@ -1936,11 +2040,21 @@ static inline void _free_event(struct pl330_thread *thrd, int ev) { struct pl330_dmac *pl330 = thrd->dmac; struct pl330_info *pi = pl330->pinfo; + void __iomem *regs = pi->base; + u32 inten = readl(regs + INTEN); /* If the event is valid and was held by the thread */ if (ev >= 0 && ev < pi->pcfg.num_events - && pl330->events[ev] == thrd->id) + && pl330->events[ev] == thrd->id) { pl330->events[ev] = -1; + + if (readl(regs + ES) & (1 << ev)) { + if (!(inten & (1 << ev))) + writel(inten | (1 << ev), regs + INTEN); + writel(1 << ev, regs + INTCLR); + writel(inten & ~(1 << ev) , regs + INTEN); + } + } } static void pl330_release_channel(void *ch_id) @@ -2599,7 +2713,7 @@ static int add_desc(struct dma_pl330_dmac *pdmac, gfp_t flg, int count) if (!pdmac) return 0; - desc = kmalloc(count * sizeof(*desc), flg); + desc = kzalloc(count * sizeof(*desc), flg); if (!desc) return 0; @@ -2669,6 +2783,7 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) desc->txd.cookie = 0; async_tx_ack(&desc->txd); + desc->req.infiniteloop = 0; desc->req.peri = peri_id ? pch->chan.chan_id : 0; desc->rqcfg.pcfg = &pch->dmac->pif.pcfg; @@ -2748,6 +2863,7 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( unsigned int i; dma_addr_t dst; dma_addr_t src; + unsigned int *infinite = context; if (len % period_len != 0) return NULL; @@ -2803,6 +2919,7 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( desc->rqcfg.brst_size = pch->burst_sz; desc->rqcfg.brst_len = 1; + desc->req.infiniteloop = *infinite; fill_px(&desc->px, dst, src, period_len); if (!first) @@ -2946,6 +3063,28 @@ static irqreturn_t pl330_irq_handler(int irq, void *data) return IRQ_NONE; } +int pl330_dma_getposition(struct dma_chan *chan, + dma_addr_t *src, dma_addr_t *dst) +{ + struct dma_pl330_chan *pch = to_pchan(chan); + struct pl330_info *pi; + void __iomem *regs; + struct pl330_thread *thrd; + + if (unlikely(!pch)) + return -EINVAL; + + thrd = pch->pl330_chid; + pi = &pch->dmac->pif; + regs = pi->base; + + *src = readl(regs + SA(thrd->id)); + *dst = readl(regs + DA(thrd->id)); + + return 0; +} +EXPORT_SYMBOL(pl330_dma_getposition); + static int pl330_probe(struct amba_device *adev, const struct amba_id *id) { @@ -3066,7 +3205,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) } dev_info(&adev->dev, - "Loaded driver for PL330 DMAC-%x\n", adev->periphid); + "Loaded driver for PL330 DMAC-%d\n", adev->periphid); dev_info(&adev->dev, "\tDBUFF-%ux%ubytes Num_Chans-%u Num_Peri-%u Num_Events-%u\n", pi->pcfg.data_buf_dep, diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h old mode 100644 new mode 100755 index 96d3e4ab11a9..637f2130cf28 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -664,9 +664,21 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( size_t period_len, enum dma_transfer_direction dir, unsigned long flags) { + unsigned int t=0; return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len, - period_len, dir, flags, NULL); + period_len, dir, flags, &t); } +#ifdef CONFIG_ARCH_ROCKCHIP +static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_infiniteloop( + struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction dir, + unsigned long flags,unsigned int limit) +{ + unsigned int t=limit; + return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len, + period_len, dir, flags, &t); +} +#endif static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma( struct dma_chan *chan, struct dma_interleaved_template *xt, diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c index d186e7cc28f2..a6f73312e508 100755 --- a/sound/soc/soc-dmaengine-pcm.c +++ b/sound/soc/soc-dmaengine-pcm.c @@ -132,7 +132,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data( } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data); -#if CONFIG_ARCH_ROCKCHIP +#ifdef CONFIG_ARCH_ROCKCHIP static int debug_audio_timeout = 0; module_param(debug_audio_timeout, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(debug_audio_timeout, "Debug interface Audio DMA buffdone time out"); @@ -142,7 +142,7 @@ static void dmaengine_pcm_dma_complete(void *arg) struct snd_pcm_substream *substream = arg; struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); -#if CONFIG_ARCH_ROCKCHIP +#ifdef CONFIG_ARCH_ROCKCHIP if(debug_audio_timeout){ struct snd_pcm_runtime *runtime = substream->runtime; static ktime_t before = {0},after = {0}; @@ -180,10 +180,23 @@ static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) flags |= DMA_PREP_INTERRUPT; prtd->pos = 0; +#if CONFIG_ARCH_ROCKCHIP + //printk("soc dma buffersize = %d , periodsize=%d, periods=%d\n", + // snd_pcm_lib_buffer_bytes(substream), + // snd_pcm_lib_period_bytes(substream), + // snd_pcm_lib_buffer_bytes(substream)/snd_pcm_lib_period_bytes(substream)); + desc = dmaengine_prep_dma_infiniteloop(chan, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), + direction, flags, + snd_pcm_lib_buffer_bytes(substream)/snd_pcm_lib_period_bytes(substream)); +#else desc = dmaengine_prep_dma_cyclic(chan, substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream), direction, flags); +#endif if (!desc) return -ENOMEM; -- 2.34.1