提交 849fc82a 编写于 作者: K Kuninori Morimoto 提交者: Mark Brown

ASoC: rsnd: SSI supports DMA transfer

This patch adds DMAEngine transfer on SSI.
But, it transfers sound data from memory to SSI directly
without using HPBIF at this time.
It will be updated soon
Signed-off-by: NKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: NMark Brown <broonie@linaro.org>
上级 0a4d94c0
......@@ -39,13 +39,14 @@
#define RSND_SSI_PLAY (1 << 24)
#define RSND_SSI_SET(_dai_id, _pio_irq, _flags) \
{ .dai_id = _dai_id, .pio_irq = _pio_irq, .flags = _flags }
#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \
{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
#define RSND_SSI_UNUSED \
{ .dai_id = -1, .pio_irq = -1, .flags = 0 }
{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 }
struct rsnd_ssi_platform_info {
int dai_id;
int dma_id;
int pio_irq;
u32 flags;
};
......
......@@ -19,6 +19,7 @@
* SSICR
*/
#define FORCE (1 << 31) /* Fixed */
#define DMEN (1 << 28) /* DMA Enable */
#define UIEN (1 << 27) /* Underflow Interrupt Enable */
#define OIEN (1 << 26) /* Overflow Interrupt Enable */
#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */
......@@ -51,6 +52,11 @@
#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */
#define DIRQ (1 << 24) /* Data Interrupt Status Flag */
/*
* SSIWSR
*/
#define CONT (1 << 8) /* WS Continue Function */
struct rsnd_ssi {
struct clk *clk;
struct rsnd_ssi_platform_info *info; /* rcar_snd.h */
......@@ -63,6 +69,7 @@ struct rsnd_ssi {
u32 cr_clk;
u32 cr_etc;
int err;
int dma_offset;
unsigned int usrcnt;
unsigned int rate;
};
......@@ -83,7 +90,10 @@ struct rsnd_ssiu {
#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr)
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
#define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0)
#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
#define rsnd_ssi_dma_available(ssi) \
rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master)
#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
......@@ -477,6 +487,79 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.stop = rsnd_ssi_pio_stop,
};
static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len)
{
struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma);
struct rsnd_dai_stream *io = ssi->io;
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
*len = io->byte_per_period;
*buf = runtime->dma_addr +
rsnd_dai_pointer_offset(io, ssi->dma_offset + *len);
ssi->dma_offset = *len; /* it cares A/B plane */
return 0;
}
static int rsnd_ssi_dma_complete(struct rsnd_dma *dma)
{
struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma);
struct rsnd_dai_stream *io = ssi->io;
u32 status = rsnd_mod_read(&ssi->mod, SSISR);
rsnd_ssi_record_error(ssi, status);
rsnd_dai_pointer_update(ssi->io, io->byte_per_period);
return 0;
}
static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
struct rsnd_dai *rdai,
struct rsnd_dai_stream *io)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
/* enable DMA transfer */
ssi->cr_etc = DMEN;
ssi->dma_offset = 0;
rsnd_dma_start(dma);
rsnd_ssi_hw_start(ssi, ssi->rdai, io);
/* enable WS continue */
if (rsnd_rdai_is_clk_master(rdai))
rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
return 0;
}
static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
struct rsnd_dai *rdai,
struct rsnd_dai_stream *io)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
ssi->cr_etc = 0;
rsnd_ssi_hw_stop(ssi, rdai);
rsnd_dma_stop(dma);
return 0;
}
static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.name = "ssi (dma)",
.init = rsnd_ssi_init,
.quit = rsnd_ssi_quit,
.start = rsnd_ssi_dma_start,
.stop = rsnd_ssi_dma_stop,
};
/*
* Non SSI
*/
......@@ -573,10 +656,27 @@ int rsnd_ssi_probe(struct platform_device *pdev,
ops = &rsnd_ssi_non_ops;
/*
* SSI DMA case
*/
if (pinfo->dma_id > 0) {
ret = rsnd_dma_init(
priv, rsnd_mod_to_dma(&ssi->mod),
(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY),
pinfo->dma_id,
rsnd_ssi_dma_inquiry,
rsnd_ssi_dma_complete);
if (ret < 0)
dev_info(dev, "SSI DMA failed. try PIO transter\n");
else
ops = &rsnd_ssi_dma_ops;
}
/*
* SSI PIO case
*/
if (rsnd_ssi_is_pio(ssi)) {
if (!rsnd_ssi_dma_available(ssi) &&
rsnd_ssi_pio_available(ssi)) {
ret = devm_request_irq(dev, pinfo->pio_irq,
&rsnd_ssi_pio_interrupt,
IRQF_SHARED,
......@@ -605,6 +705,10 @@ void rsnd_ssi_remove(struct platform_device *pdev,
struct rsnd_ssi *ssi;
int i;
for_each_rsnd_ssi(ssi, priv, i)
for_each_rsnd_ssi(ssi, priv, i) {
clk_put(ssi->clk);
if (rsnd_ssi_dma_available(ssi))
rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod));
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册