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