dma soc audio : add support dma infiniteloop mode

soc audio rockchip default use infiniteloop mode
This commit is contained in:
qjb
2014-06-04 18:17:08 +08:00
parent c519772e80
commit 127d33d136
3 changed files with 184 additions and 20 deletions

173
drivers/dma/pl330.c Normal file → Executable file
View File

@@ -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,

14
include/linux/dmaengine.h Normal file → Executable file
View File

@@ -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,

View File

@@ -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;