xref: /kernel/linux/linux-6.6/sound/soc/sh/rcar/ssiu.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Renesas R-Car SSIU support
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "rsnd.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define SSIU_NAME "ssiu"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct rsnd_ssiu {
1262306a36Sopenharmony_ci	struct rsnd_mod mod;
1362306a36Sopenharmony_ci	u32 busif_status[8]; /* for BUSIF0 - BUSIF7 */
1462306a36Sopenharmony_ci	unsigned int usrcnt;
1562306a36Sopenharmony_ci	int id;
1662306a36Sopenharmony_ci	int id_sub;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* SSI_MODE */
2062306a36Sopenharmony_ci#define TDM_EXT		(1 << 0)
2162306a36Sopenharmony_ci#define TDM_SPLIT	(1 << 8)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
2462306a36Sopenharmony_ci#define rsnd_mod_to_ssiu(_mod) container_of((_mod), struct rsnd_ssiu, mod)
2562306a36Sopenharmony_ci#define for_each_rsnd_ssiu(pos, priv, i)				\
2662306a36Sopenharmony_ci	for (i = 0;							\
2762306a36Sopenharmony_ci	     (i < rsnd_ssiu_nr(priv)) &&				\
2862306a36Sopenharmony_ci		     ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i));	\
2962306a36Sopenharmony_ci	     i++)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/*
3262306a36Sopenharmony_ci *	SSI	Gen2		Gen3		Gen4
3362306a36Sopenharmony_ci *	0	BUSIF0-3	BUSIF0-7	BUSIF0-7
3462306a36Sopenharmony_ci *	1	BUSIF0-3	BUSIF0-7
3562306a36Sopenharmony_ci *	2	BUSIF0-3	BUSIF0-7
3662306a36Sopenharmony_ci *	3	BUSIF0		BUSIF0-7
3762306a36Sopenharmony_ci *	4	BUSIF0		BUSIF0-7
3862306a36Sopenharmony_ci *	5	BUSIF0		BUSIF0
3962306a36Sopenharmony_ci *	6	BUSIF0		BUSIF0
4062306a36Sopenharmony_ci *	7	BUSIF0		BUSIF0
4162306a36Sopenharmony_ci *	8	BUSIF0		BUSIF0
4262306a36Sopenharmony_ci *	9	BUSIF0-3	BUSIF0-7
4362306a36Sopenharmony_ci *	total	22		52		8
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_cistatic const int gen2_id[] = { 0, 4,  8, 12, 13, 14, 15, 16, 17, 18 };
4662306a36Sopenharmony_cistatic const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 };
4762306a36Sopenharmony_cistatic const int gen4_id[] = { 0 };
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* enable busif buffer over/under run interrupt. */
5062306a36Sopenharmony_ci#define rsnd_ssiu_busif_err_irq_enable(mod)  rsnd_ssiu_busif_err_irq_ctrl(mod, 1)
5162306a36Sopenharmony_ci#define rsnd_ssiu_busif_err_irq_disable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 0)
5262306a36Sopenharmony_cistatic void rsnd_ssiu_busif_err_irq_ctrl(struct rsnd_mod *mod, int enable)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	int id = rsnd_mod_id(mod);
5562306a36Sopenharmony_ci	int shift, offset;
5662306a36Sopenharmony_ci	int i;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	switch (id) {
5962306a36Sopenharmony_ci	case 0:
6062306a36Sopenharmony_ci	case 1:
6162306a36Sopenharmony_ci	case 2:
6262306a36Sopenharmony_ci	case 3:
6362306a36Sopenharmony_ci	case 4:
6462306a36Sopenharmony_ci		shift  = id;
6562306a36Sopenharmony_ci		offset = 0;
6662306a36Sopenharmony_ci		break;
6762306a36Sopenharmony_ci	case 9:
6862306a36Sopenharmony_ci		shift  = 1;
6962306a36Sopenharmony_ci		offset = 1;
7062306a36Sopenharmony_ci		break;
7162306a36Sopenharmony_ci	default:
7262306a36Sopenharmony_ci		return;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
7662306a36Sopenharmony_ci		enum rsnd_reg reg = SSI_SYS_INT_ENABLE((i * 2) + offset);
7762306a36Sopenharmony_ci		u32 val = 0xf << (shift * 4);
7862306a36Sopenharmony_ci		u32 sys_int_enable = rsnd_mod_read(mod, reg);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		if (enable)
8162306a36Sopenharmony_ci			sys_int_enable |= val;
8262306a36Sopenharmony_ci		else
8362306a36Sopenharmony_ci			sys_int_enable &= ~val;
8462306a36Sopenharmony_ci		rsnd_mod_write(mod, reg, sys_int_enable);
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cibool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	bool error = false;
9162306a36Sopenharmony_ci	int id = rsnd_mod_id(mod);
9262306a36Sopenharmony_ci	int shift, offset;
9362306a36Sopenharmony_ci	int i;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	switch (id) {
9662306a36Sopenharmony_ci	case 0:
9762306a36Sopenharmony_ci	case 1:
9862306a36Sopenharmony_ci	case 2:
9962306a36Sopenharmony_ci	case 3:
10062306a36Sopenharmony_ci	case 4:
10162306a36Sopenharmony_ci		shift  = id;
10262306a36Sopenharmony_ci		offset = 0;
10362306a36Sopenharmony_ci		break;
10462306a36Sopenharmony_ci	case 9:
10562306a36Sopenharmony_ci		shift  = 1;
10662306a36Sopenharmony_ci		offset = 1;
10762306a36Sopenharmony_ci		break;
10862306a36Sopenharmony_ci	default:
10962306a36Sopenharmony_ci		goto out;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
11362306a36Sopenharmony_ci		u32 reg = SSI_SYS_STATUS(i * 2) + offset;
11462306a36Sopenharmony_ci		u32 status = rsnd_mod_read(mod, reg);
11562306a36Sopenharmony_ci		u32 val = 0xf << (shift * 4);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		status &= val;
11862306a36Sopenharmony_ci		if (status) {
11962306a36Sopenharmony_ci			struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
12062306a36Sopenharmony_ci			struct device *dev = rsnd_priv_to_dev(priv);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci			rsnd_print_irq_status(dev, "%s err status : 0x%08x\n",
12362306a36Sopenharmony_ci					      rsnd_mod_name(mod), status);
12462306a36Sopenharmony_ci			error = true;
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci		rsnd_mod_write(mod, reg, val);
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ciout:
12962306a36Sopenharmony_ci	return error;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod,
13362306a36Sopenharmony_ci				 struct rsnd_dai_stream *io,
13462306a36Sopenharmony_ci				 enum rsnd_mod_type type)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
13762306a36Sopenharmony_ci	int busif = rsnd_mod_id_sub(mod);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return &ssiu->busif_status[busif];
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int rsnd_ssiu_init(struct rsnd_mod *mod,
14362306a36Sopenharmony_ci			  struct rsnd_dai_stream *io,
14462306a36Sopenharmony_ci			  struct rsnd_priv *priv)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
14762306a36Sopenharmony_ci	u32 ssis = rsnd_ssi_multi_secondaries_runtime(io);
14862306a36Sopenharmony_ci	int use_busif = rsnd_ssi_use_busif(io);
14962306a36Sopenharmony_ci	int id = rsnd_mod_id(mod);
15062306a36Sopenharmony_ci	int is_clk_master = rsnd_rdai_is_clk_master(rdai);
15162306a36Sopenharmony_ci	u32 val1, val2;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* clear status */
15462306a36Sopenharmony_ci	rsnd_ssiu_busif_err_status_clear(mod);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* Gen4 doesn't have SSI_MODE */
15762306a36Sopenharmony_ci	if (rsnd_is_gen4(priv))
15862306a36Sopenharmony_ci		goto ssi_mode_setting_end;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/*
16162306a36Sopenharmony_ci	 * SSI_MODE0
16262306a36Sopenharmony_ci	 */
16362306a36Sopenharmony_ci	rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/*
16662306a36Sopenharmony_ci	 * SSI_MODE1 / SSI_MODE2
16762306a36Sopenharmony_ci	 *
16862306a36Sopenharmony_ci	 * FIXME
16962306a36Sopenharmony_ci	 * sharing/multi with SSI0 are mainly supported
17062306a36Sopenharmony_ci	 */
17162306a36Sopenharmony_ci	val1 = rsnd_mod_read(mod, SSI_MODE1);
17262306a36Sopenharmony_ci	val2 = rsnd_mod_read(mod, SSI_MODE2);
17362306a36Sopenharmony_ci	if (rsnd_ssi_is_pin_sharing(io)) {
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		ssis |= (1 << id);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	} else if (ssis) {
17862306a36Sopenharmony_ci		/*
17962306a36Sopenharmony_ci		 * Multi SSI
18062306a36Sopenharmony_ci		 *
18162306a36Sopenharmony_ci		 * set synchronized bit here
18262306a36Sopenharmony_ci		 */
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		/* SSI4 is synchronized with SSI3 */
18562306a36Sopenharmony_ci		if (ssis & (1 << 4))
18662306a36Sopenharmony_ci			val1 |= (1 << 20);
18762306a36Sopenharmony_ci		/* SSI012 are synchronized */
18862306a36Sopenharmony_ci		if (ssis == 0x0006)
18962306a36Sopenharmony_ci			val1 |= (1 << 4);
19062306a36Sopenharmony_ci		/* SSI0129 are synchronized */
19162306a36Sopenharmony_ci		if (ssis == 0x0206)
19262306a36Sopenharmony_ci			val2 |= (1 << 4);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* SSI1 is sharing pin with SSI0 */
19662306a36Sopenharmony_ci	if (ssis & (1 << 1))
19762306a36Sopenharmony_ci		val1 |= is_clk_master ? 0x2 : 0x1;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* SSI2 is sharing pin with SSI0 */
20062306a36Sopenharmony_ci	if (ssis & (1 << 2))
20162306a36Sopenharmony_ci		val1 |= is_clk_master ?	0x2 << 2 :
20262306a36Sopenharmony_ci					0x1 << 2;
20362306a36Sopenharmony_ci	/* SSI4 is sharing pin with SSI3 */
20462306a36Sopenharmony_ci	if (ssis & (1 << 4))
20562306a36Sopenharmony_ci		val1 |= is_clk_master ? 0x2 << 16 :
20662306a36Sopenharmony_ci					0x1 << 16;
20762306a36Sopenharmony_ci	/* SSI9 is sharing pin with SSI0 */
20862306a36Sopenharmony_ci	if (ssis & (1 << 9))
20962306a36Sopenharmony_ci		val2 |= is_clk_master ? 0x2 : 0x1;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	rsnd_mod_bset(mod, SSI_MODE1, 0x0013001f, val1);
21262306a36Sopenharmony_ci	rsnd_mod_bset(mod, SSI_MODE2, 0x00000017, val2);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cissi_mode_setting_end:
21562306a36Sopenharmony_ci	/*
21662306a36Sopenharmony_ci	 * Enable busif buffer over/under run interrupt.
21762306a36Sopenharmony_ci	 * It will be handled from ssi.c
21862306a36Sopenharmony_ci	 * see
21962306a36Sopenharmony_ci	 *	__rsnd_ssi_interrupt()
22062306a36Sopenharmony_ci	 */
22162306a36Sopenharmony_ci	rsnd_ssiu_busif_err_irq_enable(mod);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return 0;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int rsnd_ssiu_quit(struct rsnd_mod *mod,
22762306a36Sopenharmony_ci			  struct rsnd_dai_stream *io,
22862306a36Sopenharmony_ci			  struct rsnd_priv *priv)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	/* disable busif buffer over/under run interrupt. */
23162306a36Sopenharmony_ci	rsnd_ssiu_busif_err_irq_disable(mod);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return 0;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = {
23762306a36Sopenharmony_ci	.name		= SSIU_NAME,
23862306a36Sopenharmony_ci	.init		= rsnd_ssiu_init,
23962306a36Sopenharmony_ci	.quit		= rsnd_ssiu_quit,
24062306a36Sopenharmony_ci	.get_status	= rsnd_ssiu_get_status,
24162306a36Sopenharmony_ci};
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
24462306a36Sopenharmony_ci			       struct rsnd_dai_stream *io,
24562306a36Sopenharmony_ci			       struct rsnd_priv *priv)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
24862306a36Sopenharmony_ci	u32 has_hdmi0 = rsnd_flags_has(io, RSND_STREAM_HDMI0);
24962306a36Sopenharmony_ci	u32 has_hdmi1 = rsnd_flags_has(io, RSND_STREAM_HDMI1);
25062306a36Sopenharmony_ci	int ret;
25162306a36Sopenharmony_ci	u32 mode = 0;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	ret = rsnd_ssiu_init(mod, io, priv);
25462306a36Sopenharmony_ci	if (ret < 0)
25562306a36Sopenharmony_ci		return ret;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	ssiu->usrcnt++;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/*
26062306a36Sopenharmony_ci	 * TDM Extend/Split Mode
26162306a36Sopenharmony_ci	 * see
26262306a36Sopenharmony_ci	 *	rsnd_ssi_config_init()
26362306a36Sopenharmony_ci	 */
26462306a36Sopenharmony_ci	if (rsnd_runtime_is_tdm(io))
26562306a36Sopenharmony_ci		mode = TDM_EXT;
26662306a36Sopenharmony_ci	else if (rsnd_runtime_is_tdm_split(io))
26762306a36Sopenharmony_ci		mode = TDM_SPLIT;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	rsnd_mod_write(mod, SSI_MODE, mode);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (rsnd_ssi_use_busif(io)) {
27262306a36Sopenharmony_ci		int id = rsnd_mod_id(mod);
27362306a36Sopenharmony_ci		int busif = rsnd_mod_id_sub(mod);
27462306a36Sopenharmony_ci		enum rsnd_reg adinr_reg, mode_reg, dalign_reg;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		if ((id == 9) && (busif >= 4)) {
27762306a36Sopenharmony_ci			adinr_reg = SSI9_BUSIF_ADINR(busif);
27862306a36Sopenharmony_ci			mode_reg = SSI9_BUSIF_MODE(busif);
27962306a36Sopenharmony_ci			dalign_reg = SSI9_BUSIF_DALIGN(busif);
28062306a36Sopenharmony_ci		} else {
28162306a36Sopenharmony_ci			adinr_reg = SSI_BUSIF_ADINR(busif);
28262306a36Sopenharmony_ci			mode_reg = SSI_BUSIF_MODE(busif);
28362306a36Sopenharmony_ci			dalign_reg = SSI_BUSIF_DALIGN(busif);
28462306a36Sopenharmony_ci		}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		rsnd_mod_write(mod, adinr_reg,
28762306a36Sopenharmony_ci			       rsnd_get_adinr_bit(mod, io) |
28862306a36Sopenharmony_ci			       (rsnd_io_is_play(io) ?
28962306a36Sopenharmony_ci				rsnd_runtime_channel_after_ctu(io) :
29062306a36Sopenharmony_ci				rsnd_runtime_channel_original(io)));
29162306a36Sopenharmony_ci		rsnd_mod_write(mod, mode_reg,
29262306a36Sopenharmony_ci			       rsnd_get_busif_shift(io, mod) | 1);
29362306a36Sopenharmony_ci		rsnd_mod_write(mod, dalign_reg,
29462306a36Sopenharmony_ci			       rsnd_get_dalign(mod, io));
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (has_hdmi0 || has_hdmi1) {
29862306a36Sopenharmony_ci		enum rsnd_mod_type rsnd_ssi_array[] = {
29962306a36Sopenharmony_ci			RSND_MOD_SSIM1,
30062306a36Sopenharmony_ci			RSND_MOD_SSIM2,
30162306a36Sopenharmony_ci			RSND_MOD_SSIM3,
30262306a36Sopenharmony_ci		};
30362306a36Sopenharmony_ci		struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
30462306a36Sopenharmony_ci		struct rsnd_mod *pos;
30562306a36Sopenharmony_ci		u32 val;
30662306a36Sopenharmony_ci		int i;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		i = rsnd_mod_id(ssi_mod);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		/* output all same SSI as default */
31162306a36Sopenharmony_ci		val =	i << 16 |
31262306a36Sopenharmony_ci			i << 20 |
31362306a36Sopenharmony_ci			i << 24 |
31462306a36Sopenharmony_ci			i << 28 |
31562306a36Sopenharmony_ci			i;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) {
31862306a36Sopenharmony_ci			int shift = (i * 4) + 20;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci			val	= (val & ~(0xF << shift)) |
32162306a36Sopenharmony_ci				rsnd_mod_id(pos) << shift;
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		if (has_hdmi0)
32562306a36Sopenharmony_ci			rsnd_mod_write(mod, HDMI0_SEL, val);
32662306a36Sopenharmony_ci		if (has_hdmi1)
32762306a36Sopenharmony_ci			rsnd_mod_write(mod, HDMI1_SEL, val);
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return 0;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
33462306a36Sopenharmony_ci				struct rsnd_dai_stream *io,
33562306a36Sopenharmony_ci				struct rsnd_priv *priv)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int busif = rsnd_mod_id_sub(mod);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (!rsnd_ssi_use_busif(io))
34062306a36Sopenharmony_ci		return 0;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 1 << (busif * 4));
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (rsnd_ssi_multi_secondaries_runtime(io))
34562306a36Sopenharmony_ci		rsnd_mod_write(mod, SSI_CONTROL, 0x1);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return 0;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
35162306a36Sopenharmony_ci			       struct rsnd_dai_stream *io,
35262306a36Sopenharmony_ci			       struct rsnd_priv *priv)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
35562306a36Sopenharmony_ci	int busif = rsnd_mod_id_sub(mod);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (!rsnd_ssi_use_busif(io))
35862306a36Sopenharmony_ci		return 0;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 0);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (--ssiu->usrcnt)
36362306a36Sopenharmony_ci		return 0;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (rsnd_ssi_multi_secondaries_runtime(io))
36662306a36Sopenharmony_ci		rsnd_mod_write(mod, SSI_CONTROL, 0);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic int rsnd_ssiu_id(struct rsnd_mod *mod)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* see rsnd_ssiu_probe() */
37662306a36Sopenharmony_ci	return ssiu->id;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int rsnd_ssiu_id_sub(struct rsnd_mod *mod)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* see rsnd_ssiu_probe() */
38462306a36Sopenharmony_ci	return ssiu->id_sub;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io,
38862306a36Sopenharmony_ci					  struct rsnd_mod *mod)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
39162306a36Sopenharmony_ci	int is_play = rsnd_io_is_play(io);
39262306a36Sopenharmony_ci	char *name;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/*
39562306a36Sopenharmony_ci	 * It should use "rcar_sound,ssiu" on DT.
39662306a36Sopenharmony_ci	 * But, we need to keep compatibility for old version.
39762306a36Sopenharmony_ci	 *
39862306a36Sopenharmony_ci	 * If it has "rcar_sound.ssiu", it will be used.
39962306a36Sopenharmony_ci	 * If not, "rcar_sound.ssi" will be used.
40062306a36Sopenharmony_ci	 * see
40162306a36Sopenharmony_ci	 *	rsnd_ssi_dma_req()
40262306a36Sopenharmony_ci	 *	rsnd_dma_of_path()
40362306a36Sopenharmony_ci	 */
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	name = is_play ? "rx" : "tx";
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv),
40862306a36Sopenharmony_ci					SSIU_NAME, mod, name);
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
41262306a36Sopenharmony_cistatic void rsnd_ssiu_debug_info(struct seq_file *m,
41362306a36Sopenharmony_ci				 struct rsnd_dai_stream *io,
41462306a36Sopenharmony_ci				struct rsnd_mod *mod)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SSIU,
41762306a36Sopenharmony_ci				  rsnd_mod_id(mod) * 0x80, 0x80);
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci#define DEBUG_INFO .debug_info = rsnd_ssiu_debug_info
42062306a36Sopenharmony_ci#else
42162306a36Sopenharmony_ci#define DEBUG_INFO
42262306a36Sopenharmony_ci#endif
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
42562306a36Sopenharmony_ci	.name		= SSIU_NAME,
42662306a36Sopenharmony_ci	.dma_req	= rsnd_ssiu_dma_req,
42762306a36Sopenharmony_ci	.init		= rsnd_ssiu_init_gen2,
42862306a36Sopenharmony_ci	.quit		= rsnd_ssiu_quit,
42962306a36Sopenharmony_ci	.start		= rsnd_ssiu_start_gen2,
43062306a36Sopenharmony_ci	.stop		= rsnd_ssiu_stop_gen2,
43162306a36Sopenharmony_ci	.get_status	= rsnd_ssiu_get_status,
43262306a36Sopenharmony_ci	DEBUG_INFO
43362306a36Sopenharmony_ci};
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv)))
43862306a36Sopenharmony_ci		id = 0;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv,
44462306a36Sopenharmony_ci					       struct rsnd_dai_stream *io)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
44762306a36Sopenharmony_ci	struct rsnd_ssiu *ssiu;
44862306a36Sopenharmony_ci	int is_dma_mode;
44962306a36Sopenharmony_ci	int i;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (!ssi_mod)
45262306a36Sopenharmony_ci		return;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	is_dma_mode = rsnd_ssi_is_dma_mode(ssi_mod);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/* select BUSIF0 */
45762306a36Sopenharmony_ci	for_each_rsnd_ssiu(ssiu, priv, i) {
45862306a36Sopenharmony_ci		struct rsnd_mod *mod = rsnd_mod_get(ssiu);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		if (is_dma_mode &&
46162306a36Sopenharmony_ci		    (rsnd_mod_id(ssi_mod) == rsnd_mod_id(mod)) &&
46262306a36Sopenharmony_ci		    (rsnd_mod_id_sub(mod) == 0)) {
46362306a36Sopenharmony_ci			rsnd_dai_connect(mod, io, mod->type);
46462306a36Sopenharmony_ci			return;
46562306a36Sopenharmony_ci		}
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_civoid rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
47062306a36Sopenharmony_ci			     struct device_node *playback,
47162306a36Sopenharmony_ci			     struct device_node *capture)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
47462306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
47562306a36Sopenharmony_ci	struct device_node *node = rsnd_ssiu_of_node(priv);
47662306a36Sopenharmony_ci	struct rsnd_dai_stream *io_p = &rdai->playback;
47762306a36Sopenharmony_ci	struct rsnd_dai_stream *io_c = &rdai->capture;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* use rcar_sound,ssiu if exist */
48062306a36Sopenharmony_ci	if (node) {
48162306a36Sopenharmony_ci		struct device_node *np;
48262306a36Sopenharmony_ci		int i = 0;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		for_each_child_of_node(node, np) {
48562306a36Sopenharmony_ci			struct rsnd_mod *mod;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci			i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i);
48862306a36Sopenharmony_ci			if (i < 0) {
48962306a36Sopenharmony_ci				of_node_put(np);
49062306a36Sopenharmony_ci				break;
49162306a36Sopenharmony_ci			}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci			mod = rsnd_ssiu_mod_get(priv, i);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci			if (np == playback)
49662306a36Sopenharmony_ci				rsnd_dai_connect(mod, io_p, mod->type);
49762306a36Sopenharmony_ci			if (np == capture)
49862306a36Sopenharmony_ci				rsnd_dai_connect(mod, io_c, mod->type);
49962306a36Sopenharmony_ci			i++;
50062306a36Sopenharmony_ci		}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		of_node_put(node);
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/* Keep DT compatibility */
50662306a36Sopenharmony_ci	if (!rsnd_io_to_mod_ssiu(io_p))
50762306a36Sopenharmony_ci		rsnd_parse_connect_ssiu_compatible(priv, io_p);
50862306a36Sopenharmony_ci	if (!rsnd_io_to_mod_ssiu(io_c))
50962306a36Sopenharmony_ci		rsnd_parse_connect_ssiu_compatible(priv, io_c);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ciint rsnd_ssiu_probe(struct rsnd_priv *priv)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
51562306a36Sopenharmony_ci	struct device_node *node;
51662306a36Sopenharmony_ci	struct rsnd_ssiu *ssiu;
51762306a36Sopenharmony_ci	struct rsnd_mod_ops *ops;
51862306a36Sopenharmony_ci	const int *list = NULL;
51962306a36Sopenharmony_ci	int i, nr;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/*
52262306a36Sopenharmony_ci	 * Keep DT compatibility.
52362306a36Sopenharmony_ci	 * if it has "rcar_sound,ssiu", use it.
52462306a36Sopenharmony_ci	 * if not, use "rcar_sound,ssi"
52562306a36Sopenharmony_ci	 * see
52662306a36Sopenharmony_ci	 *	rsnd_ssiu_bufsif_to_id()
52762306a36Sopenharmony_ci	 */
52862306a36Sopenharmony_ci	node = rsnd_ssiu_of_node(priv);
52962306a36Sopenharmony_ci	if (node)
53062306a36Sopenharmony_ci		nr = rsnd_node_count(priv, node, SSIU_NAME);
53162306a36Sopenharmony_ci	else
53262306a36Sopenharmony_ci		nr = priv->ssi_nr;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (!nr)
53562306a36Sopenharmony_ci		return -EINVAL;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	ssiu	= devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL);
53862306a36Sopenharmony_ci	if (!ssiu)
53962306a36Sopenharmony_ci		return -ENOMEM;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	priv->ssiu	= ssiu;
54262306a36Sopenharmony_ci	priv->ssiu_nr	= nr;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (rsnd_is_gen1(priv))
54562306a36Sopenharmony_ci		ops = &rsnd_ssiu_ops_gen1;
54662306a36Sopenharmony_ci	else
54762306a36Sopenharmony_ci		ops = &rsnd_ssiu_ops_gen2;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* Keep compatibility */
55062306a36Sopenharmony_ci	nr = 0;
55162306a36Sopenharmony_ci	if ((node) &&
55262306a36Sopenharmony_ci	    (ops == &rsnd_ssiu_ops_gen2)) {
55362306a36Sopenharmony_ci		ops->id		= rsnd_ssiu_id;
55462306a36Sopenharmony_ci		ops->id_sub	= rsnd_ssiu_id_sub;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		if (rsnd_is_gen2(priv)) {
55762306a36Sopenharmony_ci			list	= gen2_id;
55862306a36Sopenharmony_ci			nr	= ARRAY_SIZE(gen2_id);
55962306a36Sopenharmony_ci		} else if (rsnd_is_gen3(priv)) {
56062306a36Sopenharmony_ci			list	= gen3_id;
56162306a36Sopenharmony_ci			nr	= ARRAY_SIZE(gen3_id);
56262306a36Sopenharmony_ci		} else if (rsnd_is_gen4(priv)) {
56362306a36Sopenharmony_ci			list	= gen4_id;
56462306a36Sopenharmony_ci			nr	= ARRAY_SIZE(gen4_id);
56562306a36Sopenharmony_ci		} else {
56662306a36Sopenharmony_ci			dev_err(dev, "unknown SSIU\n");
56762306a36Sopenharmony_ci			return -ENODEV;
56862306a36Sopenharmony_ci		}
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	for_each_rsnd_ssiu(ssiu, priv, i) {
57262306a36Sopenharmony_ci		int ret;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		if (node) {
57562306a36Sopenharmony_ci			int j;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci			/*
57862306a36Sopenharmony_ci			 * see
57962306a36Sopenharmony_ci			 *	rsnd_ssiu_get_id()
58062306a36Sopenharmony_ci			 *	rsnd_ssiu_get_id_sub()
58162306a36Sopenharmony_ci			 */
58262306a36Sopenharmony_ci			for (j = 0; j < nr; j++) {
58362306a36Sopenharmony_ci				if (list[j] > i)
58462306a36Sopenharmony_ci					break;
58562306a36Sopenharmony_ci				ssiu->id	= j;
58662306a36Sopenharmony_ci				ssiu->id_sub	= i - list[ssiu->id];
58762306a36Sopenharmony_ci			}
58862306a36Sopenharmony_ci		} else {
58962306a36Sopenharmony_ci			ssiu->id = i;
59062306a36Sopenharmony_ci		}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci		ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
59362306a36Sopenharmony_ci				    ops, NULL, RSND_MOD_SSIU, i);
59462306a36Sopenharmony_ci		if (ret)
59562306a36Sopenharmony_ci			return ret;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return 0;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_civoid rsnd_ssiu_remove(struct rsnd_priv *priv)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	struct rsnd_ssiu *ssiu;
60462306a36Sopenharmony_ci	int i;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	for_each_rsnd_ssiu(ssiu, priv, i) {
60762306a36Sopenharmony_ci		rsnd_mod_quit(rsnd_mod_get(ssiu));
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci}
610