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, new;
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 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
get_register_params(struct snd_dice * dice,struct reg_params * tx_params,struct reg_params * rx_params)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
release_resources(struct snd_dice * dice)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
stop_streams(struct snd_dice * dice,enum amdtp_stream_direction dir,struct reg_params * params)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 ®, sizeof(reg));
147 } else {
148 snd_dice_transaction_write_rx(dice,
149 params->size * i + RX_ISOCHRONOUS,
150 ®, sizeof(reg));
151 }
152 }
153 }
154
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)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
keep_dual_resources(struct snd_dice * dice,unsigned int rate,enum amdtp_stream_direction dir,struct reg_params * params)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
finish_session(struct snd_dice * dice,struct reg_params * tx_params,struct reg_params * rx_params)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
snd_dice_stream_reserve_duplex(struct snd_dice * dice,unsigned int rate,unsigned int events_per_period,unsigned int events_per_buffer)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
start_streams(struct snd_dice * dice,enum amdtp_stream_direction dir,unsigned int rate,struct reg_params * params)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 ®, sizeof(reg));
351 } else {
352 err = snd_dice_transaction_write_rx(dice,
353 params->size * i + RX_ISOCHRONOUS,
354 ®, 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 ®, 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 */
snd_dice_stream_start_duplex(struct snd_dice * dice)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 */
snd_dice_stream_stop_duplex(struct snd_dice * dice)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
init_stream(struct snd_dice * dice,enum amdtp_stream_direction dir,unsigned int index)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 */
destroy_stream(struct snd_dice * dice,enum amdtp_stream_direction dir,unsigned int index)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
snd_dice_stream_init_duplex(struct snd_dice * dice)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
snd_dice_stream_destroy_duplex(struct snd_dice * dice)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
snd_dice_stream_update_duplex(struct snd_dice * dice)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
snd_dice_stream_detect_current_formats(struct snd_dice * dice)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
dice_lock_changed(struct snd_dice * dice)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
snd_dice_stream_lock_try(struct snd_dice * dice)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
snd_dice_stream_lock_release(struct snd_dice * dice)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