ASoC: rsnd: add Gen2 SRC and DMAEngine support

Renesas sound Gen2 has SRC (= Sampling Rate Converter)
which needs 2 DMAC.
The data path image when you use SRC on Gen2 is

[mem] -> Audio-DMAC -> SRC -> Audio-DMAC-peri-peri -> SSIU -> SSI

This patch support SRC and DMAEnine.
It is tested on R-Car H2 Lager board

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c
index 29d8990..6e5c763 100644
--- a/sound/soc/sh/rcar/scu.c
+++ b/sound/soc/sh/rcar/scu.c
@@ -33,6 +33,8 @@
 	container_of((_mod), struct rsnd_scu, mod)
 #define rsnd_scu_hpbif_is_enable(scu)	\
 	(rsnd_scu_mode_flags(scu) & RSND_SCU_USE_HPBIF)
+#define rsnd_scu_dma_available(scu) \
+	rsnd_dma_available(rsnd_mod_to_dma(&(scu)->mod))
 
 #define for_each_rsnd_scu(pos, priv, i)				\
 	for ((i) = 0;						\
@@ -472,6 +474,103 @@
 /*
  *		Gen2 functions
  */
+static int rsnd_scu_set_convert_rate_gen2(struct rsnd_mod *mod,
+					  struct rsnd_dai *rdai,
+					  struct rsnd_dai_stream *io)
+{
+	int ret;
+
+	ret = rsnd_scu_set_convert_rate(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_mod_read(mod, SRC_ADINR));
+	rsnd_mod_write(mod, SSI_BUSIF_MODE,  rsnd_mod_read(mod, SRC_BUSIF_MODE));
+
+	rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
+
+	rsnd_mod_write(mod, SRC_BSDSR, 0x01800000);
+	rsnd_mod_write(mod, SRC_BSISR, 0x00100060);
+
+	return 0;
+}
+
+static int rsnd_scu_set_convert_timing_gen2(struct rsnd_mod *mod,
+					    struct rsnd_dai *rdai,
+					    struct rsnd_dai_stream *io)
+{
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
+	u32 convert_rate = rsnd_scu_convert_rate(scu);
+	int ret;
+
+	if (convert_rate)
+		ret = rsnd_adg_set_convert_clk_gen2(mod, rdai, io,
+						    runtime->rate,
+						    convert_rate);
+	else
+		ret = rsnd_adg_set_convert_timing_gen2(mod, rdai, io);
+
+	return ret;
+}
+
+static int rsnd_scu_init_gen2(struct rsnd_mod *mod,
+			      struct rsnd_dai *rdai,
+			      struct rsnd_dai_stream *io)
+{
+	int ret;
+
+	ret = rsnd_scu_init(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_scu_set_convert_rate_gen2(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_scu_set_convert_timing_gen2(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int rsnd_scu_start_gen2(struct rsnd_mod *mod,
+			       struct rsnd_dai *rdai,
+			       struct rsnd_dai_stream *io)
+{
+	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
+
+	rsnd_dma_start(rsnd_mod_to_dma(&scu->mod));
+
+	rsnd_mod_write(mod, SSI_CTRL, 0x1);
+	rsnd_mod_write(mod, SRC_CTRL, 0x11);
+
+	return rsnd_scu_start(mod, rdai, io);
+}
+
+static int rsnd_scu_stop_gen2(struct rsnd_mod *mod,
+			      struct rsnd_dai *rdai,
+			      struct rsnd_dai_stream *io)
+{
+	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
+
+	rsnd_mod_write(mod, SSI_CTRL, 0);
+	rsnd_mod_write(mod, SRC_CTRL, 0);
+
+	rsnd_dma_stop(rsnd_mod_to_dma(&scu->mod));
+
+	return rsnd_scu_stop(mod, rdai, io);
+}
+
+static struct rsnd_mod_ops rsnd_scu_gen2_ops = {
+	.name	= "scu (gen2)",
+	.init	= rsnd_scu_init_gen2,
+	.quit	= rsnd_scu_quit,
+	.start	= rsnd_scu_start_gen2,
+	.stop	= rsnd_scu_stop_gen2,
+};
+
 static int rsnd_scu_start_non_gen2(struct rsnd_mod *mod,
 				   struct rsnd_dai *rdai,
 				   struct rsnd_dai_stream *io)
@@ -534,6 +633,17 @@
 		if (rsnd_scu_hpbif_is_enable(scu)) {
 			if (rsnd_is_gen1(priv))
 				ops = &rsnd_scu_gen1_ops;
+			if (rsnd_is_gen2(priv)) {
+				struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, i);
+				int ret = rsnd_dma_init(priv,
+						rsnd_mod_to_dma(&scu->mod),
+						rsnd_ssi_is_play(ssi),
+						scu->info->dma_id);
+				if (ret < 0)
+					return ret;
+
+				ops = &rsnd_scu_gen2_ops;
+			}
 		} else {
 			if (rsnd_is_gen1(priv))
 				ops = &rsnd_scu_non_gen1_ops;
@@ -553,4 +663,11 @@
 void rsnd_scu_remove(struct platform_device *pdev,
 		     struct rsnd_priv *priv)
 {
+	struct rsnd_scu *scu;
+	int i;
+
+	for_each_rsnd_scu(scu, priv, i) {
+		if (rsnd_scu_dma_available(scu))
+			rsnd_dma_quit(priv, rsnd_mod_to_dma(&scu->mod));
+	}
 }