xref: /openbmc/linux/sound/soc/sh/rcar/ssiu.c (revision c2bc6527)
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 
11 struct 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  */
45 static const int gen2_id[] = { 0, 4,  8, 12, 13, 14, 15, 16, 17, 18 };
46 static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 };
47 static 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)
rsnd_ssiu_busif_err_irq_ctrl(struct rsnd_mod * mod,int enable)52 static 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 
rsnd_ssiu_busif_err_status_clear(struct rsnd_mod * mod)88 bool 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 	}
128 out:
129 	return error;
130 }
131 
rsnd_ssiu_get_status(struct rsnd_mod * mod,struct rsnd_dai_stream * io,enum rsnd_mod_type type)132 static 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 
rsnd_ssiu_init(struct rsnd_mod * mod,struct rsnd_dai_stream * io,struct rsnd_priv * priv)142 static 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 
214 ssi_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 
rsnd_ssiu_quit(struct rsnd_mod * mod,struct rsnd_dai_stream * io,struct rsnd_priv * priv)226 static 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 
236 static 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 
rsnd_ssiu_init_gen2(struct rsnd_mod * mod,struct rsnd_dai_stream * io,struct rsnd_priv * priv)243 static 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 
rsnd_ssiu_start_gen2(struct rsnd_mod * mod,struct rsnd_dai_stream * io,struct rsnd_priv * priv)333 static 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 
rsnd_ssiu_stop_gen2(struct rsnd_mod * mod,struct rsnd_dai_stream * io,struct rsnd_priv * priv)350 static 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 
rsnd_ssiu_id(struct rsnd_mod * mod)371 static 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 
rsnd_ssiu_id_sub(struct rsnd_mod * mod)379 static 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 
rsnd_ssiu_dma_req(struct rsnd_dai_stream * io,struct rsnd_mod * mod)387 static 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
rsnd_ssiu_debug_info(struct seq_file * m,struct rsnd_dai_stream * io,struct rsnd_mod * mod)412 static 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_GEN2_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 
424 static 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 
rsnd_ssiu_mod_get(struct rsnd_priv * priv,int id)435 static 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 
rsnd_parse_connect_ssiu_compatible(struct rsnd_priv * priv,struct rsnd_dai_stream * io)443 static 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 
rsnd_parse_connect_ssiu(struct rsnd_dai * rdai,struct device_node * playback,struct device_node * capture)469 void 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 		struct device_node *np;
482 		int i = 0;
483 
484 		for_each_child_of_node(node, np) {
485 			struct rsnd_mod *mod;
486 
487 			i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i);
488 			if (i < 0) {
489 				of_node_put(np);
490 				break;
491 			}
492 
493 			mod = rsnd_ssiu_mod_get(priv, i);
494 
495 			if (np == playback)
496 				rsnd_dai_connect(mod, io_p, mod->type);
497 			if (np == capture)
498 				rsnd_dai_connect(mod, io_c, mod->type);
499 			i++;
500 		}
501 
502 		of_node_put(node);
503 	}
504 
505 	/* Keep DT compatibility */
506 	if (!rsnd_io_to_mod_ssiu(io_p))
507 		rsnd_parse_connect_ssiu_compatible(priv, io_p);
508 	if (!rsnd_io_to_mod_ssiu(io_c))
509 		rsnd_parse_connect_ssiu_compatible(priv, io_c);
510 }
511 
rsnd_ssiu_probe(struct rsnd_priv * priv)512 int rsnd_ssiu_probe(struct rsnd_priv *priv)
513 {
514 	struct device *dev = rsnd_priv_to_dev(priv);
515 	struct device_node *node;
516 	struct rsnd_ssiu *ssiu;
517 	struct rsnd_mod_ops *ops;
518 	const int *list = NULL;
519 	int i, nr;
520 
521 	/*
522 	 * Keep DT compatibility.
523 	 * if it has "rcar_sound,ssiu", use it.
524 	 * if not, use "rcar_sound,ssi"
525 	 * see
526 	 *	rsnd_ssiu_bufsif_to_id()
527 	 */
528 	node = rsnd_ssiu_of_node(priv);
529 	if (node)
530 		nr = rsnd_node_count(priv, node, SSIU_NAME);
531 	else
532 		nr = priv->ssi_nr;
533 
534 	if (!nr)
535 		return -EINVAL;
536 
537 	ssiu	= devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL);
538 	if (!ssiu)
539 		return -ENOMEM;
540 
541 	priv->ssiu	= ssiu;
542 	priv->ssiu_nr	= nr;
543 
544 	if (rsnd_is_gen1(priv))
545 		ops = &rsnd_ssiu_ops_gen1;
546 	else
547 		ops = &rsnd_ssiu_ops_gen2;
548 
549 	/* Keep compatibility */
550 	nr = 0;
551 	if ((node) &&
552 	    (ops == &rsnd_ssiu_ops_gen2)) {
553 		ops->id		= rsnd_ssiu_id;
554 		ops->id_sub	= rsnd_ssiu_id_sub;
555 
556 		if (rsnd_is_gen2(priv)) {
557 			list	= gen2_id;
558 			nr	= ARRAY_SIZE(gen2_id);
559 		} else if (rsnd_is_gen3(priv)) {
560 			list	= gen3_id;
561 			nr	= ARRAY_SIZE(gen3_id);
562 		} else if (rsnd_is_gen4(priv)) {
563 			list	= gen4_id;
564 			nr	= ARRAY_SIZE(gen4_id);
565 		} else {
566 			dev_err(dev, "unknown SSIU\n");
567 			return -ENODEV;
568 		}
569 	}
570 
571 	for_each_rsnd_ssiu(ssiu, priv, i) {
572 		int ret;
573 
574 		if (node) {
575 			int j;
576 
577 			/*
578 			 * see
579 			 *	rsnd_ssiu_get_id()
580 			 *	rsnd_ssiu_get_id_sub()
581 			 */
582 			for (j = 0; j < nr; j++) {
583 				if (list[j] > i)
584 					break;
585 				ssiu->id	= j;
586 				ssiu->id_sub	= i - list[ssiu->id];
587 			}
588 		} else {
589 			ssiu->id = i;
590 		}
591 
592 		ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
593 				    ops, NULL, RSND_MOD_SSIU, i);
594 		if (ret)
595 			return ret;
596 	}
597 
598 	return 0;
599 }
600 
rsnd_ssiu_remove(struct rsnd_priv * priv)601 void rsnd_ssiu_remove(struct rsnd_priv *priv)
602 {
603 	struct rsnd_ssiu *ssiu;
604 	int i;
605 
606 	for_each_rsnd_ssiu(ssiu, priv, i) {
607 		rsnd_mod_quit(rsnd_mod_get(ssiu));
608 	}
609 }
610