[alsa-devel] [PATCH 3/5] ASoC: rsnd: SSI supports DMA transfer

Kuninori Morimoto kuninori.morimoto.gx at renesas.com
Mon Jul 29 03:59:02 CEST 2013


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 at renesas.com>
---
 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 33233ed..a72687d 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 c48a6c7..2079ccf 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.7.9.5



More information about the Alsa-devel mailing list