aboutsummaryrefslogtreecommitdiffstats
path: root/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0061-ASoC-R-Car-add-tdm16-support-enable-tdm-for-ssi78.patch
blob: 0a6e504af1b036953ec263e7fabceabe0f48df23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
From 7e4907c9f824c1b52b4c2cf8be91d62c75839e39 Mon Sep 17 00:00:00 2001
From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
Date: Thu, 24 Nov 2016 15:50:25 +0300
Subject: [PATCH] ASoC: R-Car: add tdm16 support, enable tdm for ssi78

Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
---
 sound/soc/sh/rcar/adg.c  | 13 ++++------
 sound/soc/sh/rcar/core.c | 19 ++++++++++----
 sound/soc/sh/rcar/gen.c  |  4 +++
 sound/soc/sh/rcar/rsnd.h |  3 +++
 sound/soc/sh/rcar/ssi.c  | 66 +++++++++++++++++++++++++++++++++++++++++-------
 sound/soc/sh/rcar/ssiu.c | 11 +++++---
 6 files changed, 91 insertions(+), 25 deletions(-)

diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 85a33ac..a73b45c 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -46,15 +46,12 @@ struct rsnd_adg {
 #define adg_mode_flags(adg)	(adg->flags)
 
 #define for_each_rsnd_clk(pos, adg, i)		\
-	for (i = 0;				\
-	     (i < CLKMAX) &&			\
-	     ((pos) = adg->clk[i]);		\
-	     i++)
+	for (i = 0; i < CLKMAX; i++)		\
+		if (((pos) = adg->clk[i]))	\
+
 #define for_each_rsnd_clkout(pos, adg, i)	\
-	for (i = 0;				\
-	     (i < CLKOUTMAX) &&			\
-	     ((pos) = adg->clkout[i]);	\
-	     i++)
+	for (i = 0; i < CLKOUTMAX; i++)		\
+		if (((pos) = adg->clkout[i]))
 #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
 
 static u32 rsnd_adg_calculate_rbgx(unsigned long div)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 448072c..75e992f 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -277,7 +277,10 @@ int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io)
 
 int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io)
 {
-	return rsnd_runtime_channel_for_ssi(io) >= 6;
+	if (rsnd_runtime_channel_for_ssi(io) < 6)
+		return 0;
+	else
+		return rsnd_runtime_channel_for_ssi(io);
 }
 
 /*
@@ -322,8 +325,10 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
 		target = cmd ? cmd : ssiu;
 	}
 
-	mask <<= runtime->channels * 4;
-	val = val & mask;
+	if (runtime->channels < 8) {
+		mask <<= runtime->channels * 4;
+		val = val & mask;
+	}
 
 	switch (runtime->sample_bits) {
 	case 16:
@@ -680,6 +685,10 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
 	switch (slots) {
 	case 6:
 		/* TDM Extend Mode */
+	case 8:
+		/* TDM Mode */
+	case 16:
+		/* TDM16 Mode */
 		rsnd_set_slot(rdai, slots, 1);
 		break;
 	default:
@@ -775,7 +784,7 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
 		drv->playback.rates		= RSND_RATES;
 		drv->playback.formats		= RSND_FMTS;
 		drv->playback.channels_min	= 2;
-		drv->playback.channels_max	= 6;
+		drv->playback.channels_max	= 16;
 		drv->playback.stream_name	= rdai->playback.name;
 
 		snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
@@ -783,7 +792,7 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
 		drv->capture.rates		= RSND_RATES;
 		drv->capture.formats		= RSND_FMTS;
 		drv->capture.channels_min	= 2;
-		drv->capture.channels_max	= 6;
+		drv->capture.channels_max	= 16;
 		drv->capture.stream_name	= rdai->capture.name;
 
 		rdai->playback.rdai		= rdai;
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index e785fe94..fce2a7b 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -334,6 +334,9 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
 		RSND_GEN_M_REG(SSITDR,		0x08,	0x40),
 		RSND_GEN_M_REG(SSIRDR,		0x0c,	0x40),
 		RSND_GEN_M_REG(SSIWSR,		0x20,	0x40),
