xref: /linux/sound/firewire/dice/dice-stream.c (revision e7d759f31ca295d589f7420719c311870bb3166f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * dice_stream.c - a part of driver for DICE based devices
4  *
5  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
6  * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
7  */
8 
9 #include "dice.h"
10 
11 #define	READY_TIMEOUT_MS	200
12 #define NOTIFICATION_TIMEOUT_MS	100
13 
14 struct reg_params {
15 	unsigned int count;
16 	unsigned int size;
17 };
18 
19 const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
20 	/* mode 0 */
21 	[0] =  32000,
22 	[1] =  44100,
23 	[2] =  48000,
24 	/* mode 1 */
25 	[3] =  88200,
26 	[4] =  96000,
27 	/* mode 2 */
28 	[5] = 176400,
29 	[6] = 192000,
30 };
31 
32 int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
33 				  enum snd_dice_rate_mode *mode)
34 {
35 	/* Corresponding to each entry in snd_dice_rates. */
36 	static const enum snd_dice_rate_mode modes[] = {
37 		[0] = SND_DICE_RATE_MODE_LOW,
38 		[1] = SND_DICE_RATE_MODE_LOW,
39 		[2] = SND_DICE_RATE_MODE_LOW,
40 		[3] = SND_DICE_RATE_MODE_MIDDLE,
41 		[4] = SND_DICE_RATE_MODE_MIDDLE,
42 		[5] = SND_DICE_RATE_MODE_HIGH,
43 		[6] = SND_DICE_RATE_MODE_HIGH,
44 	};
45 	int i;
46 
47 	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
48 		if (!(dice->clock_caps & BIT(i)))
49 			continue;
50 		if (snd_dice_rates[i] != rate)
51 			continue;
52 
53 		*mode = modes[i];
54 		return 0;
55 	}
56 
57 	return -EINVAL;
58 }
59 
60 static int select_clock(struct snd_dice *dice, unsigned int rate)
61 {
62 	__be32 reg, new;
63 	u32 data;
64 	int i;
65 	int err;
66 
67 	err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
68 					       &reg, sizeof(reg));
69 	if (err < 0)
70 		return err;
71 
72 	data = be32_to_cpu(reg);
73 
74 	data &= ~CLOCK_RATE_MASK;
75 	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
76 		if (snd_dice_rates[i] == rate)
77 			break;
78 	}
79 	if (i == ARRAY_SIZE(snd_dice_rates))
80 		return -EINVAL;
81 	data |= i << CLOCK_RATE_SHIFT;
82 
83 	if (completion_done(&dice->clock_accepted))
84 		reinit_completion(&dice->clock_accepted);
85 
86 	new = cpu_to_be32(data);
87 	err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
88 						&new, sizeof(new));
89 	if (err < 0)
90 		return err;
91 
92 	if (wait_for_completion_timeout(&dice->clock_accepted,
93 			msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
94 		if (reg != new)
95 			return -ETIMEDOUT;
96 	}
97 
98 	return 0;
99 }
100 
101 static int get_register_params(struct snd_dice *dice,
102 			       struct reg_params *tx_params,
103 			       struct reg_params *rx_params)
104 {
105 	__be32 reg[2];
106 	int err;
107 
108 	err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
109 	if (err < 0)
110 		return err;
111 	tx_params->count =
112 			min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
113 	tx_params->size = be32_to_cpu(reg[1]) * 4;
114 
115 	err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg));
116 	if (err < 0)
117 		return err;
118 	rx_params->count =
119 			min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
120 	rx_params->size = be32_to_cpu(reg[1]) * 4;
121 
122 	return 0;
123 }
124 
125 static void release_resources(struct snd_dice *dice)
126 {
127 	int i;
128 
129 	for (i = 0; i < MAX_STREAMS; ++i) {
130 		fw_iso_resources_free(&dice->tx_resources[i]);
131 		fw_iso_resources_free(&dice->rx_resources[i]);
132 	}
133 }
134 
135 static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
136 			 struct reg_params *params)
137 {
138 	__be32 reg;
139 	unsigned int i;
140 
141 	for (i = 0; i < params->count; i++) {
142 		reg = cpu_to_be32((u32)-1);
143 		if (dir == AMDTP_IN_STREAM) {
144 			snd_dice_transaction_write_tx(dice,
145 					params->size * i + TX_ISOCHRONOUS,
146 					&reg, sizeof(reg));
147 		} else {
148 			snd_dice_transaction_write_rx(dice,
149 					params->size * i + RX_ISOCHRONOUS,
150 					&reg, sizeof(reg));
151 		}
152 	}
153 }
154 
155 static int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream,
156 			  struct fw_iso_resources *resources, unsigned int rate,
157 			  unsigned int pcm_chs, unsigned int midi_ports)
158 {
159 	bool double_pcm_frames;
160 	unsigned int i;
161 	int err;
162 
163 	// At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
164 	// one data block of AMDTP packet. Thus sampling transfer frequency is
165 	// a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
166 	// transferred on AMDTP packets at 96 kHz. Two successive samples of a
167 	// channel are stored consecutively in the packet. This quirk is called
168 	// as 'Dual Wire'.
169 	// For this quirk, blocking mode is required and PCM buffer size should
170 	// be aligned to SYT_INTERVAL.
171 	double_pcm_frames = (rate > 96000 && !dice->disable_double_pcm_frames);
172 	if (double_pcm_frames) {
173 		rate /= 2;
174 		pcm_chs *= 2;
175 	}
176 
177 	err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
178 					 double_pcm_frames);
179 	if (err < 0)
180 		return err;
181 
182 	if (double_pcm_frames) {
183 		pcm_chs /= 2;
184 
185 		for (i = 0; i < pcm_chs; i++) {
186 			amdtp_am824_set_pcm_position(stream, i, i * 2);
187 			amdtp_am824_set_pcm_position(stream, i + pcm_chs,
188 						     i * 2 + 1);
189 		}
190 	}
191 
192 	return fw_iso_resources_allocate(resources,
193 				amdtp_stream_get_max_payload(stream),
194 				fw_parent_device(dice->unit)->max_speed);
195 }
196 
197 static int keep_dual_resources(struct snd_dice *dice, unsigned int rate,
198 			       enum amdtp_stream_direction dir,
199 			       struct reg_params *params)
200 {
201 	enum snd_dice_rate_mode mode;
202 	int i;
203 	int err;
204 
205 	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
206 	if (err < 0)
207 		return err;
208 
209 	for (i = 0; i < params->count; ++i) {
210 		__be32 reg[2];
211 		struct amdtp_stream *stream;
212 		struct fw_iso_resources *resources;
213 		unsigned int pcm_cache;
214 		unsigned int pcm_chs;
215 		unsigned int midi_ports;
216 
217 		if (dir == AMDTP_IN_STREAM) {
218 			stream = &dice->tx_stream[i];
219 			resources = &dice->tx_resources[i];
220 
221 			pcm_cache = dice->tx_pcm_chs[i][mode];
222 			err = snd_dice_transaction_read_tx(dice,
223 					params->size * i + TX_NUMBER_AUDIO,
224 					reg, sizeof(reg));
225 		} else {
226 			stream = &dice->rx_stream[i];
227 			resources = &dice->rx_resources[i];
228 
229 			pcm_cache = dice->rx_pcm_chs[i][mode];
230 			err = snd_dice_transaction_read_rx(dice,
231 					params->size * i + RX_NUMBER_AUDIO,
232 					reg, sizeof(reg));
233 		}
234 		if (err < 0)
235 			return err;
236 		pcm_chs = be32_to_cpu(reg[0]);
237 		midi_ports = be32_to_cpu(reg[1]);
238 
239 		// These are important for developer of this driver.
240 		if (pcm_chs != pcm_cache) {
241 			dev_info(&dice->unit->device,
242 				 "cache mismatch: pcm: %u:%u, midi: %u\n",
243 				 pcm_chs, pcm_cache, midi_ports);
244 			return -EPROTO;
245 		}
246 
247 		err = keep_resources(dice, stream, resources, rate, pcm_chs,
248 				     midi_ports);
249 		if (err < 0)
250 			return err;
251 	}
252 
253 	return 0;
254 }
255 
256 static void finish_session(struct snd_dice *dice, struct reg_params *tx_params,
257 			   struct reg_params *rx_params)
258 {
259 	stop_streams(dice, AMDTP_IN_STREAM, tx_params);
260 	stop_streams(dice, AMDTP_OUT_STREAM, rx_params);
261 
262 	snd_dice_transaction_clear_enable(dice);
263 }
264 
265 int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate,
266 				   unsigned int events_per_period,
267 				   unsigned int events_per_buffer)
268 {
269 	unsigned int curr_rate;
270 	int err;
271 
272 	// Check sampling transmission frequency.
273 	err = snd_dice_transaction_get_rate(dice, &curr_rate);
274 	if (err < 0)
275 		return err;
276 	if (rate == 0)
277 		rate = curr_rate;
278 
279 	if (dice->substreams_counter == 0 || curr_rate != rate) {
280 		struct reg_params tx_params, rx_params;
281 
282 		amdtp_domain_stop(&dice->domain);
283 
284 		err = get_register_params(dice, &tx_params, &rx_params);
285 		if (err < 0)
286 			return err;
287 		finish_session(dice, &tx_params, &rx_params);
288 
289 		release_resources(dice);
290 
291 		// Just after owning the unit (GLOBAL_OWNER), the unit can
292 		// return invalid stream formats. Selecting clock parameters
293 		// have an effect for the unit to refine it.
294 		err = select_clock(dice, rate);
295 		if (err < 0)
296 			return err;
297 
298 		// After changing sampling transfer frequency, the value of
299 		// register can be changed.
300 		err = get_register_params(dice, &tx_params, &rx_params);
301 		if (err < 0)
302 			return err;
303 
304 		err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM,
305 					  &tx_params);
306 		if (err < 0)
307 			goto error;
308 
309 		err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM,
310 					  &rx_params);
311 		if (err < 0)
312 			goto error;
313 
314 		err = amdtp_domain_set_events_per_period(&dice->domain,
315 					events_per_period, events_per_buffer);
316 		if (err < 0)
317 			goto error;
318 	}
319 
320 	return 0;
321 error:
322 	release_resources(dice);
323 	return err;
324 }
325 
326 static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
327 			 unsigned int rate, struct reg_params *params)
328 {
329 	unsigned int max_speed = fw_parent_device(dice->unit)->max_speed;
330 	int i;
331 	int err;
332 
333 	for (i = 0; i < params->count; i++) {
334 		struct amdtp_stream *stream;
335 		struct fw_iso_resources *resources;
336 		__be32 reg;
337 
338 		if (dir == AMDTP_IN_STREAM) {
339 			stream = dice->tx_stream + i;
340 			resources = dice->tx_resources + i;
341 		} else {
342 			stream = dice->rx_stream + i;
343 			resources = dice->rx_resources + i;
344 		}
345 
346 		reg = cpu_to_be32(resources->channel);
347 		if (dir == AMDTP_IN_STREAM) {
348 			err = snd_dice_transaction_write_tx(dice,
349 					params->size * i + TX_ISOCHRONOUS,
350 					&reg, sizeof(reg));
351 		} else {
352 			err = snd_dice_transaction_write_rx(dice,
353 					params->size * i + RX_ISOCHRONOUS,
354 					&reg, sizeof(reg));
355 		}
356 		if (err < 0)
357 			return err;
358 
359 		if (dir == AMDTP_IN_STREAM) {
360 			reg = cpu_to_be32(max_speed);
361 			err = snd_dice_transaction_write_tx(dice,
362 					params->size * i + TX_SPEED,
363 					&reg, sizeof(reg));
364 			if (err < 0)
365 				return err;
366 		}
367 
368 		err = amdtp_domain_add_stream(&dice->domain, stream,
369 					      resources->channel, max_speed);
370 		if (err < 0)
371 			return err;
372 	}
373 
374 	return 0;
375 }
376 
377 /*
378  * MEMO: After this function, there're two states of streams:
379  *  - None streams are running.
380  *  - All streams are running.
381  */
382 int snd_dice_stream_start_duplex(struct snd_dice *dice)
383 {
384 	unsigned int generation = dice->rx_resources[0].generation;
385 	struct reg_params tx_params, rx_params;
386 	unsigned int i;
387 	unsigned int rate;
388 	enum snd_dice_rate_mode mode;
389 	int err;
390 
391 	if (dice->substreams_counter == 0)
392 		return -EIO;
393 
394 	err = get_register_params(dice, &tx_params, &rx_params);
395 	if (err < 0)
396 		return err;
397 
398 	// Check error of packet streaming.
399 	for (i = 0; i < MAX_STREAMS; ++i) {
400 		if (amdtp_streaming_error(&dice->tx_stream[i]) ||
401 		    amdtp_streaming_error(&dice->rx_stream[i])) {
402 			amdtp_domain_stop(&dice->domain);
403 			finish_session(dice, &tx_params, &rx_params);
404 			break;
405 		}
406 	}
407 
408 	if (generation != fw_parent_device(dice->unit)->card->generation) {
409 		for (i = 0; i < MAX_STREAMS; ++i) {
410 			if (i < tx_params.count)
411 				fw_iso_resources_update(dice->tx_resources + i);
412 			if (i < rx_params.count)
413 				fw_iso_resources_update(dice->rx_resources + i);
414 		}
415 	}
416 
417 	// Check required streams are running or not.
418 	err = snd_dice_transaction_get_rate(dice, &rate);
419 	if (err < 0)
420 		return err;
421 	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
422 	if (err < 0)
423 		return err;
424 	for (i = 0; i < MAX_STREAMS; ++i) {
425 		if (dice->tx_pcm_chs[i][mode] > 0 &&
426 		    !amdtp_stream_running(&dice->tx_stream[i]))
427 			break;
428 		if (dice->rx_pcm_chs[i][mode] > 0 &&
429 		    !amdtp_stream_running(&dice->rx_stream[i]))
430 			break;
431 	}
432 	if (i < MAX_STREAMS) {
433 		// Start both streams.
434 		err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
435 		if (err < 0)
436 			goto error;
437 
438 		err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
439 		if (err < 0)
440 			goto error;
441 
442 		err = snd_dice_transaction_set_enable(dice);
443 		if (err < 0) {
444 			dev_err(&dice->unit->device,
445 				"fail to enable interface\n");
446 			goto error;
447 		}
448 
449 		// MEMO: The device immediately starts packet transmission when enabled. Some
450 		// devices are strictly to generate any discontinuity in the sequence of tx packet
451 		// when they receives invalid sequence of presentation time in CIP header. The
452 		// sequence replay for media clock recovery can suppress the behaviour.
453 		err = amdtp_domain_start(&dice->domain, 0, true, false);
454 		if (err < 0)
455 			goto error;
456 
457 		if (!amdtp_domain_wait_ready(&dice->domain, READY_TIMEOUT_MS)) {
458 			err = -ETIMEDOUT;
459 			goto error;
460 		}
461 	}
462 
463 	return 0;
464 error:
465 	amdtp_domain_stop(&dice->domain);
466 	finish_session(dice, &tx_params, &rx_params);
467 	return err;
468 }
469 
470 /*
471  * MEMO: After this function, there're two states of streams:
472  *  - None streams are running.
473  *  - All streams are running.
474  */
475 void snd_dice_stream_stop_duplex(struct snd_dice *dice)
476 {
477 	struct reg_params tx_params, rx_params;
478 
479 	if (dice->substreams_counter == 0) {
480 		if (get_register_params(dice, &tx_params, &rx_params) >= 0)
481 			finish_session(dice, &tx_params, &rx_params);
482 
483 		amdtp_domain_stop(&dice->domain);
484 		release_resources(dice);
485 	}
486 }
487 
488 static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
489 		       unsigned int index)
490 {
491 	struct amdtp_stream *stream;
492 	struct fw_iso_resources *resources;
493 	int err;
494 
495 	if (dir == AMDTP_IN_STREAM) {
496 		stream = &dice->tx_stream[index];
497 		resources = &dice->tx_resources[index];
498 	} else {
499 		stream = &dice->rx_stream[index];
500 		resources = &dice->rx_resources[index];
501 	}
502 
503 	err = fw_iso_resources_init(resources, dice->unit);
504 	if (err < 0)
505 		goto end;
506 	resources->channels_mask = 0x00000000ffffffffuLL;
507 
508 	err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
509 	if (err < 0) {
510 		amdtp_stream_destroy(stream);
511 		fw_iso_resources_destroy(resources);
512 	}
513 end:
514 	return err;
515 }
516 
517 /*
518  * This function should be called before starting streams or after stopping
519  * streams.
520  */
521 static void destroy_stream(struct snd_dice *dice,
522 			   enum amdtp_stream_direction dir,
523 			   unsigned int index)
524 {
525 	struct amdtp_stream *stream;
526 	struct fw_iso_resources *resources;
527 
528 	if (dir == AMDTP_IN_STREAM) {
529 		stream = &dice->tx_stream[index];
530 		resources = &dice->tx_resources[index];
531 	} else {
532 		stream = &dice->rx_stream[index];
533 		resources = &dice->rx_resources[index];
534 	}
535 
536 	amdtp_stream_destroy(stream);
537 	fw_iso_resources_destroy(resources);
538 }
539 
540 int snd_dice_stream_init_duplex(struct snd_dice *dice)
541 {
542 	int i, err;
543 
544 	for (i = 0; i < MAX_STREAMS; i++) {
545 		err = init_stream(dice, AMDTP_IN_STREAM, i);
546 		if (err < 0) {
547 			for (; i >= 0; i--)
548 				destroy_stream(dice, AMDTP_IN_STREAM, i);
549 			goto end;
550 		}
551 	}
552 
553 	for (i = 0; i < MAX_STREAMS; i++) {
554 		err = init_stream(dice, AMDTP_OUT_STREAM, i);
555 		if (err < 0) {
556 			for (; i >= 0; i--)
557 				destroy_stream(dice, AMDTP_OUT_STREAM, i);
558 			for (i = 0; i < MAX_STREAMS; i++)
559 				destroy_stream(dice, AMDTP_IN_STREAM, i);
560 			goto end;
561 		}
562 	}
563 
564 	err = amdtp_domain_init(&dice->domain);
565 	if (err < 0) {
566 		for (i = 0; i < MAX_STREAMS; ++i) {
567 			destroy_stream(dice, AMDTP_OUT_STREAM, i);
568 			destroy_stream(dice, AMDTP_IN_STREAM, i);
569 		}
570 	}
571 end:
572 	return err;
573 }
574 
575 void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
576 {
577 	unsigned int i;
578 
579 	for (i = 0; i < MAX_STREAMS; i++) {
580 		destroy_stream(dice, AMDTP_IN_STREAM, i);
581 		destroy_stream(dice, AMDTP_OUT_STREAM, i);
582 	}
583 
584 	amdtp_domain_destroy(&dice->domain);
585 }
586 
587 void snd_dice_stream_update_duplex(struct snd_dice *dice)
588 {
589 	struct reg_params tx_params, rx_params;
590 
591 	/*
592 	 * On a bus reset, the DICE firmware disables streaming and then goes
593 	 * off contemplating its own navel for hundreds of milliseconds before
594 	 * it can react to any of our attempts to reenable streaming.  This
595 	 * means that we lose synchronization anyway, so we force our streams
596 	 * to stop so that the application can restart them in an orderly
597 	 * manner.
598 	 */
599 	dice->global_enabled = false;
600 
601 	if (get_register_params(dice, &tx_params, &rx_params) == 0) {
602 		amdtp_domain_stop(&dice->domain);
603 
604 		stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
605 		stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
606 	}
607 }
608 
609 int snd_dice_stream_detect_current_formats(struct snd_dice *dice)
610 {
611 	unsigned int rate;
612 	enum snd_dice_rate_mode mode;
613 	__be32 reg[2];
614 	struct reg_params tx_params, rx_params;
615 	int i;
616 	int err;
617 
618 	/* If extended protocol is available, detect detail spec. */
619 	err = snd_dice_detect_extension_formats(dice);
620 	if (err >= 0)
621 		return err;
622 
623 	/*
624 	 * Available stream format is restricted at current mode of sampling
625 	 * clock.
626 	 */
627 	err = snd_dice_transaction_get_rate(dice, &rate);
628 	if (err < 0)
629 		return err;
630 
631 	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
632 	if (err < 0)
633 		return err;
634 
635 	/*
636 	 * Just after owning the unit (GLOBAL_OWNER), the unit can return
637 	 * invalid stream formats. Selecting clock parameters have an effect
638 	 * for the unit to refine it.
639 	 */
640 	err = select_clock(dice, rate);
641 	if (err < 0)
642 		return err;
643 
644 	err = get_register_params(dice, &tx_params, &rx_params);
645 	if (err < 0)
646 		return err;
647 
648 	for (i = 0; i < tx_params.count; ++i) {
649 		err = snd_dice_transaction_read_tx(dice,
650 				tx_params.size * i + TX_NUMBER_AUDIO,
651 				reg, sizeof(reg));
652 		if (err < 0)
653 			return err;
654 		dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
655 		dice->tx_midi_ports[i] = max_t(unsigned int,
656 				be32_to_cpu(reg[1]), dice->tx_midi_ports[i]);
657 	}
658 	for (i = 0; i < rx_params.count; ++i) {
659 		err = snd_dice_transaction_read_rx(dice,
660 				rx_params.size * i + RX_NUMBER_AUDIO,
661 				reg, sizeof(reg));
662 		if (err < 0)
663 			return err;
664 		dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
665 		dice->rx_midi_ports[i] = max_t(unsigned int,
666 				be32_to_cpu(reg[1]), dice->rx_midi_ports[i]);
667 	}
668 
669 	return 0;
670 }
671 
672 static void dice_lock_changed(struct snd_dice *dice)
673 {
674 	dice->dev_lock_changed = true;
675 	wake_up(&dice->hwdep_wait);
676 }
677 
678 int snd_dice_stream_lock_try(struct snd_dice *dice)
679 {
680 	int err;
681 
682 	spin_lock_irq(&dice->lock);
683 
684 	if (dice->dev_lock_count < 0) {
685 		err = -EBUSY;
686 		goto out;
687 	}
688 
689 	if (dice->dev_lock_count++ == 0)
690 		dice_lock_changed(dice);
691 	err = 0;
692 out:
693 	spin_unlock_irq(&dice->lock);
694 	return err;
695 }
696 
697 void snd_dice_stream_lock_release(struct snd_dice *dice)
698 {
699 	spin_lock_irq(&dice->lock);
700 
701 	if (WARN_ON(dice->dev_lock_count <= 0))
702 		goto out;
703 
704 	if (--dice->dev_lock_count == 0)
705 		dice_lock_changed(dice);
706 out:
707 	spin_unlock_irq(&dice->lock);
708 }
709