ASoC: rsnd: use mod base common method on SSI-parent

Renesas sound needs many devices
(SSI/SSIU/SRC/CTU/MIX/DVC/CMD/AudioDMAC/AudioDMACpp).
SSI/SRC/CTU/MIX/DVC are implemented as module.
SSI parent, SSIU are implemented as part of SSI
CMD is implemented as part of CTU/MIX/DVC
AudioDMAC/AudioDMACpp are implemented as part of SSI/SRC
It is nice sense that these all devices are implemented as mod.

This patch makes SSI parent mod base common method

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index bb08d66..3e81471 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -67,7 +67,9 @@
 
 	u32 cr_own;
 	u32 cr_clk;
+	u32 cr_mode;
 	int chan;
+	int rate;
 	int err;
 	unsigned int usrcnt;
 };
@@ -82,9 +84,9 @@
 #define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
-#define rsnd_ssi_parent(ssi) ((ssi)->parent)
 #define rsnd_ssi_mode_flags(p) ((p)->info->flags)
 #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
+#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
 #define rsnd_ssi_of_node(priv) \
 	of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
 
@@ -168,7 +170,9 @@
 	struct rsnd_priv *priv = rsnd_io_to_priv(io);
 	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
 	struct rsnd_mod *mod = rsnd_mod_get(ssi);
+	struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
 	int j, ret;
 	int ssi_clk_mul_table[] = {
 		1, 2, 4, 8, 16, 6, 12,
@@ -176,6 +180,21 @@
 	unsigned int main_rate;
 	unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
 
+	if (!rsnd_rdai_is_clk_master(rdai))
+		return 0;
+
+	if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
+		return 0;
+
+	if (ssi->usrcnt > 1) {
+		if (ssi->rate != rate) {
+			dev_err(dev, "SSI parent/child should use same rate\n");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
 	/*
 	 * Find best clock, and try to start ADG
 	 */
@@ -193,6 +212,10 @@
 			ssi->cr_clk	= FORCE | SWL_32 |
 				SCKD | SWSD | CKDV(j);
 
+			ssi->rate = rate;
+
+			rsnd_mod_write(mod, SSIWSR, CONT);
+
 			dev_dbg(dev, "%s[%d] outputs %u Hz\n",
 				rsnd_mod_name(mod),
 				rsnd_mod_id(mod), rate);
@@ -205,113 +228,26 @@
 	return -EIO;
 }
 
-static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
+static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
+				     struct rsnd_dai_stream *io)
 {
-	struct rsnd_mod *mod = rsnd_mod_get(ssi);
-
-	ssi->cr_clk = 0;
-	rsnd_adg_ssi_clk_stop(mod);
-}
-
-static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
-			      struct rsnd_dai_stream *io)
-{
-	struct rsnd_priv *priv = rsnd_io_to_priv(io);
 	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-	struct device *dev = rsnd_priv_to_dev(priv);
 	struct rsnd_mod *mod = rsnd_mod_get(ssi);
-	u32 cr_mode;
-	u32 cr;
+	struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
 
-	if (0 == ssi->usrcnt) {
-		rsnd_mod_power_on(mod);
-
-		if (rsnd_rdai_is_clk_master(rdai)) {
-			struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
-
-			if (ssi_parent)
-				rsnd_ssi_hw_start(ssi_parent, io);
-			else
-				rsnd_ssi_master_clk_start(ssi, io);
-		}
-	}
-
-	if (rsnd_ssi_is_dma_mode(mod)) {
-		cr_mode = UIEN | OIEN |	/* over/under run */
-			  DMEN;		/* DMA : enable DMA */
-	} else {
-		cr_mode = DIEN;		/* PIO : enable Data interrupt */
-	}
-
-	cr  =	ssi->cr_own	|
-		ssi->cr_clk	|
-		cr_mode		|
-		EN;
-
-	rsnd_mod_write(mod, SSICR, cr);
-
-	/* enable WS continue */
-	if (rsnd_rdai_is_clk_master(rdai))
-		rsnd_mod_write(mod, SSIWSR, CONT);
-
-	/* clear error status */
-	rsnd_ssi_status_clear(mod);
-
-	ssi->usrcnt++;
-
-	dev_dbg(dev, "%s[%d] hw started\n",
-		rsnd_mod_name(mod), rsnd_mod_id(mod));
-}
-
-static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
-{
-	struct rsnd_mod *mod = rsnd_mod_get(ssi);
-	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-	struct device *dev = rsnd_priv_to_dev(priv);
-	u32 cr;
-
-	if (0 == ssi->usrcnt) {
-		dev_err(dev, "%s called without starting\n", __func__);
+	if (!rsnd_rdai_is_clk_master(rdai))
 		return;
-	}
 
-	ssi->usrcnt--;
+	if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
+		return;
 
-	if (0 == ssi->usrcnt) {
-		/*
-		 * disable all IRQ,
-		 * and, wait all data was sent
-		 */
-		cr  =	ssi->cr_own	|
-			ssi->cr_clk;
+	if (ssi->usrcnt > 1)
+		return;
 
-		rsnd_mod_write(mod, SSICR, cr | EN);
-		rsnd_ssi_status_check(mod, DIRQ);
+	ssi->cr_clk	= 0;
+	ssi->rate	= 0;
 
-		/*
-		 * disable SSI,
-		 * and, wait idle state
-		 */
-		rsnd_mod_write(mod, SSICR, cr);	/* disabled all */
-		rsnd_ssi_status_check(mod, IIRQ);
-
-		if (rsnd_rdai_is_clk_master(rdai)) {
-			struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
-
-			if (ssi_parent)
-				rsnd_ssi_hw_stop(io, ssi_parent);
-			else
-				rsnd_ssi_master_clk_stop(ssi);
-		}
-
-		rsnd_mod_power_off(mod);
-
-		ssi->chan = 0;
-	}
-
-	dev_dbg(dev, "%s[%d] hw stopped\n",
-		rsnd_mod_name(mod), rsnd_mod_id(mod));
+	rsnd_adg_ssi_clk_stop(mod);
 }
 
 /*
@@ -325,6 +261,18 @@
 	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
 	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 	u32 cr;
+	int ret;
+
+	ssi->usrcnt++;
+
+	rsnd_mod_power_on(mod);
+
+	ret = rsnd_ssi_master_clk_start(ssi, io);
+	if (ret < 0)
+		return ret;
+
+	if (rsnd_ssi_is_parent(mod, io))
+		return 0;
 
 	cr = FORCE;
 
@@ -359,12 +307,24 @@
 	if (rsnd_io_is_play(io))
 		cr |= TRMD;
 
-	/*
-	 * set ssi parameter
-	 */
 	ssi->cr_own	= cr;
