mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
dma soc audio : add support dma infiniteloop mode
soc audio rockchip default use infiniteloop mode
This commit is contained in:
173
drivers/dma/pl330.c
Normal file → Executable file
173
drivers/dma/pl330.c
Normal file → Executable 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
14
include/linux/dmaengine.h
Normal file → Executable 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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user