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 | ||
94c8101a TS |
281 | int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate, |
282 | unsigned int events_per_period) | |
3cd2c2d7 TS |
283 | { |
284 | unsigned int curr_rate; | |
285 | int err; | |
286 | ||
287 | // Check sampling transmission frequency. | |
288 | err = snd_dice_transaction_get_rate(dice, &curr_rate); | |
289 | if (err < 0) | |
290 | return err; | |
291 | if (rate == 0) | |
292 | rate = curr_rate; | |
293 | ||
294 | if (dice->substreams_counter == 0 || curr_rate != rate) { | |
295 | struct reg_params tx_params, rx_params; | |
296 | ||
e9f21129 TS |
297 | amdtp_domain_stop(&dice->domain); |
298 | ||
3cd2c2d7 TS |
299 | err = get_register_params(dice, &tx_params, &rx_params); |
300 | if (err < 0) | |
301 | return err; | |
3cd2c2d7 TS |
302 | finish_session(dice, &tx_params, &rx_params); |
303 | ||
304 | release_resources(dice); | |
305 | ||
306 | // Just after owning the unit (GLOBAL_OWNER), the unit can | |
307 | // return invalid stream formats. Selecting clock parameters | |
308 | // have an effect for the unit to refine it. | |
309 | err = ensure_phase_lock(dice, rate); | |
310 | if (err < 0) | |
311 | return err; | |
312 | ||
313 | // After changing sampling transfer frequency, the value of | |
314 | // register can be changed. | |
315 | err = get_register_params(dice, &tx_params, &rx_params); | |
316 | if (err < 0) | |
317 | return err; | |
318 | ||
319 | err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM, | |
320 | &tx_params); | |
321 | if (err < 0) | |
322 | goto error; | |
323 | ||
324 | err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM, | |
325 | &rx_params); | |
326 | if (err < 0) | |
327 | goto error; | |
94c8101a TS |
328 | |
329 | err = amdtp_domain_set_events_per_period(&dice->domain, | |
a0e02331 | 330 | events_per_period, 0); |
94c8101a TS |
331 | if (err < 0) |
332 | goto error; | |
3cd2c2d7 TS |
333 | } |
334 | ||
335 | return 0; | |
336 | error: | |
337 | release_resources(dice); | |
338 | return err; | |
339 | } | |
340 | ||
c738aed1 TS |
341 | static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, |
342 | unsigned int rate, struct reg_params *params) | |
343 | { | |
344 | unsigned int max_speed = fw_parent_device(dice->unit)->max_speed; | |
345 | int i; | |
346 | int err; | |
347 | ||
c738aed1 TS |
348 | for (i = 0; i < params->count; i++) { |
349 | struct amdtp_stream *stream; | |
350 | struct fw_iso_resources *resources; | |
351 | __be32 reg; | |
352 | ||
353 | if (dir == AMDTP_IN_STREAM) { | |
354 | stream = dice->tx_stream + i; | |
355 | resources = dice->tx_resources + i; | |
356 | } else { | |
357 | stream = dice->rx_stream + i; | |
358 | resources = dice->rx_resources + i; | |
359 | } | |
360 | ||
361 | reg = cpu_to_be32(resources->channel); | |
436b5abe TS |
362 | if (dir == AMDTP_IN_STREAM) { |
363 | err = snd_dice_transaction_write_tx(dice, | |
8cc1a8ab | 364 | params->size * i + TX_ISOCHRONOUS, |
c738aed1 | 365 | ®, sizeof(reg)); |
436b5abe TS |
366 | } else { |
367 | err = snd_dice_transaction_write_rx(dice, | |
8cc1a8ab | 368 | params->size * i + RX_ISOCHRONOUS, |
c738aed1 | 369 | ®, sizeof(reg)); |
436b5abe TS |
370 | } |
371 | if (err < 0) | |
372 | return err; | |
373 | ||
b0e159fe | 374 | if (dir == AMDTP_IN_STREAM) { |
c738aed1 | 375 | reg = cpu_to_be32(max_speed); |
b0e159fe TS |
376 | err = snd_dice_transaction_write_tx(dice, |
377 | params->size * i + TX_SPEED, | |
c738aed1 | 378 | ®, sizeof(reg)); |
b0e159fe TS |
379 | if (err < 0) |
380 | return err; | |
381 | } | |
382 | ||
e9f21129 TS |
383 | err = amdtp_domain_add_stream(&dice->domain, stream, |
384 | resources->channel, max_speed); | |
436b5abe TS |
385 | if (err < 0) |
386 | return err; | |
288a8d0c TS |
387 | } |
388 | ||
c72d3a0a | 389 | return 0; |
288a8d0c TS |
390 | } |
391 | ||
436b5abe TS |
392 | /* |
393 | * MEMO: After this function, there're two states of streams: | |
394 | * - None streams are running. | |
395 | * - All streams are running. | |
396 | */ | |
3cd2c2d7 | 397 | int snd_dice_stream_start_duplex(struct snd_dice *dice) |
288a8d0c | 398 | { |
d5553026 | 399 | unsigned int generation = dice->rx_resources[0].generation; |
3cd2c2d7 | 400 | struct reg_params tx_params, rx_params; |
436b5abe | 401 | unsigned int i; |
3cd2c2d7 | 402 | unsigned int rate; |
ec592fd3 | 403 | enum snd_dice_rate_mode mode; |
436b5abe | 404 | int err; |
9a02843c TS |
405 | |
406 | if (dice->substreams_counter == 0) | |
436b5abe | 407 | return -EIO; |
288a8d0c | 408 | |
3cd2c2d7 TS |
409 | err = get_register_params(dice, &tx_params, &rx_params); |
410 | if (err < 0) | |
436b5abe | 411 | return err; |
436b5abe | 412 | |
3cd2c2d7 | 413 | // Check error of packet streaming. |
ec592fd3 | 414 | for (i = 0; i < MAX_STREAMS; ++i) { |
3cd2c2d7 TS |
415 | if (amdtp_streaming_error(&dice->tx_stream[i]) || |
416 | amdtp_streaming_error(&dice->rx_stream[i])) { | |
e9f21129 | 417 | amdtp_domain_stop(&dice->domain); |
3cd2c2d7 | 418 | finish_session(dice, &tx_params, &rx_params); |
ec592fd3 | 419 | break; |
3cd2c2d7 | 420 | } |
1bc8e12d | 421 | } |
ec592fd3 | 422 | |
d5553026 TS |
423 | if (generation != fw_parent_device(dice->unit)->card->generation) { |
424 | for (i = 0; i < MAX_STREAMS; ++i) { | |
425 | if (i < tx_params.count) | |
426 | fw_iso_resources_update(dice->tx_resources + i); | |
427 | if (i < rx_params.count) | |
428 | fw_iso_resources_update(dice->rx_resources + i); | |
429 | } | |
430 | } | |
431 | ||
3cd2c2d7 TS |
432 | // Check required streams are running or not. |
433 | err = snd_dice_transaction_get_rate(dice, &rate); | |
434 | if (err < 0) | |
435 | return err; | |
ec592fd3 TS |
436 | err = snd_dice_stream_get_rate_mode(dice, rate, &mode); |
437 | if (err < 0) | |
438 | return err; | |
439 | for (i = 0; i < MAX_STREAMS; ++i) { | |
440 | if (dice->tx_pcm_chs[i][mode] > 0 && | |
441 | !amdtp_stream_running(&dice->tx_stream[i])) | |
442 | break; | |
443 | if (dice->rx_pcm_chs[i][mode] > 0 && | |
444 | !amdtp_stream_running(&dice->rx_stream[i])) | |
445 | break; | |
446 | } | |
3cd2c2d7 TS |
447 | if (i < MAX_STREAMS) { |
448 | // Start both streams. | |
449 | err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); | |
450 | if (err < 0) | |
451 | goto error; | |
452 | ||
453 | err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); | |
454 | if (err < 0) | |
455 | goto error; | |
456 | ||
457 | err = snd_dice_transaction_set_enable(dice); | |
458 | if (err < 0) { | |
459 | dev_err(&dice->unit->device, | |
460 | "fail to enable interface\n"); | |
461 | goto error; | |
462 | } | |
463 | ||
e9f21129 TS |
464 | err = amdtp_domain_start(&dice->domain); |
465 | if (err < 0) | |
466 | goto error; | |
467 | ||
3cd2c2d7 TS |
468 | for (i = 0; i < MAX_STREAMS; i++) { |
469 | if ((i < tx_params.count && | |
470 | !amdtp_stream_wait_callback(&dice->tx_stream[i], | |
471 | CALLBACK_TIMEOUT)) || | |
472 | (i < rx_params.count && | |
473 | !amdtp_stream_wait_callback(&dice->rx_stream[i], | |
474 | CALLBACK_TIMEOUT))) { | |
475 | err = -ETIMEDOUT; | |
476 | goto error; | |
477 | } | |
478 | } | |
479 | } | |
436b5abe | 480 | |
20b94544 | 481 | return 0; |
3cd2c2d7 | 482 | error: |
e9f21129 | 483 | amdtp_domain_stop(&dice->domain); |
3cd2c2d7 TS |
484 | finish_session(dice, &tx_params, &rx_params); |
485 | return err; | |
288a8d0c TS |
486 | } |
487 | ||
436b5abe TS |
488 | /* |
489 | * MEMO: After this function, there're two states of streams: | |
490 | * - None streams are running. | |
491 | * - All streams are running. | |
492 | */ | |
9a02843c | 493 | void snd_dice_stream_stop_duplex(struct snd_dice *dice) |
288a8d0c | 494 | { |
8cc1a8ab | 495 | struct reg_params tx_params, rx_params; |
436b5abe | 496 | |
3cd2c2d7 | 497 | if (dice->substreams_counter == 0) { |
e9f21129 TS |
498 | if (get_register_params(dice, &tx_params, &rx_params) >= 0) { |
499 | amdtp_domain_stop(&dice->domain); | |
3cd2c2d7 | 500 | finish_session(dice, &tx_params, &rx_params); |
e9f21129 | 501 | } |
740680f2 TS |
502 | |
503 | release_resources(dice); | |
3cd2c2d7 | 504 | } |
6eb6c81e TS |
505 | } |
506 | ||
436b5abe TS |
507 | static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir, |
508 | unsigned int index) | |
6eb6c81e | 509 | { |
436b5abe | 510 | struct amdtp_stream *stream; |
9a02843c | 511 | struct fw_iso_resources *resources; |
436b5abe | 512 | int err; |
9a02843c | 513 | |
436b5abe TS |
514 | if (dir == AMDTP_IN_STREAM) { |
515 | stream = &dice->tx_stream[index]; | |
516 | resources = &dice->tx_resources[index]; | |
9a02843c | 517 | } else { |
436b5abe TS |
518 | stream = &dice->rx_stream[index]; |
519 | resources = &dice->rx_resources[index]; | |
9a02843c | 520 | } |
6eb6c81e | 521 | |
9a02843c | 522 | err = fw_iso_resources_init(resources, dice->unit); |
6eb6c81e TS |
523 | if (err < 0) |
524 | goto end; | |
9a02843c | 525 | resources->channels_mask = 0x00000000ffffffffuLL; |
6eb6c81e | 526 | |
5955815e | 527 | err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING); |
9a02843c TS |
528 | if (err < 0) { |
529 | amdtp_stream_destroy(stream); | |
530 | fw_iso_resources_destroy(resources); | |
531 | } | |
532 | end: | |
533 | return err; | |
534 | } | |
535 | ||
d23c2cc4 TS |
536 | /* |
537 | * This function should be called before starting streams or after stopping | |
538 | * streams. | |
539 | */ | |
436b5abe TS |
540 | static void destroy_stream(struct snd_dice *dice, |
541 | enum amdtp_stream_direction dir, | |
542 | unsigned int index) | |
9a02843c | 543 | { |
436b5abe | 544 | struct amdtp_stream *stream; |
d23c2cc4 | 545 | struct fw_iso_resources *resources; |
9a02843c | 546 | |
436b5abe TS |
547 | if (dir == AMDTP_IN_STREAM) { |
548 | stream = &dice->tx_stream[index]; | |
549 | resources = &dice->tx_resources[index]; | |
550 | } else { | |
551 | stream = &dice->rx_stream[index]; | |
552 | resources = &dice->rx_resources[index]; | |
553 | } | |
d23c2cc4 TS |
554 | |
555 | amdtp_stream_destroy(stream); | |
556 | fw_iso_resources_destroy(resources); | |
9a02843c TS |
557 | } |
558 | ||
559 | int snd_dice_stream_init_duplex(struct snd_dice *dice) | |
560 | { | |
436b5abe | 561 | int i, err; |
9a02843c | 562 | |
436b5abe TS |
563 | for (i = 0; i < MAX_STREAMS; i++) { |
564 | err = init_stream(dice, AMDTP_IN_STREAM, i); | |
565 | if (err < 0) { | |
566 | for (; i >= 0; i--) | |
0f925660 | 567 | destroy_stream(dice, AMDTP_IN_STREAM, i); |
436b5abe TS |
568 | goto end; |
569 | } | |
570 | } | |
6eb6c81e | 571 | |
436b5abe TS |
572 | for (i = 0; i < MAX_STREAMS; i++) { |
573 | err = init_stream(dice, AMDTP_OUT_STREAM, i); | |
574 | if (err < 0) { | |
575 | for (; i >= 0; i--) | |
576 | destroy_stream(dice, AMDTP_OUT_STREAM, i); | |
577 | for (i = 0; i < MAX_STREAMS; i++) | |
578 | destroy_stream(dice, AMDTP_IN_STREAM, i); | |
6e26d193 | 579 | goto end; |
436b5abe TS |
580 | } |
581 | } | |
e9f21129 TS |
582 | |
583 | err = amdtp_domain_init(&dice->domain); | |
584 | if (err < 0) { | |
585 | for (i = 0; i < MAX_STREAMS; ++i) { | |
586 | destroy_stream(dice, AMDTP_OUT_STREAM, i); | |
587 | destroy_stream(dice, AMDTP_IN_STREAM, i); | |
588 | } | |
589 | } | |
6eb6c81e TS |
590 | end: |
591 | return err; | |
6eb6c81e TS |
592 | } |
593 | ||
9a02843c | 594 | void snd_dice_stream_destroy_duplex(struct snd_dice *dice) |
6eb6c81e | 595 | { |
6b94fb14 | 596 | unsigned int i; |
9a02843c | 597 | |
6b94fb14 TS |
598 | for (i = 0; i < MAX_STREAMS; i++) { |
599 | destroy_stream(dice, AMDTP_IN_STREAM, i); | |
600 | destroy_stream(dice, AMDTP_OUT_STREAM, i); | |
436b5abe | 601 | } |
e9f21129 TS |
602 | |
603 | amdtp_domain_destroy(&dice->domain); | |
6eb6c81e TS |
604 | } |
605 | ||
9a02843c | 606 | void snd_dice_stream_update_duplex(struct snd_dice *dice) |
6eb6c81e | 607 | { |
8cc1a8ab | 608 | struct reg_params tx_params, rx_params; |
436b5abe | 609 | |
6eb6c81e TS |
610 | /* |
611 | * On a bus reset, the DICE firmware disables streaming and then goes | |
612 | * off contemplating its own navel for hundreds of milliseconds before | |
613 | * it can react to any of our attempts to reenable streaming. This | |
614 | * means that we lose synchronization anyway, so we force our streams | |
615 | * to stop so that the application can restart them in an orderly | |
616 | * manner. | |
617 | */ | |
618 | dice->global_enabled = false; | |
619 | ||
8cc1a8ab | 620 | if (get_register_params(dice, &tx_params, &rx_params) == 0) { |
e9f21129 TS |
621 | amdtp_domain_stop(&dice->domain); |
622 | ||
8cc1a8ab TS |
623 | stop_streams(dice, AMDTP_IN_STREAM, &tx_params); |
624 | stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); | |
436b5abe | 625 | } |
6eb6c81e TS |
626 | } |
627 | ||
b60152f7 TS |
628 | int snd_dice_stream_detect_current_formats(struct snd_dice *dice) |
629 | { | |
630 | unsigned int rate; | |
631 | enum snd_dice_rate_mode mode; | |
632 | __be32 reg[2]; | |
633 | struct reg_params tx_params, rx_params; | |
634 | int i; | |
635 | int err; | |
636 | ||
58579c05 TS |
637 | /* If extended protocol is available, detect detail spec. */ |
638 | err = snd_dice_detect_extension_formats(dice); | |
639 | if (err >= 0) | |
640 | return err; | |
641 | ||
b60152f7 TS |
642 | /* |
643 | * Available stream format is restricted at current mode of sampling | |
644 | * clock. | |
645 | */ | |
646 | err = snd_dice_transaction_get_rate(dice, &rate); | |
647 | if (err < 0) | |
648 | return err; | |
649 | ||
650 | err = snd_dice_stream_get_rate_mode(dice, rate, &mode); | |
651 | if (err < 0) | |
652 | return err; | |
653 | ||
654 | /* | |
655 | * Just after owning the unit (GLOBAL_OWNER), the unit can return | |
656 | * invalid stream formats. Selecting clock parameters have an effect | |
657 | * for the unit to refine it. | |
658 | */ | |
afa617f2 | 659 | err = ensure_phase_lock(dice, rate); |
b60152f7 TS |
660 | if (err < 0) |
661 | return err; | |
662 | ||
663 | err = get_register_params(dice, &tx_params, &rx_params); | |
664 | if (err < 0) | |
665 | return err; | |
666 | ||
667 | for (i = 0; i < tx_params.count; ++i) { | |
668 | err = snd_dice_transaction_read_tx(dice, | |
669 | tx_params.size * i + TX_NUMBER_AUDIO, | |
670 | reg, sizeof(reg)); | |
671 | if (err < 0) | |
672 | return err; | |
673 | dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]); | |
674 | dice->tx_midi_ports[i] = max_t(unsigned int, | |
675 | be32_to_cpu(reg[1]), dice->tx_midi_ports[i]); | |
676 | } | |
677 | for (i = 0; i < rx_params.count; ++i) { | |
678 | err = snd_dice_transaction_read_rx(dice, | |
679 | rx_params.size * i + RX_NUMBER_AUDIO, | |
680 | reg, sizeof(reg)); | |
681 | if (err < 0) | |
682 | return err; | |
683 | dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]); | |
684 | dice->rx_midi_ports[i] = max_t(unsigned int, | |
685 | be32_to_cpu(reg[1]), dice->rx_midi_ports[i]); | |
686 | } | |
687 | ||
688 | return 0; | |
689 | } | |
690 | ||
6eb6c81e TS |
691 | static void dice_lock_changed(struct snd_dice *dice) |
692 | { | |
693 | dice->dev_lock_changed = true; | |
694 | wake_up(&dice->hwdep_wait); | |
695 | } | |
696 | ||
697 | int snd_dice_stream_lock_try(struct snd_dice *dice) | |
698 | { | |
699 | int err; | |
700 | ||
701 | spin_lock_irq(&dice->lock); | |
702 | ||
703 | if (dice->dev_lock_count < 0) { | |
704 | err = -EBUSY; | |
705 | goto out; | |
706 | } | |
707 | ||
708 | if (dice->dev_lock_count++ == 0) | |
709 | dice_lock_changed(dice); | |
710 | err = 0; | |
711 | out: | |
712 | spin_unlock_irq(&dice->lock); | |
713 | return err; | |
714 | } | |
715 | ||
716 | void snd_dice_stream_lock_release(struct snd_dice *dice) | |
717 | { | |
718 | spin_lock_irq(&dice->lock); | |
719 | ||
720 | if (WARN_ON(dice->dev_lock_count <= 0)) | |
721 | goto out; | |
722 | ||
723 | if (--dice->dev_lock_count == 0) | |
724 | dice_lock_changed(dice); | |
725 | out: | |
726 | spin_unlock_irq(&dice->lock); | |
727 | } |