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

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

ASoC: rsnd: add Multi channel support



This patch adds Multi channel support on Renesas R-Car sound.
This patch is tested on Salvator-X board, but it can't use
Multi channel, because supported format is different between
codec chip and R-Car.
Thus, it was tested on board which doesn't mount codec chip,
with oscilloscope.

Signed-off-by: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Acked-by: default avatarRob Herring <robh@kernel.org>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 44bf5361
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -308,3 +308,21 @@ Example: simple sound card for TDM
			sound-dai = <&xxx>;
		};
	};

Example: simple sound card for Multi channel

&rcar_sound {
	pinctrl-0 = <&sound_pins &sound_clk_pins>;
	pinctrl-names = "default";

	/* Single DAI */
	#sound-dai-cells = <0>;

	status = "okay";

	rcar_sound,dai {
		dai0 {
			playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>;
		};
	};
};
+5 −1
Original line number Diff line number Diff line
@@ -215,7 +215,11 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io)
int rsnd_get_slot_width(struct rsnd_dai_stream *io)
{
	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
	int chan = runtime->channels / rsnd_get_slot_num(io);
	int chan = runtime->channels;

	/* Multi channel Mode */
	if (rsnd_ssi_multi_slaves(io))
		chan /= rsnd_get_slot_num(io);

	/* TDM Extend Mode needs 8ch */
	if (chan == 6)
+3 −0
Original line number Diff line number Diff line
@@ -226,6 +226,9 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
	const static struct rsnd_regmap_field_conf conf_ssiu[] = {
		RSND_GEN_S_REG(SSI_MODE0,	0x800),
		RSND_GEN_S_REG(SSI_MODE1,	0x804),
		RSND_GEN_S_REG(SSI_MODE2,	0x808),
		RSND_GEN_S_REG(SSI_CONTROL,	0x810),

		/* FIXME: it needs SSI_MODE2/3 in the future */
		RSND_GEN_M_REG(SSI_BUSIF_MODE,	0x0,	0x80),
		RSND_GEN_M_REG(SSI_BUSIF_ADINR,	0x4,	0x80),
+10 −5
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@ enum rsnd_reg {
	RSND_REG_SSI_MODE,		/* Gen2 only */
	RSND_REG_SSI_MODE0,
	RSND_REG_SSI_MODE1,
	RSND_REG_SSI_MODE2,
	RSND_REG_SSI_CONTROL,
	RSND_REG_SSI_CTRL,		/* Gen2 only */
	RSND_REG_SSI_BUSIF_MODE,	/* Gen2 only */
	RSND_REG_SSI_BUSIF_ADINR,	/* Gen2 only */
@@ -181,6 +183,9 @@ enum rsnd_mod_type {
	RSND_MOD_CTU,
	RSND_MOD_CMD,
	RSND_MOD_SRC,
	RSND_MOD_SSIM3,		/* SSI multi 3 */
	RSND_MOD_SSIM2,		/* SSI multi 2 */
	RSND_MOD_SSIM1,		/* SSI multi 1 */
	RSND_MOD_SSIP,		/* SSI parent */
	RSND_MOD_SSI,
	RSND_MOD_SSIU,
@@ -542,6 +547,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);

#define rsnd_ssi_is_pin_sharing(io)	\
	__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
@@ -549,10 +555,9 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);

#define rsnd_ssi_of_node(priv)						\
	of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
#define rsnd_parse_connect_ssi(rdai, playback, capture)			\
	rsnd_parse_connect_common(rdai, rsnd_ssi_mod_get,		\
				  rsnd_ssi_of_node(rsnd_rdai_to_priv(rdai)), \
				  playback, capture)
void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
			    struct device_node *playback,
			    struct device_node *capture);

/*
 *	R-Car SSIU
+112 −2
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ struct rsnd_ssi {
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
#define rsnd_ssi_mode_flags(p) ((p)->flags)
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io))

int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{
@@ -171,6 +172,41 @@ static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod)
	return 0;
}

u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
{
	struct rsnd_mod *mod;
	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
	struct rsnd_priv *priv = rsnd_io_to_priv(io);
	struct device *dev = rsnd_priv_to_dev(priv);
	enum rsnd_mod_type types[] = {
		RSND_MOD_SSIM1,
		RSND_MOD_SSIM2,
		RSND_MOD_SSIM3,
	};
	int i, mask;

	switch (runtime->channels) {
	case 2: /* Multi channel is not needed for Stereo */
		return 0;
	case 6:
		break;
	default:
		dev_err(dev, "unsupported channel\n");
		return 0;
	}

	mask = 0;
	for (i = 0; i < ARRAY_SIZE(types); i++) {
		mod = rsnd_io_to_mod(io, types[i]);
		if (!mod)
			continue;

		mask |= 1 << rsnd_mod_id(mod);
	}

	return mask;
}

