| From 72a3557f23b87b9bd6bcc3d03199c65ab0764ae3 Mon Sep 17 00:00:00 2001 |
| From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| Date: Sun, 28 Jul 2013 18:59:02 -0700 |
| Subject: 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: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| Signed-off-by: Mark Brown <broonie@linaro.org> |
| (cherry picked from commit 849fc82a6f4f32b4c8c502bb7c4a68df51170232) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| include/sound/rcar_snd.h | 7 +-- |
| sound/soc/sh/rcar/ssi.c | 110 +++++++++++++++++++++++++++++++++++++++++++++-- |
| 2 files changed, 111 insertions(+), 6 deletions(-) |
| |
| diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h |
| index 33233edd1664..a72687dda0cd 100644 |
| --- a/include/sound/rcar_snd.h |
| +++ b/include/sound/rcar_snd.h |
| @@ -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; |
| }; |
| diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c |
| index c48a6c7cd08e..2079ccf5f322 100644 |
| --- a/sound/soc/sh/rcar/ssi.c |
| +++ b/sound/soc/sh/rcar/ssi.c |
| @@ -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 |
| */ |
| @@ -574,9 +657,26 @@ 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)); |
| + } |
| + |
| } |
| -- |
| 1.8.5.rc3 |
| |