+		RSND_GEN_M_REG(SSIFMR,		0x24,	0x40),
+		RSND_GEN_M_REG(SSIFSR,		0x28,	0x40),
+		RSND_GEN_M_REG(SSICRE,		0x30,	0x40),
 	};
 	int ret_ssiu;
 	int ret_scu;
@@ -407,6 +410,7 @@ int rsnd_gen_probe(struct rsnd_priv *priv)
 		ret = rsnd_gen1_probe(priv);
 	else if (rsnd_is_gen2(priv))
 		ret = rsnd_gen2_probe(priv);
+	/* TODO: add gen3 */
 
 	if (ret < 0)
 		dev_err(dev, "unknown generation R-Car sound device\n");
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index bf9596b..8ccd9d0 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -166,6 +166,9 @@ enum rsnd_reg {
 	RSND_REG_SSITDR,
 	RSND_REG_SSIRDR,
 	RSND_REG_SSIWSR,
+	RSND_REG_SSIFMR,
+	RSND_REG_SSIFSR,
+	RSND_REG_SSICRE,
 
 	RSND_REG_MAX,
 };
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 630cb6b..62abb58 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -35,7 +35,13 @@
 #define	DWL_24		(5 << 19)	/* Data Word Length */
 #define	DWL_32		(6 << 19)	/* Data Word Length */
 
-#define	SWL_32		(3 << 16)	/* R/W System Word Length */
+#define	SWL_16		(1 << 16)	/* R/W System Word Length 16 bit */
+#define	SWL_24		(2 << 16)	/* R/W System Word Length 24 bit */
+#define	SWL_32		(3 << 16)	/* R/W System Word Length 32 bit */
+#define	SWL_48		(4 << 16)	/* R/W System Word Length 48 bit */
+#define	SWL_64		(5 << 16)	/* R/W System Word Length 64 bit */
+#define	SWL_128		(6 << 16)	/* R/W System Word Length 128 bit */
+#define	SWL_256		(7 << 16)	/* R/W System Word Length 256 bit */
 #define	SCKD		(1 << 15)	/* Serial Bit Clock Direction */
 #define	SWSD		(1 << 14)	/* Serial WS Direction */
 #define	SCKP		(1 << 13)	/* Serial Bit Clock Polarity */
@@ -61,6 +67,11 @@
 #define CONT		(1 << 8)	/* WS Continue Function */
 #define WS_MODE		(1 << 0)	/* WS Mode */
 