static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
				     struct rsnd_dai_stream *io)
{
@@ -194,6 +230,9 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
	if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
		return 0;

	if (rsnd_ssi_is_multi_slave(mod, io))
		return 0;

	if (ssi->usrcnt > 1) {
		if (ssi->rate != rate) {
			dev_err(dev, "SSI parent/child should use same rate\n");
@@ -437,8 +476,14 @@ static int __rsnd_ssi_start(struct rsnd_mod *mod,

	cr  =	ssi->cr_own	|
		ssi->cr_clk	|
		ssi->cr_mode	|
		EN;
		ssi->cr_mode;

	/*
	 * EN will be set via SSIU :: SSI_CONTROL
	 * if Multi channel mode
	 */
	if (!rsnd_ssi_multi_slaves(io))
		cr |= EN;

	rsnd_mod_write(mod, SSICR, cr);
	rsnd_mod_write(mod, SSIWSR, ssi->wsr);
@@ -609,6 +654,13 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
	int ret;

	/*
	 * SSIP/SSIU/IRQ are not needed on
	 * SSI Multi slaves
	 */
	if (rsnd_ssi_is_multi_slave(mod, io))
		return 0;

	rsnd_ssi_parent_attach(mod, io, priv);

	ret = rsnd_ssiu_attach(io, mod);
@@ -641,6 +693,13 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
	int dma_id = 0; /* not needed */
	int ret;

	/*
	 * SSIP/SSIU/IRQ/DMA are not needed on
	 * SSI Multi slaves
	 */
	if (rsnd_ssi_is_multi_slave(mod, io))
		return 0;

	ret = rsnd_ssi_common_probe(mod, io, priv);
	if (ret)
		return ret;
@@ -732,6 +791,57 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = {
/*
 *		ssi mod function
 */
static void rsnd_ssi_connect(struct rsnd_mod *mod,
			     struct rsnd_dai_stream *io)
{
	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
	enum rsnd_mod_type types[] = {
		RSND_MOD_SSI,
		RSND_MOD_SSIM1,
		RSND_MOD_SSIM2,
		RSND_MOD_SSIM3,
	};
	enum rsnd_mod_type type;
	int i;

	/* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */
	for (i = 0; i < ARRAY_SIZE(types); i++) {
		type = types[i];
		if (!rsnd_io_to_mod(io, type)) {
			rsnd_dai_connect(mod, io, type);
			rsnd_set_slot(rdai, 2 * (i + 1), (i + 1));
			return;
		}
	}
}

void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
			    struct device_node *playback,
			    struct device_node *capture)
{
	struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
	struct device_node *node;
	struct device_node *np;
	struct rsnd_mod *mod;
	int i;

	node = rsnd_ssi_of_node(priv);
	if (!node)
		return;

	i = 0;
	for_each_child_of_node(node, np) {
		mod = rsnd_ssi_mod_get(priv, i);
		if (np == playback)
			rsnd_ssi_connect(mod, &rdai->playback);
		if (np == capture)
			rsnd_ssi_connect(mod, &rdai->capture);
		i++;
	}

	of_node_put(node);
}

struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
{
	if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
Loading