ASoC: rsnd: add SRC (Sampling Rate Converter) support

This patch adds SRC support to Renesas sound driver.
SRC converts sampling rate between codec <-> cpu.
It needs special codec chip,
or very simple DA/AD converter to use it.
This patch was tested via ak4554 codec,
and supports Gen1 only at this point.

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/adg.c b/sound/soc/sh/rcar/adg.c
index 2e71a7b..a53235c 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -30,6 +30,79 @@
 	     i++, (pos) = adg->clk[i])
 #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
 
+static int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
+					 struct rsnd_mod *mod,
+					 unsigned int src_rate,
+					 unsigned int dst_rate)
+{
+	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int idx, sel, div, shift;
+	u32 mask, val;
+	int id = rsnd_mod_id(mod);
+	unsigned int sel_rate [] = {
+		clk_get_rate(adg->clk[CLKA]),	/* 000: CLKA */
+		clk_get_rate(adg->clk[CLKB]),	/* 001: CLKB */
+		clk_get_rate(adg->clk[CLKC]),	/* 010: CLKC */
+		0,				/* 011: MLBCLK (not used) */
+		adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */
+		adg->rbgb_rate_for_48khz_div_6,	/* 101: RBGB */
+	};
+
+	/* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */
+	for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
+		for (div  = 128,	idx = 0;
+		     div <= 2048;
+		     div *= 2,		idx++) {
+			if (src_rate == sel_rate[sel] / div) {
+				val = (idx << 4) | sel;
+				goto find_rate;
+			}
+		}
+	}
+	dev_err(dev, "can't find convert src clk\n");
+	return -EINVAL;
+
+find_rate:
+	shift	= (id % 4) * 8;
+	mask	= 0xFF << shift;
+	val	= val << shift;
+
+	dev_dbg(dev, "adg convert src clk = %02x\n", val);
+
+	switch (id / 4) {
+	case 0:
+		rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val);
+		break;
+	case 1:
+		rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val);
+		break;
+	case 2:
+		rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val);
+		break;
+	}
+
+	/*
+	 * Gen1 doesn't need dst_rate settings,
+	 * since it uses SSI WS pin.
+	 * see also rsnd_src_set_route_if_gen1()
+	 */
+
+	return 0;
+}
+
+int rsnd_adg_set_convert_clk(struct rsnd_priv *priv,
+			     struct rsnd_mod *mod,
+			     unsigned int src_rate,
+			     unsigned int dst_rate)
+{
+	if (rsnd_is_gen1(priv))
+		return rsnd_adg_set_convert_clk_gen1(priv, mod,
+						     src_rate, dst_rate);
+
+	return -EINVAL;
+}
+
 static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
 {
 	int id = rsnd_mod_id(mod);