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