Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef749400 authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Mark Brown
Browse files

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: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarMark Brown <broonie@linaro.org>
parent adcf7d5e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ struct rsnd_ssi_platform_info {

struct rsnd_scu_platform_info {
	u32 flags;
	u32 convert_rate; /* sampling rate convert */
};

/*
+73 −0
Original line number Diff line number Diff line
@@ -30,6 +30,79 @@ struct rsnd_adg {
	     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);
+10 −0
Original line number Diff line number Diff line
@@ -318,13 +318,23 @@ static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
		RSND_GEN1_S_REG(gen, SRU,	SSI_MODE0,	0xD0),
		RSND_GEN1_S_REG(gen, SRU,	SSI_MODE1,	0xD4),
		RSND_GEN1_M_REG(gen, SRU,	BUSIF_MODE,	0x20,	0x4),
		RSND_GEN1_M_REG(gen, SRU,	SRC_ROUTE_MODE0,0x50,	0x8),
		RSND_GEN1_M_REG(gen, SRU,	SRC_SWRSR,	0x200,	0x40),
		RSND_GEN1_M_REG(gen, SRU,	SRC_SRCIR,	0x204,	0x40),
		RSND_GEN1_M_REG(gen, SRU,	SRC_ADINR,	0x214,	0x40),
		RSND_GEN1_M_REG(gen, SRU,	SRC_IFSCR,	0x21c,	0x40),
		RSND_GEN1_M_REG(gen, SRU,	SRC_IFSVR,	0x220,	0x40),
		RSND_GEN1_M_REG(gen, SRU,	SRC_SRCCR,	0x224,	0x40),
		RSND_GEN1_M_REG(gen, SRU,	SRC_MNFSR,	0x228,	0x40),

		RSND_GEN1_S_REG(gen, ADG,	BRRA,		0x00),
		RSND_GEN1_S_REG(gen, ADG,	BRRB,		0x04),
		RSND_GEN1_S_REG(gen, ADG,	SSICKR,		0x08),
		RSND_GEN1_S_REG(gen, ADG,	AUDIO_CLK_SEL0,	0x0c),
		RSND_GEN1_S_REG(gen, ADG,	AUDIO_CLK_SEL1,	0x10),
		RSND_GEN1_S_REG(gen, ADG,	AUDIO_CLK_SEL3,	0x18),
		RSND_GEN1_S_REG(gen, ADG,	AUDIO_CLK_SEL4,	0x1c),
		RSND_GEN1_S_REG(gen, ADG,	AUDIO_CLK_SEL5,	0x20),

		RSND_GEN1_M_REG(gen, SSI,	SSICR,		0x00,	0x40),
		RSND_GEN1_M_REG(gen, SSI,	SSISR,		0x04,	0x40),
+18 −0
Original line number Diff line number Diff line
@@ -41,7 +41,14 @@ enum rsnd_reg {
	RSND_REG_SSI_MODE1,
	RSND_REG_BUSIF_MODE,
	RSND_REG_INT_ENABLE,		/* for Gen2 */
	RSND_REG_SRC_ROUTE_MODE0,
	RSND_REG_SRC_SWRSR,
	RSND_REG_SRC_SRCIR,
	RSND_REG_SRC_ADINR,
	RSND_REG_SRC_IFSCR,
	RSND_REG_SRC_IFSVR,
	RSND_REG_SRC_SRCCR,
	RSND_REG_SRC_MNFSR,

	/* ADG */
	RSND_REG_BRRA,
@@ -50,6 +57,9 @@ enum rsnd_reg {
	RSND_REG_AUDIO_CLK_SEL0,
	RSND_REG_AUDIO_CLK_SEL1,
	RSND_REG_AUDIO_CLK_SEL2,
	RSND_REG_AUDIO_CLK_SEL3,	/* for Gen1 */
	RSND_REG_AUDIO_CLK_SEL4,	/* for Gen1 */
	RSND_REG_AUDIO_CLK_SEL5,	/* for Gen1 */

	/* SSI */
	RSND_REG_SSICR,
@@ -227,6 +237,10 @@ int rsnd_adg_probe(struct platform_device *pdev,
		   struct rsnd_priv *priv);
void rsnd_adg_remove(struct platform_device *pdev,
		   struct rsnd_priv *priv);
int rsnd_adg_set_convert_clk(struct rsnd_priv *priv,
			     struct rsnd_mod *mod,
			     unsigned int src_rate,
			     unsigned int dst_rate);

/*
 *	R-Car sound priv
@@ -280,6 +294,10 @@ void rsnd_scu_remove(struct platform_device *pdev,
		     struct rsnd_priv *priv);
struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id);
bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod);
unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv,
				   struct rsnd_mod *ssi_mod,
				   struct snd_pcm_runtime *runtime);

#define rsnd_scu_nr(priv) ((priv)->scu_nr)

/*
+145 −7
Original line number Diff line number Diff line
@@ -13,9 +13,13 @@
struct rsnd_scu {
	struct rsnd_scu_platform_info *info; /* rcar_snd.h */
	struct rsnd_mod mod;
	struct clk *clk;
};

#define rsnd_scu_mode_flags(p) ((p)->info->flags)
#define rsnd_scu_convert_rate(p) ((p)->info->convert_rate)

#define RSND_SCU_NAME_SIZE 16

/*
 * ADINR
@@ -26,6 +30,15 @@ struct rsnd_scu {
#define OTBL_18		(6 << 16)
#define OTBL_16		(8 << 16)

/*
 *		image of SRC (Sampling Rate Converter)
 *
 * 96kHz   <-> +-----+	48kHz	+-----+	 48kHz	+-------+
 * 48kHz   <-> | SRC | <------>	| SSI |	<----->	| codec |
 * 44.1kHz <-> +-----+		+-----+		+-------+
 * ...
 *
 */

#define rsnd_mod_to_scu(_mod)	\
	container_of((_mod), struct rsnd_scu, mod)
@@ -56,7 +69,7 @@ static int rsnd_src_set_route_if_gen1(struct rsnd_priv *priv,
		{ 0x3, 28, }, /* 7 */
		{ 0x3, 30, }, /* 8 */
	};

	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
	u32 mask;
	u32 val;
	int shift;
@@ -86,9 +99,18 @@ static int rsnd_src_set_route_if_gen1(struct rsnd_priv *priv,
	 */
	shift	= (id % 4) * 8;
	mask	= 0x1F << shift;
	if (8 == id) /* SRU8 is very special */

	/*
	 * ADG is used as source clock if SRC was used,
	 * then, SSI WS is used as destination clock.
	 * SSI WS is used as source clock if SRC is not used
	 * (when playback, source/destination become reverse when capture)
	 */
	if (rsnd_scu_convert_rate(scu))	/* use ADG */
		val = 0;
	else if (8 == id)		/* use SSI WS, but SRU8 is special */
		val = id << shift;
	else
	else				/* use SSI WS */
		val = (id + 1) << shift;

	switch (id / 4) {
@@ -106,14 +128,45 @@ static int rsnd_src_set_route_if_gen1(struct rsnd_priv *priv,
	return 0;
}

static int rsnd_scu_rate_ctrl(struct rsnd_priv *priv,
unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv,
				   struct rsnd_mod *ssi_mod,
				   struct snd_pcm_runtime *runtime)
{
	struct rsnd_scu *scu;
	unsigned int rate;

	/* this function is assuming SSI id = SCU id here */
	scu = rsnd_mod_to_scu(rsnd_scu_mod_get(priv, rsnd_mod_id(ssi_mod)));

	/*
	 * return convert rate if SRC is used,
	 * otherwise, return runtime->rate as usual
	 */
	rate = rsnd_scu_convert_rate(scu);
	if (!rate)
		rate = runtime->rate;

	return rate;
}

static int rsnd_scu_convert_rate_ctrl(struct rsnd_priv *priv,
			      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);
	u32 adinr = runtime->channels;

	/* set/clear soft reset */
	rsnd_mod_write(mod, SRC_SWRSR, 0);
	rsnd_mod_write(mod, SRC_SWRSR, 1);

	/* Initialize the operation of the SRC internal circuits */
	rsnd_mod_write(mod, SRC_SRCIR, 1);

	/* Set channel number and output bit length */
	switch (runtime->sample_bits) {
	case 16:
		adinr |= OTBL_16;
@@ -124,9 +177,42 @@ static int rsnd_scu_rate_ctrl(struct rsnd_priv *priv,
	default:
		return -EIO;
	}

	rsnd_mod_write(mod, SRC_ADINR, adinr);

	if (convert_rate) {
		u32 fsrate = 0x0400000 / convert_rate * runtime->rate;
		int ret;

		/* Enable the initial value of IFS */
		rsnd_mod_write(mod, SRC_IFSCR, 1);

		/* Set initial value of IFS */
		rsnd_mod_write(mod, SRC_IFSVR, fsrate);

		/* Select SRC mode (fixed value) */
		rsnd_mod_write(mod, SRC_SRCCR, 0x00010110);

		/* Set the restriction value of the FS ratio (98%) */
		rsnd_mod_write(mod, SRC_MNFSR, fsrate / 100 * 98);

		if (rsnd_is_gen1(priv)) {
			/* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
		}

		/* set convert clock */
		ret = rsnd_adg_set_convert_clk(priv, mod,
					       runtime->rate,
					       convert_rate);
		if (ret < 0)
			return ret;
	}

	/* Cancel the initialization and operate the SRC function */
	rsnd_mod_write(mod, SRC_SRCIR, 0);

	/* use DMA transfer */
	rsnd_mod_write(mod, BUSIF_MODE, 1);

	return 0;
}

@@ -135,6 +221,7 @@ static int rsnd_scu_transfer_start(struct rsnd_priv *priv,
				   struct rsnd_dai *rdai,
				   struct rsnd_dai_stream *io)
{
	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
	int id = rsnd_mod_id(mod);
	u32 val;

@@ -143,7 +230,28 @@ static int rsnd_scu_transfer_start(struct rsnd_priv *priv,
		rsnd_mod_bset(mod, SRC_ROUTE_CTRL, val, val);
	}

	rsnd_mod_write(mod, BUSIF_MODE, 1);
	if (rsnd_scu_convert_rate(scu))
		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);

	return 0;
}

static int rsnd_scu_transfer_stop(struct rsnd_priv *priv,
				  struct rsnd_mod *mod,
				  struct rsnd_dai *rdai,
				  struct rsnd_dai_stream *io)
{
	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
	int id = rsnd_mod_id(mod);
	u32 mask;

	if (rsnd_is_gen1(priv)) {
		mask = (1 << id);
		rsnd_mod_bset(mod, SRC_ROUTE_CTRL, mask, 0);
	}

	if (rsnd_scu_convert_rate(scu))
		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0);

	return 0;
}
@@ -161,6 +269,7 @@ static int rsnd_scu_start(struct rsnd_mod *mod,
			  struct rsnd_dai_stream *io)
{
	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
	struct device *dev = rsnd_priv_to_dev(priv);
	int ret;

@@ -175,13 +284,15 @@ static int rsnd_scu_start(struct rsnd_mod *mod,
		return 0;
	}

	clk_enable(scu->clk);

	/* it use DMA transter */

	ret = rsnd_src_set_route_if_gen1(priv, mod, rdai, io);
	if (ret < 0)
		return ret;

	ret = rsnd_scu_rate_ctrl(priv, mod, rdai, io);
	ret = rsnd_scu_convert_rate_ctrl(priv, mod, rdai, io);
	if (ret < 0)
		return ret;

@@ -194,9 +305,27 @@ static int rsnd_scu_start(struct rsnd_mod *mod,
	return 0;
}

static int rsnd_scu_stop(struct rsnd_mod *mod,
			  struct rsnd_dai *rdai,
			  struct rsnd_dai_stream *io)
{
	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);

	if (!rsnd_scu_hpbif_is_enable(mod))
		return 0;

	rsnd_scu_transfer_stop(priv, mod, rdai, io);

	clk_disable(scu->clk);

	return 0;
}

static struct rsnd_mod_ops rsnd_scu_ops = {
	.name	= "scu",
	.start	= rsnd_scu_start,
	.stop	= rsnd_scu_stop,
};

struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id)
@@ -212,6 +341,8 @@ int rsnd_scu_probe(struct platform_device *pdev,
{
	struct device *dev = rsnd_priv_to_dev(priv);
	struct rsnd_scu *scu;
	struct clk *clk;
	char name[RSND_SCU_NAME_SIZE];
	int i, nr;

	/*
@@ -228,9 +359,16 @@ int rsnd_scu_probe(struct platform_device *pdev,
	priv->scu	= scu;

	for_each_rsnd_scu(scu, priv, i) {
		snprintf(name, RSND_SCU_NAME_SIZE, "scu.%d", i);

		clk = devm_clk_get(dev, name);
		if (IS_ERR(clk))
			return PTR_ERR(clk);

		rsnd_mod_init(priv, &scu->mod,
			      &rsnd_scu_ops, i);
		scu->info = &info->scu_info[i];
		scu->clk = clk;

		dev_dbg(dev, "SCU%d probed\n", i);
	}
Loading