File Index
Files and directories
Sign in
FILE

ssiu.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/renesas/rcar/ssiu.c C
  1// SPDX-License-Identifier: GPL-2.0
  2//
  3// Renesas R-Car SSIU support
  4//
  5// Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  6
  7#include "rsnd.h"
  8
  9#define SSIU_NAME "ssiu"
 10
 11struct rsnd_ssiu {
 12	struct rsnd_mod mod;
 13	u32 busif_status[8]; /* for BUSIF0 - BUSIF7 */
 14	unsigned int usrcnt;
 15	int id;
 16	int id_sub;
 17};
 18
 19/* SSI_MODE */
 20#define TDM_EXT		(1 << 0)
 21#define TDM_SPLIT	(1 << 8)
 22
 23#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
 24#define rsnd_mod_to_ssiu(_mod) container_of((_mod), struct rsnd_ssiu, mod)
 25#define for_each_rsnd_ssiu(pos, priv, i)				\
 26	for (i = 0;							\
 27	     (i < rsnd_ssiu_nr(priv)) &&				\
 28		     ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i));	\
 29	     i++)
 30
 31/*
 32 *	SSI	Gen2		Gen3		Gen4
 33 *	0	BUSIF0-3	BUSIF0-7	BUSIF0-7
 34 *	1	BUSIF0-3	BUSIF0-7
 35 *	2	BUSIF0-3	BUSIF0-7
 36 *	3	BUSIF0		BUSIF0-7
 37 *	4	BUSIF0		BUSIF0-7
 38 *	5	BUSIF0		BUSIF0
 39 *	6	BUSIF0		BUSIF0
 40 *	7	BUSIF0		BUSIF0
 41 *	8	BUSIF0		BUSIF0
 42 *	9	BUSIF0-3	BUSIF0-7
 43 *	total	22		52		8
 44 */
 45static const int gen2_id[] = { 0, 4,  8, 12, 13, 14, 15, 16, 17, 18 };
 46static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 };
 47static const int gen4_id[] = { 0 };
 48
 49/* enable busif buffer over/under run interrupt. */
 50#define rsnd_ssiu_busif_err_irq_enable(mod)  rsnd_ssiu_busif_err_irq_ctrl(mod, 1)
 51#define rsnd_ssiu_busif_err_irq_disable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 0)
 52static void rsnd_ssiu_busif_err_irq_ctrl(struct rsnd_mod *mod, int enable)
 53{
 54	int id = rsnd_mod_id(mod);
 55	int shift, offset;
 56	int i;
 57
 58	switch (id) {
 59	case 0:
 60	case 1:
 61	case 2:
 62	case 3:
 63	case 4:
 64		shift  = id;
 65		offset = 0;
 66		break;
 67	case 9:
 68		shift  = 1;
 69		offset = 1;
 70		break;
 71	default:
 72		return;
 73	}
 74
 75	for (i = 0; i < 4; i++) {
 76		enum rsnd_reg reg = SSI_SYS_INT_ENABLE((i * 2) + offset);
 77		u32 val = 0xf << (shift * 4);
 78		u32 sys_int_enable = rsnd_mod_read(mod, reg);
 79
 80		if (enable)
 81			sys_int_enable |= val;
 82		else
 83			sys_int_enable &= ~val;
 84		rsnd_mod_write(mod, reg, sys_int_enable);
 85	}
 86}
 87
 88bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod)
 89{
 90	bool error = false;
 91	int id = rsnd_mod_id(mod);
 92	int shift, offset;
 93	int i;
 94
 95	switch (id) {
 96	case 0:
 97	case 1:
 98	case 2:
 99	case 3:
100	case 4:
101		shift  = id;
102		offset = 0;
103		break;
104	case 9:
105		shift  = 1;
106		offset = 1;
107		break;
108	default:
109		goto out;
110	}
111
112	for (i = 0; i < 4; i++) {
113		u32 reg = SSI_SYS_STATUS(i * 2) + offset;
114		u32 status = rsnd_mod_read(mod, reg);
115		u32 val = 0xf << (shift * 4);
116
117		status &= val;
118		if (status) {
119			struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
120			struct device *dev = rsnd_priv_to_dev(priv);
121
122			rsnd_print_irq_status(dev, "%s err status : 0x%08x\n",
123					      rsnd_mod_name(mod), status);
124			error = true;
125		}
126		rsnd_mod_write(mod, reg, val);
127	}
128out:
129	return error;
130}
131
132static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod,
133				 struct rsnd_dai_stream *io,
134				 enum rsnd_mod_type type)
135{
136	struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
137	int busif = rsnd_mod_id_sub(mod);
138
139	return &ssiu->busif_status[busif];
140}
141
142static int rsnd_ssiu_init(struct rsnd_mod *mod,
143			  struct rsnd_dai_stream *io,
144			  struct rsnd_priv *priv)
145{
146	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
147	u32 ssis = rsnd_ssi_multi_secondaries_runtime(io);
148	int use_busif = rsnd_ssi_use_busif(io);
149	int id = rsnd_mod_id(mod);
150	int is_clk_master = rsnd_rdai_is_clk_master(rdai);
151	u32 val1, val2;
152
153	/* clear status */
154	rsnd_ssiu_busif_err_status_clear(mod);
155
156	/* Gen4 doesn't have SSI_MODE */
157	if (rsnd_is_gen4(priv))
158		goto ssi_mode_setting_end;
159
160	/*
161	 * SSI_MODE0
162	 */
163	rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
164
165	/*
166	 * SSI_MODE1 / SSI_MODE2
167	 *
168	 * FIXME
169	 * sharing/multi with SSI0 are mainly supported
170	 */
171	val1 = rsnd_mod_read(mod, SSI_MODE1);
172	val2 = rsnd_mod_read(mod, SSI_MODE2);
173	if (rsnd_ssi_is_pin_sharing(io)) {
174
175		ssis |= (1 << id);
176
177	} else if (ssis) {
178		/*
179		 * Multi SSI
180		 *
181		 * set synchronized bit here
182		 */
183
184		/* SSI4 is synchronized with SSI3 */
185		if (ssis & (1 << 4))
186			val1 |= (1 << 20);
187		/* SSI012 are synchronized */
188		if (ssis == 0x0006)
189			val1 |= (1 << 4);
190		/* SSI0129 are synchronized */
191		if (ssis == 0x0206)
192			val2 |= (1 << 4);
193	}
194
195	/* SSI1 is sharing pin with SSI0 */
196	if (ssis & (1 << 1))
197		val1 |= is_clk_master ? 0x2 : 0x1;
198
199	/* SSI2 is sharing pin with SSI0 */
200	if (ssis & (1 << 2))
201		val1 |= is_clk_master ?	0x2 << 2 :
202					0x1 << 2;
203	/* SSI4 is sharing pin with SSI3 */
204	if (ssis & (1 << 4))
205		val1 |= is_clk_master ? 0x2 << 16 :
206					0x1 << 16;
207	/* SSI9 is sharing pin with SSI0 */
208	if (ssis & (1 << 9))
209		val2 |= is_clk_master ? 0x2 : 0x1;
210
211	rsnd_mod_bset(mod, SSI_MODE1, 0x0013001f, val1);
212	rsnd_mod_bset(mod, SSI_MODE2, 0x00000017, val2);
213
214ssi_mode_setting_end:
215	/*
216	 * Enable busif buffer over/under run interrupt.
217	 * It will be handled from ssi.c
218	 * see
219	 *	__rsnd_ssi_interrupt()
220	 */
221	rsnd_ssiu_busif_err_irq_enable(mod);
222
223	return 0;
224}
225
226static int rsnd_ssiu_quit(struct rsnd_mod *mod,
227			  struct rsnd_dai_stream *io,
228			  struct rsnd_priv *priv)
229{
230	/* disable busif buffer over/under run interrupt. */
231	rsnd_ssiu_busif_err_irq_disable(mod);
232
233	return 0;
234}
235
236static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = {
237	.name		= SSIU_NAME,
238	.init		= rsnd_ssiu_init,
239	.quit		= rsnd_ssiu_quit,
240	.get_status	= rsnd_ssiu_get_status,
241};
242
243static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
244			       struct rsnd_dai_stream *io,
245			       struct rsnd_priv *priv)
246{
247	struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
248	u32 has_hdmi0 = rsnd_flags_has(io, RSND_STREAM_HDMI0);
249	u32 has_hdmi1 = rsnd_flags_has(io, RSND_STREAM_HDMI1);
250	int ret;
251	u32 mode = 0;
252
253	ret = rsnd_ssiu_init(mod, io, priv);
254	if (ret < 0)
255		return ret;
256
257	ssiu->usrcnt++;
258
259	/*
260	 * TDM Extend/Split Mode
261	 * see
262	 *	rsnd_ssi_config_init()
263	 */
264	if (rsnd_runtime_is_tdm(io))
265		mode = TDM_EXT;
266	else if (rsnd_runtime_is_tdm_split(io))
267		mode = TDM_SPLIT;
268
269	rsnd_mod_write(mod, SSI_MODE, mode);
270
271	if (rsnd_ssi_use_busif(io)) {
272		int id = rsnd_mod_id(mod);
273		int busif = rsnd_mod_id_sub(mod);
274		enum rsnd_reg adinr_reg, mode_reg, dalign_reg;
275
276		if ((id == 9) && (busif >= 4)) {
277			adinr_reg = SSI9_BUSIF_ADINR(busif);
278			mode_reg = SSI9_BUSIF_MODE(busif);
279			dalign_reg = SSI9_BUSIF_DALIGN(busif);
280		} else {
281			adinr_reg = SSI_BUSIF_ADINR(busif);
282			mode_reg = SSI_BUSIF_MODE(busif);
283			dalign_reg = SSI_BUSIF_DALIGN(busif);
284		}
285
286		rsnd_mod_write(mod, adinr_reg,
287			       rsnd_get_adinr_bit(mod, io) |
288			       (rsnd_io_is_play(io) ?
289				rsnd_runtime_channel_after_ctu(io) :
290				rsnd_runtime_channel_original(io)));
291		rsnd_mod_write(mod, mode_reg,
292			       rsnd_get_busif_shift(io, mod) | 1);
293		rsnd_mod_write(mod, dalign_reg,
294			       rsnd_get_dalign(mod, io));
295	}
296
297	if (has_hdmi0 || has_hdmi1) {
298		enum rsnd_mod_type rsnd_ssi_array[] = {
299			RSND_MOD_SSIM1,
300			RSND_MOD_SSIM2,
301			RSND_MOD_SSIM3,
302		};
303		struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
304		struct rsnd_mod *pos;
305		u32 val;
306		int i;
307
308		i = rsnd_mod_id(ssi_mod);
309
310		/* output all same SSI as default */
311		val =	i << 16 |
312			i << 20 |
313			i << 24 |
314			i << 28 |
315			i;
316
317		for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) {
318			int shift = (i * 4) + 20;
319
320			val	= (val & ~(0xF << shift)) |
321				rsnd_mod_id(pos) << shift;
322		}
323
324		if (has_hdmi0)
325			rsnd_mod_write(mod, HDMI0_SEL, val);
326		if (has_hdmi1)
327			rsnd_mod_write(mod, HDMI1_SEL, val);
328	}
329
330	return 0;
331}
332
333static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
334				struct rsnd_dai_stream *io,
335				struct rsnd_priv *priv)
336{
337	int busif = rsnd_mod_id_sub(mod);
338
339	if (!rsnd_ssi_use_busif(io))
340		return 0;
341
342	rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 1 << (busif * 4));
343
344	if (rsnd_ssi_multi_secondaries_runtime(io))
345		rsnd_mod_write(mod, SSI_CONTROL, 0x1);
346
347	return 0;
348}
349
350static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
351			       struct rsnd_dai_stream *io,
352			       struct rsnd_priv *priv)
353{
354	struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
355	int busif = rsnd_mod_id_sub(mod);
356
357	if (!rsnd_ssi_use_busif(io))
358		return 0;
359
360	rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 0);
361
362	if (--ssiu->usrcnt)
363		return 0;
364
365	if (rsnd_ssi_multi_secondaries_runtime(io))
366		rsnd_mod_write(mod, SSI_CONTROL, 0);
367
368	return 0;
369}
370
371static int rsnd_ssiu_id(struct rsnd_mod *mod)
372{
373	struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
374
375	/* see rsnd_ssiu_probe() */
376	return ssiu->id;
377}
378
379static int rsnd_ssiu_id_sub(struct rsnd_mod *mod)
380{
381	struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
382
383	/* see rsnd_ssiu_probe() */
384	return ssiu->id_sub;
385}
386
387static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io,
388					  struct rsnd_mod *mod)
389{
390	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
391	int is_play = rsnd_io_is_play(io);
392	char *name;
393
394	/*
395	 * It should use "rcar_sound,ssiu" on DT.
396	 * But, we need to keep compatibility for old version.
397	 *
398	 * If it has "rcar_sound.ssiu", it will be used.
399	 * If not, "rcar_sound.ssi" will be used.
400	 * see
401	 *	rsnd_ssi_dma_req()
402	 *	rsnd_dma_of_path()
403	 */
404
405	name = is_play ? "rx" : "tx";
406
407	return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv),
408					SSIU_NAME, mod, name);
409}
410
411#ifdef CONFIG_DEBUG_FS
412static void rsnd_ssiu_debug_info(struct seq_file *m,
413				 struct rsnd_dai_stream *io,
414				struct rsnd_mod *mod)
415{
416	rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SSIU,
417				  rsnd_mod_id(mod) * 0x80, 0x80);
418}
419#define DEBUG_INFO .debug_info = rsnd_ssiu_debug_info
420#else
421#define DEBUG_INFO
422#endif
423
424static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
425	.name		= SSIU_NAME,
426	.dma_req	= rsnd_ssiu_dma_req,
427	.init		= rsnd_ssiu_init_gen2,
428	.quit		= rsnd_ssiu_quit,
429	.start		= rsnd_ssiu_start_gen2,
430	.stop		= rsnd_ssiu_stop_gen2,
431	.get_status	= rsnd_ssiu_get_status,
432	DEBUG_INFO
433};
434
435static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
436{
437	if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv)))
438		id = 0;
439
440	return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
441}
442
443static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv,
444					       struct rsnd_dai_stream *io)
445{
446	struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
447	struct rsnd_ssiu *ssiu;
448	int is_dma_mode;
449	int i;
450
451	if (!ssi_mod)
452		return;
453
454	is_dma_mode = rsnd_ssi_is_dma_mode(ssi_mod);
455
456	/* select BUSIF0 */
457	for_each_rsnd_ssiu(ssiu, priv, i) {
458		struct rsnd_mod *mod = rsnd_mod_get(ssiu);
459
460		if (is_dma_mode &&
461		    (rsnd_mod_id(ssi_mod) == rsnd_mod_id(mod)) &&
462		    (rsnd_mod_id_sub(mod) == 0)) {
463			rsnd_dai_connect(mod, io, mod->type);
464			return;
465		}
466	}
467}
468
469void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
470			     struct device_node *playback,
471			     struct device_node *capture)
472{
473	struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
474	struct device *dev = rsnd_priv_to_dev(priv);
475	struct device_node *node = rsnd_ssiu_of_node(priv);
476	struct rsnd_dai_stream *io_p = &rdai->playback;
477	struct rsnd_dai_stream *io_c = &rdai->capture;
478
479	/* use rcar_sound,ssiu if exist */
480	if (node) {
481		int i = 0;
482
483		for_each_child_of_node_scoped(node, np) {
484			struct rsnd_mod *mod;
485
486			i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i);
487			if (i < 0)
488				break;
489
490			mod = rsnd_ssiu_mod_get(priv, i);
491
492			if (np == playback)
493				rsnd_dai_connect(mod, io_p, mod->type);
494			if (np == capture)
495				rsnd_dai_connect(mod, io_c, mod->type);
496			i++;
497		}
498
499		of_node_put(node);
500	}
501
502	/* Keep DT compatibility */
503	if (!rsnd_io_to_mod_ssiu(io_p))
504		rsnd_parse_connect_ssiu_compatible(priv, io_p);
505	if (!rsnd_io_to_mod_ssiu(io_c))
506		rsnd_parse_connect_ssiu_compatible(priv, io_c);
507}
508
509int rsnd_ssiu_probe(struct rsnd_priv *priv)
510{
511	struct device *dev = rsnd_priv_to_dev(priv);
512	struct device_node *node __free(device_node) = rsnd_ssiu_of_node(priv);
513	struct rsnd_ssiu *ssiu;
514	struct rsnd_mod_ops *ops;
515	const int *list = NULL;
516	int i, nr;
517
518	/*
519	 * Keep DT compatibility.
520	 * if it has "rcar_sound,ssiu", use it.
521	 * if not, use "rcar_sound,ssi"
522	 * see
523	 *	rsnd_ssiu_bufsif_to_id()
524	 */
525	if (node)
526		nr = rsnd_node_count(priv, node, SSIU_NAME);
527	else
528		nr = priv->ssi_nr;
529
530	if (!nr)
531		return -EINVAL;
532
533	ssiu	= devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL);
534	if (!ssiu)
535		return -ENOMEM;
536
537	priv->ssiu	= ssiu;
538	priv->ssiu_nr	= nr;
539
540	if (rsnd_is_gen1(priv))
541		ops = &rsnd_ssiu_ops_gen1;
542	else
543		ops = &rsnd_ssiu_ops_gen2;
544
545	/* Keep compatibility */
546	nr = 0;
547	if ((node) &&
548	    (ops == &rsnd_ssiu_ops_gen2)) {
549		ops->id		= rsnd_ssiu_id;
550		ops->id_sub	= rsnd_ssiu_id_sub;
551
552		if (rsnd_is_gen2(priv)) {
553			list	= gen2_id;
554			nr	= ARRAY_SIZE(gen2_id);
555		} else if (rsnd_is_gen3(priv)) {
556			list	= gen3_id;
557			nr	= ARRAY_SIZE(gen3_id);
558		} else if (rsnd_is_gen4(priv)) {
559			list	= gen4_id;
560			nr	= ARRAY_SIZE(gen4_id);
561		} else {
562			dev_err(dev, "unknown SSIU\n");
563			return -ENODEV;
564		}
565	}
566
567	for_each_rsnd_ssiu(ssiu, priv, i) {
568		int ret;
569
570		if (node) {
571			int j;
572
573			/*
574			 * see
575			 *	rsnd_ssiu_get_id()
576			 *	rsnd_ssiu_get_id_sub()
577			 */
578			for (j = 0; j < nr; j++) {
579				if (list[j] > i)
580					break;
581				ssiu->id	= j;
582				ssiu->id_sub	= i - list[ssiu->id];
583			}
584		} else {
585			ssiu->id = i;
586		}
587
588		ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
589				    ops, NULL, RSND_MOD_SSIU, i);
590		if (ret)
591			return ret;
592	}
593
594	return 0;
595}
596
597void rsnd_ssiu_remove(struct rsnd_priv *priv)
598{
599	struct rsnd_ssiu *ssiu;
600	int i;
601
602	for_each_rsnd_ssiu(ssiu, priv, i) {
603		rsnd_mod_quit(rsnd_mod_get(ssiu));
604	}
605}
~/ Read-only file preview.