dma soc audio : add support dma infiniteloop mode
authorqjb <qjb@rock-chips.com>
Wed, 4 Jun 2014 10:17:08 +0000 (18:17 +0800)
committerqjb <qjb@rock-chips.com>
Wed, 4 Jun 2014 10:17:08 +0000 (18:17 +0800)
soc audio rockchip default use infiniteloop mode

drivers/dma/pl330.c [changed mode: 0644->0755]
include/linux/dmaengine.h [changed mode: 0644->0755]
sound/soc/soc-dmaengine-pcm.c

old mode 100644 (file)
new mode 100755 (executable)
index 9ca6f7d..6cebbd1
@@ -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,
old mode 100644 (file)
new mode 100755 (executable)
index 96d3e4a..637f213
@@ -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,
index d186e7cc28f25a71b1ad91defa177a6be28d8ff9..a6f73312e508d29ea848f947cf192e8906677640 100755 (executable)
@@ -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;