162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Renesas R-Car SRC support
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2013 Renesas Solutions Corp.
662306a36Sopenharmony_ci// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * You can use Synchronous Sampling Rate Convert (if no DVC)
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *	amixer set "SRC Out Rate" on
1262306a36Sopenharmony_ci *	aplay xxx.wav &
1362306a36Sopenharmony_ci *	amixer set "SRC Out Rate" 96000 // convert rate to 96000Hz
1462306a36Sopenharmony_ci *	amixer set "SRC Out Rate" 22050 // convert rate to 22050Hz
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * you can enable below define if you don't need
1962306a36Sopenharmony_ci * SSI interrupt status debug message when debugging
2062306a36Sopenharmony_ci * see rsnd_print_irq_status()
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * #define RSND_DEBUG_NO_IRQ_STATUS 1
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "rsnd.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define SRC_NAME "src"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* SCU_SYSTEM_STATUS0/1 */
3062306a36Sopenharmony_ci#define OUF_SRC(id)	((1 << (id + 16)) | (1 << id))
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct rsnd_src {
3362306a36Sopenharmony_ci	struct rsnd_mod mod;
3462306a36Sopenharmony_ci	struct rsnd_mod *dma;
3562306a36Sopenharmony_ci	struct rsnd_kctrl_cfg_s sen;  /* sync convert enable */
3662306a36Sopenharmony_ci	struct rsnd_kctrl_cfg_s sync; /* sync convert */
3762306a36Sopenharmony_ci	int irq;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define RSND_SRC_NAME_SIZE 16
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id)
4362306a36Sopenharmony_ci#define rsnd_src_nr(priv) ((priv)->src_nr)
4462306a36Sopenharmony_ci#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val)
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define rsnd_mod_to_src(_mod)				\
4762306a36Sopenharmony_ci	container_of((_mod), struct rsnd_src, mod)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define for_each_rsnd_src(pos, priv, i)				\
5062306a36Sopenharmony_ci	for ((i) = 0;						\
5162306a36Sopenharmony_ci	     ((i) < rsnd_src_nr(priv)) &&			\
5262306a36Sopenharmony_ci	     ((pos) = (struct rsnd_src *)(priv)->src + i);	\
5362306a36Sopenharmony_ci	     i++)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*
5762306a36Sopenharmony_ci *		image of SRC (Sampling Rate Converter)
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci * 96kHz   <-> +-----+	48kHz	+-----+	 48kHz	+-------+
6062306a36Sopenharmony_ci * 48kHz   <-> | SRC | <------>	| SSI |	<----->	| codec |
6162306a36Sopenharmony_ci * 44.1kHz <-> +-----+		+-----+		+-------+
6262306a36Sopenharmony_ci * ...
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void rsnd_src_activation(struct rsnd_mod *mod)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_SWRSR, 0);
6962306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_SWRSR, 1);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void rsnd_src_halt(struct rsnd_mod *mod)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_SRCIR, 1);
7562306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_SWRSR, 0);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
7962306a36Sopenharmony_ci					 struct rsnd_mod *mod)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
8262306a36Sopenharmony_ci	int is_play = rsnd_io_is_play(io);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return rsnd_dma_request_channel(rsnd_src_of_node(priv),
8562306a36Sopenharmony_ci					SRC_NAME, mod,
8662306a36Sopenharmony_ci					is_play ? "rx" : "tx");
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
9062306a36Sopenharmony_ci				 struct rsnd_mod *mod)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
9362306a36Sopenharmony_ci	struct rsnd_src *src = rsnd_mod_to_src(mod);
9462306a36Sopenharmony_ci	u32 convert_rate;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (!runtime)
9762306a36Sopenharmony_ci		return 0;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (!rsnd_src_sync_is_enabled(mod))
10062306a36Sopenharmony_ci		return rsnd_io_converted_rate(io);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	convert_rate = src->sync.val;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (!convert_rate)
10562306a36Sopenharmony_ci		convert_rate = rsnd_io_converted_rate(io);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (!convert_rate)
10862306a36Sopenharmony_ci		convert_rate = runtime->rate;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return convert_rate;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciunsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
11462306a36Sopenharmony_ci			       struct rsnd_dai_stream *io,
11562306a36Sopenharmony_ci			       int is_in)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
11862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
11962306a36Sopenharmony_ci	unsigned int rate = 0;
12062306a36Sopenharmony_ci	int is_play = rsnd_io_is_play(io);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/*
12362306a36Sopenharmony_ci	 * Playback
12462306a36Sopenharmony_ci	 * runtime_rate -> [SRC] -> convert_rate
12562306a36Sopenharmony_ci	 *
12662306a36Sopenharmony_ci	 * Capture
12762306a36Sopenharmony_ci	 * convert_rate -> [SRC] -> runtime_rate
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (is_play == is_in)
13162306a36Sopenharmony_ci		return runtime->rate;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/*
13462306a36Sopenharmony_ci	 * return convert rate if SRC is used,
13562306a36Sopenharmony_ci	 * otherwise, return runtime->rate as usual
13662306a36Sopenharmony_ci	 */
13762306a36Sopenharmony_ci	if (src_mod)
13862306a36Sopenharmony_ci		rate = rsnd_src_convert_rate(io, src_mod);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (!rate)
14162306a36Sopenharmony_ci		rate = runtime->rate;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return rate;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic const u32 bsdsr_table_pattern1[] = {
14762306a36Sopenharmony_ci	0x01800000, /* 6 - 1/6 */
14862306a36Sopenharmony_ci	0x01000000, /* 6 - 1/4 */
14962306a36Sopenharmony_ci	0x00c00000, /* 6 - 1/3 */
15062306a36Sopenharmony_ci	0x00800000, /* 6 - 1/2 */
15162306a36Sopenharmony_ci	0x00600000, /* 6 - 2/3 */
15262306a36Sopenharmony_ci	0x00400000, /* 6 - 1   */
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic const u32 bsdsr_table_pattern2[] = {
15662306a36Sopenharmony_ci	0x02400000, /* 6 - 1/6 */
15762306a36Sopenharmony_ci	0x01800000, /* 6 - 1/4 */
15862306a36Sopenharmony_ci	0x01200000, /* 6 - 1/3 */
15962306a36Sopenharmony_ci	0x00c00000, /* 6 - 1/2 */
16062306a36Sopenharmony_ci	0x00900000, /* 6 - 2/3 */
16162306a36Sopenharmony_ci	0x00600000, /* 6 - 1   */
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic const u32 bsisr_table[] = {
16562306a36Sopenharmony_ci	0x00100060, /* 6 - 1/6 */
16662306a36Sopenharmony_ci	0x00100040, /* 6 - 1/4 */
16762306a36Sopenharmony_ci	0x00100030, /* 6 - 1/3 */
16862306a36Sopenharmony_ci	0x00100020, /* 6 - 1/2 */
16962306a36Sopenharmony_ci	0x00100020, /* 6 - 2/3 */
17062306a36Sopenharmony_ci	0x00100020, /* 6 - 1   */
17162306a36Sopenharmony_ci};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic const u32 chan288888[] = {
17462306a36Sopenharmony_ci	0x00000006, /* 1 to 2 */
17562306a36Sopenharmony_ci	0x000001fe, /* 1 to 8 */
17662306a36Sopenharmony_ci	0x000001fe, /* 1 to 8 */
17762306a36Sopenharmony_ci	0x000001fe, /* 1 to 8 */
17862306a36Sopenharmony_ci	0x000001fe, /* 1 to 8 */
17962306a36Sopenharmony_ci	0x000001fe, /* 1 to 8 */
18062306a36Sopenharmony_ci};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic const u32 chan244888[] = {
18362306a36Sopenharmony_ci	0x00000006, /* 1 to 2 */
18462306a36Sopenharmony_ci	0x0000001e, /* 1 to 4 */
18562306a36Sopenharmony_ci	0x0000001e, /* 1 to 4 */
18662306a36Sopenharmony_ci	0x000001fe, /* 1 to 8 */
18762306a36Sopenharmony_ci	0x000001fe, /* 1 to 8 */
18862306a36Sopenharmony_ci	0x000001fe, /* 1 to 8 */
18962306a36Sopenharmony_ci};
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic const u32 chan222222[] = {
19262306a36Sopenharmony_ci	0x00000006, /* 1 to 2 */
19362306a36Sopenharmony_ci	0x00000006, /* 1 to 2 */
19462306a36Sopenharmony_ci	0x00000006, /* 1 to 2 */
19562306a36Sopenharmony_ci	0x00000006, /* 1 to 2 */
19662306a36Sopenharmony_ci	0x00000006, /* 1 to 2 */
19762306a36Sopenharmony_ci	0x00000006, /* 1 to 2 */
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
20162306a36Sopenharmony_ci				      struct rsnd_mod *mod)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
20462306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
20562306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
20662306a36Sopenharmony_ci	int is_play = rsnd_io_is_play(io);
20762306a36Sopenharmony_ci	int use_src = 0;
20862306a36Sopenharmony_ci	u32 fin, fout;
20962306a36Sopenharmony_ci	u32 ifscr, fsrate, adinr;
21062306a36Sopenharmony_ci	u32 cr, route;
21162306a36Sopenharmony_ci	u32 i_busif, o_busif, tmp;
21262306a36Sopenharmony_ci	const u32 *bsdsr_table;
21362306a36Sopenharmony_ci	const u32 *chptn;
21462306a36Sopenharmony_ci	uint ratio;
21562306a36Sopenharmony_ci	int chan;
21662306a36Sopenharmony_ci	int idx;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (!runtime)
21962306a36Sopenharmony_ci		return;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	fin  = rsnd_src_get_in_rate(priv, io);
22262306a36Sopenharmony_ci	fout = rsnd_src_get_out_rate(priv, io);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	chan = rsnd_runtime_channel_original(io);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* 6 - 1/6 are very enough ratio for SRC_BSDSR */
22762306a36Sopenharmony_ci	if (fin == fout)
22862306a36Sopenharmony_ci		ratio = 0;
22962306a36Sopenharmony_ci	else if (fin > fout)
23062306a36Sopenharmony_ci		ratio = 100 * fin / fout;
23162306a36Sopenharmony_ci	else
23262306a36Sopenharmony_ci		ratio = 100 * fout / fin;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (ratio > 600) {
23562306a36Sopenharmony_ci		dev_err(dev, "FSO/FSI ratio error\n");
23662306a36Sopenharmony_ci		return;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/*
24262306a36Sopenharmony_ci	 * SRC_ADINR
24362306a36Sopenharmony_ci	 */
24462306a36Sopenharmony_ci	adinr = rsnd_get_adinr_bit(mod, io) | chan;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/*
24762306a36Sopenharmony_ci	 * SRC_IFSCR / SRC_IFSVR
24862306a36Sopenharmony_ci	 */
24962306a36Sopenharmony_ci	ifscr = 0;
25062306a36Sopenharmony_ci	fsrate = 0;
25162306a36Sopenharmony_ci	if (use_src) {
25262306a36Sopenharmony_ci		u64 n;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		ifscr = 1;
25562306a36Sopenharmony_ci		n = (u64)0x0400000 * fin;
25662306a36Sopenharmony_ci		do_div(n, fout);
25762306a36Sopenharmony_ci		fsrate = n;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/*
26162306a36Sopenharmony_ci	 * SRC_SRCCR / SRC_ROUTE_MODE0
26262306a36Sopenharmony_ci	 */
26362306a36Sopenharmony_ci	cr	= 0x00011110;
26462306a36Sopenharmony_ci	route	= 0x0;
26562306a36Sopenharmony_ci	if (use_src) {
26662306a36Sopenharmony_ci		route	= 0x1;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		if (rsnd_src_sync_is_enabled(mod)) {
26962306a36Sopenharmony_ci			cr |= 0x1;
27062306a36Sopenharmony_ci			route |= rsnd_io_is_play(io) ?
27162306a36Sopenharmony_ci				(0x1 << 24) : (0x1 << 25);
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/*
27662306a36Sopenharmony_ci	 * SRC_BSDSR / SRC_BSISR
27762306a36Sopenharmony_ci	 *
27862306a36Sopenharmony_ci	 * see
27962306a36Sopenharmony_ci	 *	Combination of Register Setting Related to
28062306a36Sopenharmony_ci	 *	FSO/FSI Ratio and Channel, Latency
28162306a36Sopenharmony_ci	 */
28262306a36Sopenharmony_ci	switch (rsnd_mod_id(mod)) {
28362306a36Sopenharmony_ci	case 0:
28462306a36Sopenharmony_ci		chptn		= chan288888;
28562306a36Sopenharmony_ci		bsdsr_table	= bsdsr_table_pattern1;
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci	case 1:
28862306a36Sopenharmony_ci	case 3:
28962306a36Sopenharmony_ci	case 4:
29062306a36Sopenharmony_ci		chptn		= chan244888;
29162306a36Sopenharmony_ci		bsdsr_table	= bsdsr_table_pattern1;
29262306a36Sopenharmony_ci		break;
29362306a36Sopenharmony_ci	case 2:
29462306a36Sopenharmony_ci	case 9:
29562306a36Sopenharmony_ci		chptn		= chan222222;
29662306a36Sopenharmony_ci		bsdsr_table	= bsdsr_table_pattern1;
29762306a36Sopenharmony_ci		break;
29862306a36Sopenharmony_ci	case 5:
29962306a36Sopenharmony_ci	case 6:
30062306a36Sopenharmony_ci	case 7:
30162306a36Sopenharmony_ci	case 8:
30262306a36Sopenharmony_ci		chptn		= chan222222;
30362306a36Sopenharmony_ci		bsdsr_table	= bsdsr_table_pattern2;
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	default:
30662306a36Sopenharmony_ci		goto convert_rate_err;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/*
31062306a36Sopenharmony_ci	 * E3 need to overwrite
31162306a36Sopenharmony_ci	 */
31262306a36Sopenharmony_ci	if (rsnd_is_e3(priv))
31362306a36Sopenharmony_ci		switch (rsnd_mod_id(mod)) {
31462306a36Sopenharmony_ci		case 0:
31562306a36Sopenharmony_ci		case 4:
31662306a36Sopenharmony_ci			chptn	= chan222222;
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(chan222222); idx++)
32062306a36Sopenharmony_ci		if (chptn[idx] & (1 << chan))
32162306a36Sopenharmony_ci			break;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (chan > 8 ||
32462306a36Sopenharmony_ci	    idx >= ARRAY_SIZE(chan222222))
32562306a36Sopenharmony_ci		goto convert_rate_err;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* BUSIF_MODE */
32862306a36Sopenharmony_ci	tmp = rsnd_get_busif_shift(io, mod);
32962306a36Sopenharmony_ci	i_busif = ( is_play ? tmp : 0) | 1;
33062306a36Sopenharmony_ci	o_busif = (!is_play ? tmp : 0) | 1;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_SRCIR, 1);	/* initialize */
33562306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_ADINR, adinr);
33662306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_IFSCR, ifscr);
33762306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_IFSVR, fsrate);
33862306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_SRCCR, cr);
33962306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]);
34062306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]);
34162306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_SRCIR, 0);	/* cancel initialize */
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_I_BUSIF_MODE, i_busif);
34462306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_O_BUSIF_MODE, o_busif);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	return;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ciconvert_rate_err:
35362306a36Sopenharmony_ci	dev_err(dev, "unknown BSDSR/BSDIR settings\n");
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic int rsnd_src_irq(struct rsnd_mod *mod,
35762306a36Sopenharmony_ci			struct rsnd_dai_stream *io,
35862306a36Sopenharmony_ci			struct rsnd_priv *priv,
35962306a36Sopenharmony_ci			int enable)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct rsnd_src *src = rsnd_mod_to_src(mod);
36262306a36Sopenharmony_ci	u32 sys_int_val, int_val, sys_int_mask;
36362306a36Sopenharmony_ci	int irq = src->irq;
36462306a36Sopenharmony_ci	int id = rsnd_mod_id(mod);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	sys_int_val =
36762306a36Sopenharmony_ci	sys_int_mask = OUF_SRC(id);
36862306a36Sopenharmony_ci	int_val = 0x3300;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/*
37162306a36Sopenharmony_ci	 * IRQ is not supported on non-DT
37262306a36Sopenharmony_ci	 * see
37362306a36Sopenharmony_ci	 *	rsnd_src_probe_()
37462306a36Sopenharmony_ci	 */
37562306a36Sopenharmony_ci	if ((irq <= 0) || !enable) {
37662306a36Sopenharmony_ci		sys_int_val = 0;
37762306a36Sopenharmony_ci		int_val = 0;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/*
38162306a36Sopenharmony_ci	 * WORKAROUND
38262306a36Sopenharmony_ci	 *
38362306a36Sopenharmony_ci	 * ignore over flow error when rsnd_src_sync_is_enabled()
38462306a36Sopenharmony_ci	 */
38562306a36Sopenharmony_ci	if (rsnd_src_sync_is_enabled(mod))
38662306a36Sopenharmony_ci		sys_int_val = sys_int_val & 0xffff;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val);
38962306a36Sopenharmony_ci	rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val);
39062306a36Sopenharmony_ci	rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic void rsnd_src_status_clear(struct rsnd_mod *mod)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	u32 val = OUF_SRC(rsnd_mod_id(mod));
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	rsnd_mod_write(mod, SCU_SYS_STATUS0, val);
40062306a36Sopenharmony_ci	rsnd_mod_write(mod, SCU_SYS_STATUS1, val);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic bool rsnd_src_error_occurred(struct rsnd_mod *mod)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
40662306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
40762306a36Sopenharmony_ci	u32 val0, val1;
40862306a36Sopenharmony_ci	u32 status0, status1;
40962306a36Sopenharmony_ci	bool ret = false;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	val0 = val1 = OUF_SRC(rsnd_mod_id(mod));
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/*
41462306a36Sopenharmony_ci	 * WORKAROUND
41562306a36Sopenharmony_ci	 *
41662306a36Sopenharmony_ci	 * ignore over flow error when rsnd_src_sync_is_enabled()
41762306a36Sopenharmony_ci	 */
41862306a36Sopenharmony_ci	if (rsnd_src_sync_is_enabled(mod))
41962306a36Sopenharmony_ci		val0 = val0 & 0xffff;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0);
42262306a36Sopenharmony_ci	status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1);
42362306a36Sopenharmony_ci	if ((status0 & val0) || (status1 & val1)) {
42462306a36Sopenharmony_ci		rsnd_print_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n",
42562306a36Sopenharmony_ci				      rsnd_mod_name(mod), status0, status1);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		ret = true;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return ret;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int rsnd_src_start(struct rsnd_mod *mod,
43462306a36Sopenharmony_ci			  struct rsnd_dai_stream *io,
43562306a36Sopenharmony_ci			  struct rsnd_priv *priv)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	u32 val;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/*
44062306a36Sopenharmony_ci	 * WORKAROUND
44162306a36Sopenharmony_ci	 *
44262306a36Sopenharmony_ci	 * Enable SRC output if you want to use sync convert together with DVC
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ?
44562306a36Sopenharmony_ci		0x01 : 0x11;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_CTRL, val);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return 0;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int rsnd_src_stop(struct rsnd_mod *mod,
45362306a36Sopenharmony_ci			 struct rsnd_dai_stream *io,
45462306a36Sopenharmony_ci			 struct rsnd_priv *priv)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	rsnd_mod_write(mod, SRC_CTRL, 0);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	return 0;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic int rsnd_src_init(struct rsnd_mod *mod,
46262306a36Sopenharmony_ci			 struct rsnd_dai_stream *io,
46362306a36Sopenharmony_ci			 struct rsnd_priv *priv)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct rsnd_src *src = rsnd_mod_to_src(mod);
46662306a36Sopenharmony_ci	int ret;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* reset sync convert_rate */
46962306a36Sopenharmony_ci	src->sync.val = 0;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	ret = rsnd_mod_power_on(mod);
47262306a36Sopenharmony_ci	if (ret < 0)
47362306a36Sopenharmony_ci		return ret;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	rsnd_src_activation(mod);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	rsnd_src_set_convert_rate(io, mod);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	rsnd_src_status_clear(mod);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return 0;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int rsnd_src_quit(struct rsnd_mod *mod,
48562306a36Sopenharmony_ci			 struct rsnd_dai_stream *io,
48662306a36Sopenharmony_ci			 struct rsnd_priv *priv)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct rsnd_src *src = rsnd_mod_to_src(mod);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	rsnd_src_halt(mod);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	rsnd_mod_power_off(mod);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* reset sync convert_rate */
49562306a36Sopenharmony_ci	src->sync.val = 0;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return 0;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic void __rsnd_src_interrupt(struct rsnd_mod *mod,
50162306a36Sopenharmony_ci				 struct rsnd_dai_stream *io)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
50462306a36Sopenharmony_ci	bool stop = false;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	spin_lock(&priv->lock);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* ignore all cases if not working */
50962306a36Sopenharmony_ci	if (!rsnd_io_is_working(io))
51062306a36Sopenharmony_ci		goto rsnd_src_interrupt_out;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (rsnd_src_error_occurred(mod))
51362306a36Sopenharmony_ci		stop = true;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	rsnd_src_status_clear(mod);
51662306a36Sopenharmony_cirsnd_src_interrupt_out:
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	spin_unlock(&priv->lock);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (stop)
52162306a36Sopenharmony_ci		snd_pcm_stop_xrun(io->substream);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic irqreturn_t rsnd_src_interrupt(int irq, void *data)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct rsnd_mod *mod = data;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	rsnd_mod_interrupt(mod, __rsnd_src_interrupt);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return IRQ_HANDLED;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic int rsnd_src_probe_(struct rsnd_mod *mod,
53462306a36Sopenharmony_ci			   struct rsnd_dai_stream *io,
53562306a36Sopenharmony_ci			   struct rsnd_priv *priv)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct rsnd_src *src = rsnd_mod_to_src(mod);
53862306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
53962306a36Sopenharmony_ci	int irq = src->irq;
54062306a36Sopenharmony_ci	int ret;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (irq > 0) {
54362306a36Sopenharmony_ci		/*
54462306a36Sopenharmony_ci		 * IRQ is not supported on non-DT
54562306a36Sopenharmony_ci		 * see
54662306a36Sopenharmony_ci		 *	rsnd_src_irq()
54762306a36Sopenharmony_ci		 */
54862306a36Sopenharmony_ci		ret = devm_request_irq(dev, irq,
54962306a36Sopenharmony_ci				       rsnd_src_interrupt,
55062306a36Sopenharmony_ci				       IRQF_SHARED,
55162306a36Sopenharmony_ci				       dev_name(dev), mod);
55262306a36Sopenharmony_ci		if (ret)
55362306a36Sopenharmony_ci			return ret;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	ret = rsnd_dma_attach(io, mod, &src->dma);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return ret;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic int rsnd_src_pcm_new(struct rsnd_mod *mod,
56262306a36Sopenharmony_ci			    struct rsnd_dai_stream *io,
56362306a36Sopenharmony_ci			    struct snd_soc_pcm_runtime *rtd)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct rsnd_src *src = rsnd_mod_to_src(mod);
56662306a36Sopenharmony_ci	int ret;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/*
56962306a36Sopenharmony_ci	 * enable SRC sync convert if possible
57062306a36Sopenharmony_ci	 */
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/*
57362306a36Sopenharmony_ci	 * It can't use SRC Synchronous convert
57462306a36Sopenharmony_ci	 * when Capture if it uses CMD
57562306a36Sopenharmony_ci	 */
57662306a36Sopenharmony_ci	if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io))
57762306a36Sopenharmony_ci		return 0;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	/*
58062306a36Sopenharmony_ci	 * enable sync convert
58162306a36Sopenharmony_ci	 */
58262306a36Sopenharmony_ci	ret = rsnd_kctrl_new_s(mod, io, rtd,
58362306a36Sopenharmony_ci			       rsnd_io_is_play(io) ?
58462306a36Sopenharmony_ci			       "SRC Out Rate Switch" :
58562306a36Sopenharmony_ci			       "SRC In Rate Switch",
58662306a36Sopenharmony_ci			       rsnd_kctrl_accept_anytime,
58762306a36Sopenharmony_ci			       rsnd_src_set_convert_rate,
58862306a36Sopenharmony_ci			       &src->sen, 1);
58962306a36Sopenharmony_ci	if (ret < 0)
59062306a36Sopenharmony_ci		return ret;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	ret = rsnd_kctrl_new_s(mod, io, rtd,
59362306a36Sopenharmony_ci			       rsnd_io_is_play(io) ?
59462306a36Sopenharmony_ci			       "SRC Out Rate" :
59562306a36Sopenharmony_ci			       "SRC In Rate",
59662306a36Sopenharmony_ci			       rsnd_kctrl_accept_runtime,
59762306a36Sopenharmony_ci			       rsnd_src_set_convert_rate,
59862306a36Sopenharmony_ci			       &src->sync, 192000);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	return ret;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
60462306a36Sopenharmony_cistatic void rsnd_src_debug_info(struct seq_file *m,
60562306a36Sopenharmony_ci				struct rsnd_dai_stream *io,
60662306a36Sopenharmony_ci				struct rsnd_mod *mod)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
60962306a36Sopenharmony_ci				  rsnd_mod_id(mod) * 0x20, 0x20);
61062306a36Sopenharmony_ci	seq_puts(m, "\n");
61162306a36Sopenharmony_ci	rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
61262306a36Sopenharmony_ci				  0x1c0, 0x20);
61362306a36Sopenharmony_ci	seq_puts(m, "\n");
61462306a36Sopenharmony_ci	rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
61562306a36Sopenharmony_ci				  0x200 + rsnd_mod_id(mod) * 0x40, 0x40);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci#define DEBUG_INFO .debug_info = rsnd_src_debug_info
61862306a36Sopenharmony_ci#else
61962306a36Sopenharmony_ci#define DEBUG_INFO
62062306a36Sopenharmony_ci#endif
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic struct rsnd_mod_ops rsnd_src_ops = {
62362306a36Sopenharmony_ci	.name		= SRC_NAME,
62462306a36Sopenharmony_ci	.dma_req	= rsnd_src_dma_req,
62562306a36Sopenharmony_ci	.probe		= rsnd_src_probe_,
62662306a36Sopenharmony_ci	.init		= rsnd_src_init,
62762306a36Sopenharmony_ci	.quit		= rsnd_src_quit,
62862306a36Sopenharmony_ci	.start		= rsnd_src_start,
62962306a36Sopenharmony_ci	.stop		= rsnd_src_stop,
63062306a36Sopenharmony_ci	.irq		= rsnd_src_irq,
63162306a36Sopenharmony_ci	.pcm_new	= rsnd_src_pcm_new,
63262306a36Sopenharmony_ci	.get_status	= rsnd_mod_get_status,
63362306a36Sopenharmony_ci	DEBUG_INFO
63462306a36Sopenharmony_ci};
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistruct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
63962306a36Sopenharmony_ci		id = 0;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return rsnd_mod_get(rsnd_src_get(priv, id));
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ciint rsnd_src_probe(struct rsnd_priv *priv)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct device_node *node;
64762306a36Sopenharmony_ci	struct device_node *np;
64862306a36Sopenharmony_ci	struct device *dev = rsnd_priv_to_dev(priv);
64962306a36Sopenharmony_ci	struct rsnd_src *src;
65062306a36Sopenharmony_ci	struct clk *clk;
65162306a36Sopenharmony_ci	char name[RSND_SRC_NAME_SIZE];
65262306a36Sopenharmony_ci	int i, nr, ret;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	/* This driver doesn't support Gen1 at this point */
65562306a36Sopenharmony_ci	if (rsnd_is_gen1(priv))
65662306a36Sopenharmony_ci		return 0;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	node = rsnd_src_of_node(priv);
65962306a36Sopenharmony_ci	if (!node)
66062306a36Sopenharmony_ci		return 0; /* not used is not error */
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	nr = rsnd_node_count(priv, node, SRC_NAME);
66362306a36Sopenharmony_ci	if (!nr) {
66462306a36Sopenharmony_ci		ret = -EINVAL;
66562306a36Sopenharmony_ci		goto rsnd_src_probe_done;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	src	= devm_kcalloc(dev, nr, sizeof(*src), GFP_KERNEL);
66962306a36Sopenharmony_ci	if (!src) {
67062306a36Sopenharmony_ci		ret = -ENOMEM;
67162306a36Sopenharmony_ci		goto rsnd_src_probe_done;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	priv->src_nr	= nr;
67562306a36Sopenharmony_ci	priv->src	= src;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	i = 0;
67862306a36Sopenharmony_ci	for_each_child_of_node(node, np) {
67962306a36Sopenharmony_ci		if (!of_device_is_available(np))
68062306a36Sopenharmony_ci			goto skip;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		i = rsnd_node_fixed_index(dev, np, SRC_NAME, i);
68362306a36Sopenharmony_ci		if (i < 0) {
68462306a36Sopenharmony_ci			ret = -EINVAL;
68562306a36Sopenharmony_ci			of_node_put(np);
68662306a36Sopenharmony_ci			goto rsnd_src_probe_done;
68762306a36Sopenharmony_ci		}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		src = rsnd_src_get(priv, i);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci		snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
69262306a36Sopenharmony_ci			 SRC_NAME, i);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		src->irq = irq_of_parse_and_map(np, 0);
69562306a36Sopenharmony_ci		if (!src->irq) {
69662306a36Sopenharmony_ci			ret = -EINVAL;
69762306a36Sopenharmony_ci			of_node_put(np);
69862306a36Sopenharmony_ci			goto rsnd_src_probe_done;
69962306a36Sopenharmony_ci		}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		clk = devm_clk_get(dev, name);
70262306a36Sopenharmony_ci		if (IS_ERR(clk)) {
70362306a36Sopenharmony_ci			ret = PTR_ERR(clk);
70462306a36Sopenharmony_ci			of_node_put(np);
70562306a36Sopenharmony_ci			goto rsnd_src_probe_done;
70662306a36Sopenharmony_ci		}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci		ret = rsnd_mod_init(priv, rsnd_mod_get(src),
70962306a36Sopenharmony_ci				    &rsnd_src_ops, clk, RSND_MOD_SRC, i);
71062306a36Sopenharmony_ci		if (ret) {
71162306a36Sopenharmony_ci			of_node_put(np);
71262306a36Sopenharmony_ci			goto rsnd_src_probe_done;
71362306a36Sopenharmony_ci		}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ciskip:
71662306a36Sopenharmony_ci		i++;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	ret = 0;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cirsnd_src_probe_done:
72262306a36Sopenharmony_ci	of_node_put(node);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	return ret;
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_civoid rsnd_src_remove(struct rsnd_priv *priv)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	struct rsnd_src *src;
73062306a36Sopenharmony_ci	int i;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	for_each_rsnd_src(src, priv, i) {
73362306a36Sopenharmony_ci		rsnd_mod_quit(rsnd_mod_get(src));
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci}
736