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 | buf: ®, len: 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(x: &dice->clock_accepted)) |
84 | reinit_completion(x: &dice->clock_accepted); |
85 | |
86 | new = cpu_to_be32(data); |
87 | err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, |
88 | buf: &new, len: sizeof(new)); |
89 | if (err < 0) |
90 | return err; |
91 | |
92 | if (wait_for_completion_timeout(x: &dice->clock_accepted, |
93 | timeout: 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, buf: reg, len: 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, buf: reg, len: 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(r: &dice->tx_resources[i]); |
131 | fw_iso_resources_free(r: &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 | offset: params->size * i + TX_ISOCHRONOUS, |
146 | buf: ®, len: sizeof(reg)); |
147 | } else { |
148 | snd_dice_transaction_write_rx(dice, |
149 | offset: params->size * i + RX_ISOCHRONOUS, |
150 | buf: ®, len: 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(s: stream, rate, pcm_channels: 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(s: stream, index: i, position: i * 2); |
187 | amdtp_am824_set_pcm_position(s: stream, index: i + pcm_chs, |
188 | position: i * 2 + 1); |
189 | } |
190 | } |
191 | |
192 | return fw_iso_resources_allocate(r: resources, |
193 | max_payload_bytes: amdtp_stream_get_max_payload(s: 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: &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 | offset: params->size * i + TX_NUMBER_AUDIO, |
224 | buf: reg, len: 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 | offset: params->size * i + RX_NUMBER_AUDIO, |
232 | buf: reg, len: 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, dir: AMDTP_IN_STREAM, params: tx_params); |
260 | stop_streams(dice, dir: AMDTP_OUT_STREAM, params: 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, rate: &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(d: &dice->domain); |
283 | |
284 | err = get_register_params(dice, tx_params: &tx_params, rx_params: &rx_params); |
285 | if (err < 0) |
286 | return err; |
287 | finish_session(dice, tx_params: &tx_params, rx_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: &tx_params, rx_params: &rx_params); |
301 | if (err < 0) |
302 | return err; |
303 | |
304 | err = keep_dual_resources(dice, rate, dir: AMDTP_IN_STREAM, |
305 | params: &tx_params); |
306 | if (err < 0) |
307 | goto error; |
308 | |
309 | err = keep_dual_resources(dice, rate, dir: AMDTP_OUT_STREAM, |
310 | params: &rx_params); |
311 | if (err < 0) |
312 | goto error; |
313 | |
314 | err = amdtp_domain_set_events_per_period(d: &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 | offset: params->size * i + TX_ISOCHRONOUS, |
350 | buf: ®, len: sizeof(reg)); |
351 | } else { |
352 | err = snd_dice_transaction_write_rx(dice, |
353 | offset: params->size * i + RX_ISOCHRONOUS, |
354 | buf: ®, len: 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 | offset: params->size * i + TX_SPEED, |
363 | buf: ®, len: sizeof(reg)); |
364 | if (err < 0) |
365 | return err; |
366 | } |
367 | |
368 | err = amdtp_domain_add_stream(d: &dice->domain, s: stream, |
369 | channel: resources->channel, speed: 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: &tx_params, rx_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(s: &dice->tx_stream[i]) || |
401 | amdtp_streaming_error(s: &dice->rx_stream[i])) { |
402 | amdtp_domain_stop(d: &dice->domain); |
403 | finish_session(dice, tx_params: &tx_params, rx_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(r: dice->tx_resources + i); |
412 | if (i < rx_params.count) |
413 | fw_iso_resources_update(r: dice->rx_resources + i); |
414 | } |
415 | } |
416 | |
417 | // Check required streams are running or not. |
418 | err = snd_dice_transaction_get_rate(dice, rate: &rate); |
419 | if (err < 0) |
420 | return err; |
421 | err = snd_dice_stream_get_rate_mode(dice, rate, mode: &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(s: &dice->tx_stream[i])) |
427 | break; |
428 | if (dice->rx_pcm_chs[i][mode] > 0 && |
429 | !amdtp_stream_running(s: &dice->rx_stream[i])) |
430 | break; |
431 | } |
432 | if (i < MAX_STREAMS) { |
433 | // Start both streams. |
434 | err = start_streams(dice, dir: AMDTP_IN_STREAM, rate, params: &tx_params); |
435 | if (err < 0) |
436 | goto error; |
437 | |
438 | err = start_streams(dice, dir: AMDTP_OUT_STREAM, rate, params: &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(d: &dice->domain, tx_init_skip_cycles: 0, replay_seq: true, replay_on_the_fly: false); |
454 | if (err < 0) |
455 | goto error; |
456 | |
457 | if (!amdtp_domain_wait_ready(d: &dice->domain, READY_TIMEOUT_MS)) { |
458 | err = -ETIMEDOUT; |
459 | goto error; |
460 | } |
461 | } |
462 | |
463 | return 0; |
464 | error: |
465 | amdtp_domain_stop(d: &dice->domain); |
466 | finish_session(dice, tx_params: &tx_params, rx_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: &tx_params, rx_params: &rx_params) >= 0) |
481 | finish_session(dice, tx_params: &tx_params, rx_params: &rx_params); |
482 | |
483 | amdtp_domain_stop(d: &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(r: resources, unit: dice->unit); |
504 | if (err < 0) |
505 | goto end; |
506 | resources->channels_mask = 0x00000000ffffffffuLL; |
507 | |
508 | err = amdtp_am824_init(s: stream, unit: dice->unit, dir, flags: CIP_BLOCKING); |
509 | if (err < 0) { |
510 | amdtp_stream_destroy(s: stream); |
511 | fw_iso_resources_destroy(r: 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(s: stream); |
537 | fw_iso_resources_destroy(r: 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, dir: AMDTP_IN_STREAM, index: i); |
546 | if (err < 0) { |
547 | for (; i >= 0; i--) |
548 | destroy_stream(dice, dir: AMDTP_IN_STREAM, index: i); |
549 | goto end; |
550 | } |
551 | } |
552 | |
553 | for (i = 0; i < MAX_STREAMS; i++) { |
554 | err = init_stream(dice, dir: AMDTP_OUT_STREAM, index: i); |
555 | if (err < 0) { |
556 | for (; i >= 0; i--) |
557 | destroy_stream(dice, dir: AMDTP_OUT_STREAM, index: i); |
558 | for (i = 0; i < MAX_STREAMS; i++) |
559 | destroy_stream(dice, dir: AMDTP_IN_STREAM, index: i); |
560 | goto end; |
561 | } |
562 | } |
563 | |
564 | err = amdtp_domain_init(d: &dice->domain); |
565 | if (err < 0) { |
566 | for (i = 0; i < MAX_STREAMS; ++i) { |
567 | destroy_stream(dice, dir: AMDTP_OUT_STREAM, index: i); |
568 | destroy_stream(dice, dir: AMDTP_IN_STREAM, index: 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, dir: AMDTP_IN_STREAM, index: i); |
581 | destroy_stream(dice, dir: AMDTP_OUT_STREAM, index: i); |
582 | } |
583 | |
584 | amdtp_domain_destroy(d: &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: &tx_params, rx_params: &rx_params) == 0) { |
602 | amdtp_domain_stop(d: &dice->domain); |
603 | |
604 | stop_streams(dice, dir: AMDTP_IN_STREAM, params: &tx_params); |
605 | stop_streams(dice, dir: AMDTP_OUT_STREAM, params: &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: &rate); |
628 | if (err < 0) |
629 | return err; |
630 | |
631 | err = snd_dice_stream_get_rate_mode(dice, rate, mode: &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: &tx_params, rx_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 | offset: tx_params.size * i + TX_NUMBER_AUDIO, |
651 | buf: reg, len: 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 | offset: rx_params.size * i + RX_NUMBER_AUDIO, |
661 | buf: reg, len: 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(lock: &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(lock: &dice->lock); |
694 | return err; |
695 | } |
696 | |
697 | void snd_dice_stream_lock_release(struct snd_dice *dice) |
698 | { |
699 | spin_lock_irq(lock: &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(lock: &dice->lock); |
708 | } |
709 | |