+
+	if (rsnd_ssi_is_dma_mode(mod)) {
+		cr =	UIEN | OIEN |	/* over/under run */
+			DMEN;		/* DMA : enable DMA */
+	} else {
+		cr =	DIEN;		/* PIO : enable Data interrupt */
+	}
+
+	ssi->cr_mode	= cr;
+
 	ssi->err	= -1; /* ignore 1st error */
 
+	/* clear error status */
+	rsnd_ssi_status_clear(mod);
+
+	rsnd_ssi_irq_enable(mod);
+
 	return 0;
 }
 
@@ -375,6 +335,9 @@
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 	struct device *dev = rsnd_priv_to_dev(priv);
 
+	if (rsnd_ssi_is_parent(mod, io))
+		goto rsnd_ssi_quit_end;
+
 	if (ssi->err > 0)
 		dev_warn(dev, "%s[%d] under/over flow err = %d\n",
 			 rsnd_mod_name(mod), rsnd_mod_id(mod), ssi->err);
@@ -382,6 +345,19 @@
 	ssi->cr_own	= 0;
 	ssi->err	= 0;
 
+	rsnd_ssi_irq_disable(mod);
+
+rsnd_ssi_quit_end:
+	rsnd_ssi_master_clk_stop(ssi, io);
+
+	rsnd_mod_power_off(mod);
+
+	ssi->usrcnt--;
+
+	if (ssi->usrcnt < 0)
+		dev_err(dev, "%s[%d] usrcnt error\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod));
+
 	return 0;
 }
 
@@ -391,14 +367,13 @@
 			      struct snd_pcm_hw_params *params)
 {
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-	struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
 	int chan = params_channels(params);
 
 	/*
 	 * Already working.
 	 * It will happen if SSI has parent/child connection.
 	 */
-	if (ssi->usrcnt) {
+	if (ssi->usrcnt > 1) {
 		/*
 		 * it is error if child <-> parent SSI uses
 		 * different channels.
@@ -407,11 +382,7 @@
 			return -EIO;
 	}
 
-	/* It will be removed on rsnd_ssi_hw_stop */
 	ssi->chan = chan;
-	if (ssi_parent)
-		return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io,
-					  substream, params);
 
 	return 0;
 }
@@ -432,15 +403,59 @@
 	return status;
 }
 