+/*
+ * SSICRE
+ */
+#define	CHNL_16		(1 << 0)	/* Channels */
+
 #define SSI_NAME "ssi"
 
 struct rsnd_ssi {
@@ -71,6 +82,7 @@ struct rsnd_ssi {
 	u32 cr_own;
 	u32 cr_clk;
 	u32 cr_mode;
+	u32 cre_own;
 	u32 wsr;
 	int chan;
 	int rate;
@@ -224,8 +236,12 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
 
 	/*
 	 * Find best clock, and try to start ADG
+	 *
+	 * Start with j = 1 because:
+	 * "CKDV = 000 is invalid when WS_MODE = 1 or CONT = 1 in the WS
+	 * Mode Register."
 	 */
-	for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+	for (j = 1; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
 
 		/*
 		 * It will set SSIWSR.CONT here, but SSICR.CKDV = 000
@@ -239,15 +255,23 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
 		/*
 		 * this driver is assuming that
 		 * system word is 32bit x chan
-		 * see rsnd_ssi_init()
+		 *
+		 * Expect:
+		 * TDM 16ch mode where SWL should be 16 bit
 		 */
-		main_rate = rate * 32 * chan * ssi_clk_mul_table[j];
+		if (chan != 16)
+			main_rate = rate * 32 * chan * ssi_clk_mul_table[j];
+		else
+			main_rate = rate * 16 * chan * ssi_clk_mul_table[j];
 
 		ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
 		if (0 == ret) {
-			ssi->cr_clk	= FORCE | SWL_32 |
+			ssi->cr_clk	= FORCE |
 				SCKD | SWSD | CKDV(j);
-			ssi->wsr = CONT;
+			if (chan != 16)
+				ssi->cr_clk |= SWL_32;
+			else
+				ssi->cr_clk |= SWL_16;
 
 			ssi->rate = rate;
 
@@ -288,10 +312,13 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
 static void rsnd_ssi_config_init(struct rsnd_mod *mod,
 				struct rsnd_dai_stream *io)
 {
+	struct rsnd_priv *priv = rsnd_io_to_priv(io);
+	struct device *dev = rsnd_priv_to_dev(priv);
 	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
 	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 	u32 cr_own;
+	u32 cre_own;
 	u32 cr_mode;
 	u32 wsr;
 	int is_tdm;
@@ -301,12 +328,24 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod,
 	/*
 	 * always use 32bit system word.
 	 * see also rsnd_ssi_master_clk_enable()
+	 * NOPE!
 	 */
-	cr_own = FORCE | SWL_32 | PDTA;
+	cr_own = FORCE | PDTA;
+	cre_own = 0;
+
+	/*
+	 * TDM16 mode can handle only 16bit data
+	 */
+	if (rsnd_runtime_channel_for_ssi(io) != 16)
+		cr_own |= SWL_32;
+	else
+		cr_own |= SWL_16;
+
+	cre_own = 0;
 
 	if (rdai->bit_clk_inv)
 		cr_own |= SCKP;
-	if (rdai->frm_clk_inv ^ is_tdm)
+	if (rdai->frm_clk_inv ^ (!!is_tdm))
 		cr_own |= SWSP;
 	if (rdai->data_alignment)
 		cr_own |= SDTA;
@@ -337,12 +376,20 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod,
 	 *	rsnd_ssiu_init_gen2()
 	 */
 	wsr = ssi->wsr;
-	if (is_tdm) {
+	if (is_tdm == 8) {
 		wsr	|= WS_MODE;
 		cr_own	|= CHNL_8;
+	} else if (is_tdm == 16) {
+		wsr	|= WS_MODE;
+		cre_own	|= CHNL_16;
+	} else if (is_tdm) {
+		dev_err(dev, "%s[%d] invalid tdm channels %d\n",
+			rsnd_mod_name(mod),
+			rsnd_mod_id(mod), is_tdm);
 	}
 
 	ssi->cr_own	= cr_own;
+	ssi->cre_own	= cre_own;
 	ssi->cr_mode	= cr_mode;
 	ssi->wsr	= wsr;
 }
@@ -352,6 +399,7 @@ static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 
 	rsnd_mod_write(mod, SSIWSR,	ssi->wsr);
+	rsnd_mod_write(mod, SSICRE,	ssi->cre_own);
 	rsnd_mod_write(mod, SSICR,	ssi->cr_own	|
 					ssi->cr_clk	|
 					ssi->cr_mode); /* without EN */
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
index 3f95d6b..19f5f5e 100644
--- a/sound/soc/sh/rcar/ssiu.c
+++ b/sound/soc/sh/rcar/ssiu.c
@@ -61,13 +61,18 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
 		case 4:
 			shift = 16;
 			break;
+		case 8:
+			/* ignore? */
+			break;
 		default:
 			return -EINVAL;
 		}
 
-		mask1 |= 0x3 << shift;
-		val1 = rsnd_rdai_is_clk_master(rdai) ?
-			0x2 << shift : 0x1 << shift;
+		if (shift >= 0) {
+			mask1 |= 0x3 << shift;
+			val1 = rsnd_rdai_is_clk_master(rdai) ?
+				0x2 << shift : 0x1 << shift;
+		}
 
 	} else if (multi_ssi_slaves) {
 
-- 
1.9.1