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