+static int __rsnd_ssi_start(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io,
+			    struct rsnd_priv *priv)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	u32 cr;
+
+	cr  =	ssi->cr_own	|
+		ssi->cr_clk	|
+		ssi->cr_mode	|
+		EN;
+
+	rsnd_mod_write(mod, SSICR, cr);
+
+	return 0;
+}
+
 static int rsnd_ssi_start(struct rsnd_mod *mod,
 			  struct rsnd_dai_stream *io,
 			  struct rsnd_priv *priv)
 {
+	/*
+	 * no limit to start
+	 * see also
+	 *	rsnd_ssi_stop
+	 *	rsnd_ssi_interrupt
+	 */
+	return __rsnd_ssi_start(mod, io, priv);
+}
+
+static int __rsnd_ssi_stop(struct rsnd_mod *mod,
+			   struct rsnd_dai_stream *io,
+			   struct rsnd_priv *priv)
+{
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	u32 cr;
 
-	rsnd_ssi_hw_start(ssi, io);
+	/*
+	 * disable all IRQ,
+	 * and, wait all data was sent
+	 */
+	cr  =	ssi->cr_own	|
+		ssi->cr_clk;
 
-	rsnd_ssi_irq_enable(mod);
+	rsnd_mod_write(mod, SSICR, cr | EN);
+	rsnd_ssi_status_check(mod, DIRQ);
+
+	/*
+	 * disable SSI,
+	 * and, wait idle state
+	 */
+	rsnd_mod_write(mod, SSICR, cr);	/* disabled all */
+	rsnd_ssi_status_check(mod, IIRQ);
 
 	return 0;
 }
@@ -451,13 +466,16 @@
 {
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 
-	rsnd_ssi_irq_disable(mod);
+	/*
+	 * don't stop if not last user
+	 * see also
+	 *	rsnd_ssi_start
+	 *	rsnd_ssi_interrupt
+	 */
+	if (ssi->usrcnt > 1)
+		return 0;
 
-	rsnd_ssi_record_error(ssi);
-
-	rsnd_ssi_hw_stop(io, ssi);
-
-	return 0;
+	return __rsnd_ssi_stop(mod, io, priv);
 }
 
 static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
@@ -505,8 +523,8 @@
 		dev_dbg(dev, "%s[%d] restart\n",
 			rsnd_mod_name(mod), rsnd_mod_id(mod));
 
-		rsnd_ssi_stop(mod, io, priv);
-		rsnd_ssi_start(mod, io, priv);
+		__rsnd_ssi_stop(mod, io, priv);
+		__rsnd_ssi_start(mod, io, priv);
 	}
 
 	if (ssi->err > 1024) {
@@ -535,6 +553,27 @@
 /*
  *		SSI PIO
  */
+static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
+				   struct rsnd_dai_stream *io,
+				   struct rsnd_priv *priv)
+{
+	if (!__rsnd_ssi_is_pin_sharing(mod))
+		return;
+
+	switch (rsnd_mod_id(mod)) {
+	case 1:
+	case 2:
+		rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP);
+		break;
+	case 4:
+		rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP);
+		break;
+	case 8:
+		rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP);
+		break;
+	}
+}
+
 static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
 				 struct rsnd_dai_stream *io,
 				 struct rsnd_priv *priv)
@@ -543,6 +582,8 @@
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 	int ret;
 
+	rsnd_ssi_parent_attach(mod, io, priv);
+
 	ret = rsnd_ssiu_attach(io, mod);
 	if (ret < 0)
 		return ret;
@@ -679,28 +720,6 @@
 	return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
 }
 
-static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
-{
-	struct rsnd_mod *mod = rsnd_mod_get(ssi);
-
-	if (!__rsnd_ssi_is_pin_sharing(mod))
-		return;
-
-	switch (rsnd_mod_id(mod)) {
-	case 1:
-	case 2:
-		ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0));
-		break;
-	case 4:
-		ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 3));
-		break;
-	case 8:
-		ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 7));
-		break;
-	}
-}
-
-
 static void rsnd_of_parse_ssi(struct platform_device *pdev,
 			      const struct rsnd_of_data *of_data,
 			      struct rsnd_priv *priv)
@@ -810,8 +829,6 @@
 				    RSND_MOD_SSI, i);
 		if (ret)
 			return ret;
-
-		rsnd_ssi_parent_setup(priv, ssi);
 	}
 
 	return 0;