blob: d3a98762a7fe75f701f7dfa81be878eb2038bedb [file] [log] [blame]
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