struct pl330_xfer *x;
/* Hook to attach to DMAC's list of reqs with due callback */
struct list_head rqd;
+ unsigned int infiniteloop;
};
/*
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)
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)
{
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;
}
id = pl330->events[ev];
+ if (id == -1)
+ continue;
+
thrd = &pl330->channels[id];
active = thrd->req_running;
/* 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);
{
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)
if (!pdmac)
return 0;
- desc = kmalloc(count * sizeof(*desc), flg);
+ desc = kzalloc(count * sizeof(*desc), flg);
if (!desc)
return 0;
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;
unsigned int i;
dma_addr_t dst;
dma_addr_t src;
+ unsigned int *infinite = context;
if (len % period_len != 0)
return NULL;
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)
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)
{
}
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,
}
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");
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};
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;