summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2013-07-28 18:59:02 -0700
committerMark Brown <broonie@linaro.org>2013-08-06 17:56:13 +0100
commit849fc82a6f4f32b4c8c502bb7c4a68df51170232 (patch)
tree59745fe7d701eec3504c166a949fdad5cad467cb
parent0a4d94c07ce782e645a8c0484d52221758b4c398 (diff)
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>
-rw-r--r--include/sound/rcar_snd.h7
-rw-r--r--sound/soc/sh/rcar/ssi.c110
2 files changed, 111 insertions, 6 deletions
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h
index 33233edd166..a72687dda0c 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 c48a6c7cd08..2079ccf5f32 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));
+ }
+
}