ASoC: rsnd: add dma.c for Audio DMAC / Audio DMAC peri peri
Renesas sound driver had been assumed that Audio DMAC / Audio DMAC peri peri are implemented by DMAEngine. But, result of DMA ML discussion, it was concluded that it is not a general purpose DMAC. And it should be moved from current DMAEngine to rsnd driver. So, Audio DMAC peri peri become non DMAEngine. OTOH, ALSA SoC has soc-generic-dmaengine-pcm implementation. but it seems difficult to use this generic implementation on rsnd driver at this point, since it needs to fallback to PIO mode if DMA can't use. and additionally it needs 2 DMAC (= Audio DMAC / Audio DMAC peri peri). These are not "generic" feature. Of course I will try to use this generic dmaengine in the future somehow, but just use current style at this point until it can formally use 2 DMACs. This patch adds new dma.c and moves current dma code to dma.c from core.c. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Родитель
c5212b4556
Коммит
bfe834be95
|
@ -1,2 +1,2 @@
|
|||
snd-soc-rcar-objs := core.o gen.o src.o adg.o ssi.o dvc.o
|
||||
snd-soc-rcar-objs := core.o gen.o dma.o src.o adg.o ssi.o dvc.o
|
||||
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
|
|
@ -160,224 +160,6 @@ void rsnd_mod_init(struct rsnd_mod *mod,
|
|||
mod->clk = clk;
|
||||
}
|
||||
|
||||
/*
|
||||
* rsnd_dma functions
|
||||
*/
|
||||
void rsnd_dma_stop(struct rsnd_dma *dma)
|
||||
{
|
||||
dmaengine_terminate_all(dma->chan);
|
||||
}
|
||||
|
||||
static void rsnd_dma_complete(void *data)
|
||||
{
|
||||
struct rsnd_dma *dma = (struct rsnd_dma *)data;
|
||||
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
|
||||
/*
|
||||
* Renesas sound Gen1 needs 1 DMAC,
|
||||
* Gen2 needs 2 DMAC.
|
||||
* In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
|
||||
* But, Audio-DMAC-peri-peri doesn't have interrupt,
|
||||
* and this driver is assuming that here.
|
||||
*
|
||||
* If Audio-DMAC-peri-peri has interrpt,
|
||||
* rsnd_dai_pointer_update() will be called twice,
|
||||
* ant it will breaks io->byte_pos
|
||||
*/
|
||||
|
||||
rsnd_dai_pointer_update(io, io->byte_per_period);
|
||||
}
|
||||
|
||||
void rsnd_dma_start(struct rsnd_dma *dma)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
struct snd_pcm_substream *substream = io->substream;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
|
||||
desc = dmaengine_prep_dma_cyclic(dma->chan,
|
||||
(dma->addr) ? dma->addr :
|
||||
substream->runtime->dma_addr,
|
||||
snd_pcm_lib_buffer_bytes(substream),
|
||||
snd_pcm_lib_period_bytes(substream),
|
||||
dma->dir,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
|
||||
if (!desc) {
|
||||
dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
|
||||
return;
|
||||
}
|
||||
|
||||
desc->callback = rsnd_dma_complete;
|
||||
desc->callback_param = dma;
|
||||
|
||||
if (dmaengine_submit(desc) < 0) {
|
||||
dev_err(dev, "dmaengine_submit() fail\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dma_async_issue_pending(dma->chan);
|
||||
}
|
||||
|
||||
#define DMA_NAME_SIZE 16
|
||||
#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */
|
||||
static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod)
|
||||
{
|
||||
if (mod)
|
||||
return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d",
|
||||
rsnd_mod_dma_name(mod), rsnd_mod_id(mod));
|
||||
else
|
||||
return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem");
|
||||
|
||||
}
|
||||
|
||||
static void rsnd_dma_of_name(struct rsnd_mod *mod_from,
|
||||
struct rsnd_mod *mod_to,
|
||||
char *dma_name)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
index = _rsnd_dma_of_name(dma_name + index, mod_from);
|
||||
*(dma_name + index++) = '_';
|
||||
index = _rsnd_dma_of_name(dma_name + index, mod_to);
|
||||
}
|
||||
|
||||
static void rsnd_dma_of_path(struct rsnd_dma *dma,
|
||||
int is_play,
|
||||
struct rsnd_mod **mod_from,
|
||||
struct rsnd_mod **mod_to)
|
||||
{
|
||||
struct rsnd_mod *this = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
|
||||
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
|
||||
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
||||
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
|
||||
struct rsnd_mod *mod[MOD_MAX];
|
||||
int i, index;
|
||||
|
||||
|
||||
for (i = 0; i < MOD_MAX; i++)
|
||||
mod[i] = NULL;
|
||||
|
||||
/*
|
||||
* in play case...
|
||||
*
|
||||
* src -> dst
|
||||
*
|
||||
* mem -> SSI
|
||||
* mem -> SRC -> SSI
|
||||
* mem -> SRC -> DVC -> SSI
|
||||
*/
|
||||
mod[0] = NULL; /* for "mem" */
|
||||
index = 1;
|
||||
for (i = 1; i < MOD_MAX; i++) {
|
||||
if (!src) {
|
||||
mod[i] = ssi;
|
||||
} else if (!dvc) {
|
||||
mod[i] = src;
|
||||
src = NULL;
|
||||
} else {
|
||||
if ((!is_play) && (this == src))
|
||||
this = dvc;
|
||||
|
||||
mod[i] = (is_play) ? src : dvc;
|
||||
i++;
|
||||
mod[i] = (is_play) ? dvc : src;
|
||||
src = NULL;
|
||||
dvc = NULL;
|
||||
}
|
||||
|
||||
if (mod[i] == this)
|
||||
index = i;
|
||||
|
||||
if (mod[i] == ssi)
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_play) {
|
||||
*mod_from = mod[index - 1];
|
||||
*mod_to = mod[index];
|
||||
} else {
|
||||
*mod_from = mod[index];
|
||||
*mod_to = mod[index - 1];
|
||||
}
|
||||
}
|
||||
|
||||
int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
|
||||
{
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct dma_slave_config cfg;
|
||||
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_mod *mod_from;
|
||||
struct rsnd_mod *mod_to;
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
char dma_name[DMA_NAME_SIZE];
|
||||
dma_cap_mask_t mask;
|
||||
int ret;
|
||||
|
||||
if (dma->chan) {
|
||||
dev_err(dev, "it already has dma channel\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to);
|
||||
rsnd_dma_of_name(mod_from, mod_to, dma_name);
|
||||
|
||||
cfg.slave_id = id;
|
||||
cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
|
||||
cfg.src_addr = rsnd_gen_dma_addr(priv, mod_from, is_play, 1);
|
||||
cfg.dst_addr = rsnd_gen_dma_addr(priv, mod_to, is_play, 0);
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
||||
dev_dbg(dev, "dma : %s %pad -> %pad\n",
|
||||
dma_name, &cfg.src_addr, &cfg.dst_addr);
|
||||
|
||||
dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
|
||||
(void *)id, dev,
|
||||
dma_name);
|
||||
if (!dma->chan) {
|
||||
dev_err(dev, "can't get dma channel\n");
|
||||
goto rsnd_dma_channel_err;
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(dma->chan, &cfg);
|
||||
if (ret < 0)
|
||||
goto rsnd_dma_init_err;
|
||||
|
||||
dma->addr = is_play ? cfg.src_addr : cfg.dst_addr;
|
||||
dma->dir = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
|
||||
|
||||
return 0;
|
||||
|
||||
rsnd_dma_init_err:
|
||||
rsnd_dma_quit(dma);
|
||||
rsnd_dma_channel_err:
|
||||
|
||||
/*
|
||||
* DMA failed. try to PIO mode
|
||||
* see
|
||||
* rsnd_ssi_fallback()
|
||||
* rsnd_rdai_continuance_probe()
|
||||
*/
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
void rsnd_dma_quit(struct rsnd_dma *dma)
|
||||
{
|
||||
if (dma->chan)
|
||||
dma_release_channel(dma->chan);
|
||||
|
||||
dma->chan = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* settting function
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* Renesas R-Car Audio DMAC support
|
||||
*
|
||||
* Copyright (C) 2015 Renesas Electronics Corp.
|
||||
* Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include "rsnd.h"
|
||||
|
||||
static void rsnd_dma_complete(void *data)
|
||||
{
|
||||
struct rsnd_dma *dma = (struct rsnd_dma *)data;
|
||||
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
|
||||
/*
|
||||
* Renesas sound Gen1 needs 1 DMAC,
|
||||
* Gen2 needs 2 DMAC.
|
||||
* In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
|
||||
* But, Audio-DMAC-peri-peri doesn't have interrupt,
|
||||
* and this driver is assuming that here.
|
||||
*
|
||||
* If Audio-DMAC-peri-peri has interrpt,
|
||||
* rsnd_dai_pointer_update() will be called twice,
|
||||
* ant it will breaks io->byte_pos
|
||||
*/
|
||||
|
||||
rsnd_dai_pointer_update(io, io->byte_per_period);
|
||||
}
|
||||
|
||||
#define DMA_NAME_SIZE 16
|
||||
#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */
|
||||
static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod)
|
||||
{
|
||||
if (mod)
|
||||
return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d",
|
||||
rsnd_mod_dma_name(mod), rsnd_mod_id(mod));
|
||||
else
|
||||
return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem");
|
||||
|
||||
}
|
||||
|
||||
static void rsnd_dma_of_name(struct rsnd_mod *mod_from,
|
||||
struct rsnd_mod *mod_to,
|
||||
char *dma_name)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
index = _rsnd_dma_of_name(dma_name + index, mod_from);
|
||||
*(dma_name + index++) = '_';
|
||||
index = _rsnd_dma_of_name(dma_name + index, mod_to);
|
||||
}
|
||||
|
||||
void rsnd_dma_stop(struct rsnd_dma *dma)
|
||||
{
|
||||
dmaengine_terminate_all(dma->chan);
|
||||
}
|
||||
|
||||
void rsnd_dma_start(struct rsnd_dma *dma)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
struct snd_pcm_substream *substream = io->substream;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
|
||||
desc = dmaengine_prep_dma_cyclic(dma->chan,
|
||||
(dma->addr) ? dma->addr :
|
||||
substream->runtime->dma_addr,
|
||||
snd_pcm_lib_buffer_bytes(substream),
|
||||
snd_pcm_lib_period_bytes(substream),
|
||||
dma->dir,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
|
||||
if (!desc) {
|
||||
dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
|
||||
return;
|
||||
}
|
||||
|
||||
desc->callback = rsnd_dma_complete;
|
||||
desc->callback_param = dma;
|
||||
|
||||
if (dmaengine_submit(desc) < 0) {
|
||||
dev_err(dev, "dmaengine_submit() fail\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dma_async_issue_pending(dma->chan);
|
||||
}
|
||||
|
||||
static void rsnd_dma_of_path(struct rsnd_dma *dma,
|
||||
int is_play,
|
||||
struct rsnd_mod **mod_from,
|
||||
struct rsnd_mod **mod_to);
|
||||
|
||||
int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
|
||||
{
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct dma_slave_config cfg = {};
|
||||
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_mod *mod_from;
|
||||
struct rsnd_mod *mod_to;
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
char dma_name[DMA_NAME_SIZE];
|
||||
dma_cap_mask_t mask;
|
||||
int ret;
|
||||
|
||||
if (dma->chan) {
|
||||
dev_err(dev, "it already has dma channel\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to);
|
||||
rsnd_dma_of_name(mod_from, mod_to, dma_name);
|
||||
|
||||
cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
|
||||
cfg.src_addr = rsnd_gen_dma_addr(priv, mod_from, is_play, 1);
|
||||
cfg.dst_addr = rsnd_gen_dma_addr(priv, mod_to, is_play, 0);
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
||||
dev_dbg(dev, "dma : %s %pad -> %pad\n",
|
||||
dma_name, &cfg.src_addr, &cfg.dst_addr);
|
||||
|
||||
dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
|
||||
(void *)id, dev,
|
||||
dma_name);
|
||||
if (!dma->chan) {
|
||||
dev_err(dev, "can't get dma channel\n");
|
||||
goto rsnd_dma_channel_err;
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(dma->chan, &cfg);
|
||||
if (ret < 0)
|
||||
goto rsnd_dma_init_err;
|
||||
|
||||
dma->addr = is_play ? cfg.src_addr : cfg.dst_addr;
|
||||
dma->dir = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
|
||||
|
||||
return 0;
|
||||
|
||||
rsnd_dma_init_err:
|
||||
rsnd_dma_quit(dma);
|
||||
rsnd_dma_channel_err:
|
||||
|
||||
/*
|
||||
* DMA failed. try to PIO mode
|
||||
* see
|
||||
* rsnd_ssi_fallback()
|
||||
* rsnd_rdai_continuance_probe()
|
||||
*/
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
void rsnd_dma_quit(struct rsnd_dma *dma)
|
||||
{
|
||||
if (dma->chan)
|
||||
dma_release_channel(dma->chan);
|
||||
|
||||
dma->chan = NULL;
|
||||
}
|
||||
|
||||
static void rsnd_dma_of_path(struct rsnd_dma *dma,
|
||||
int is_play,
|
||||
struct rsnd_mod **mod_from,
|
||||
struct rsnd_mod **mod_to)
|
||||
{
|
||||
struct rsnd_mod *this = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
|
||||
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
|
||||
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
||||
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
|
||||
struct rsnd_mod *mod[MOD_MAX];
|
||||
int i, index;
|
||||
|
||||
|
||||
for (i = 0; i < MOD_MAX; i++)
|
||||
mod[i] = NULL;
|
||||
|
||||
/*
|
||||
* in play case...
|
||||
*
|
||||
* src -> dst
|
||||
*
|
||||
* mem -> SSI
|
||||
* mem -> SRC -> SSI
|
||||
* mem -> SRC -> DVC -> SSI
|
||||
*/
|
||||
mod[0] = NULL; /* for "mem" */
|
||||
index = 1;
|
||||
for (i = 1; i < MOD_MAX; i++) {
|
||||
if (!src) {
|
||||
mod[i] = ssi;
|
||||
} else if (!dvc) {
|
||||
mod[i] = src;
|
||||
src = NULL;
|
||||
} else {
|
||||
if ((!is_play) && (this == src))
|
||||
this = dvc;
|
||||
|
||||
mod[i] = (is_play) ? src : dvc;
|
||||
i++;
|
||||
mod[i] = (is_play) ? dvc : src;
|
||||
src = NULL;
|
||||
dvc = NULL;
|
||||
}
|
||||
|
||||
if (mod[i] == this)
|
||||
index = i;
|
||||
|
||||
if (mod[i] == ssi)
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_play) {
|
||||
*mod_from = mod[index - 1];
|
||||
*mod_to = mod[index];
|
||||
} else {
|
||||
*mod_from = mod[index];
|
||||
*mod_to = mod[index - 1];
|
||||
}
|
||||
}
|
||||
|
Загрузка…
Ссылка в новой задаче