Commit | Line | Data |
---|---|---|
6eb6c81e TS |
1 | /* |
2 | * dice_stream.c - a part of driver for DICE based devices | |
3 | * | |
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | |
5 | * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp> | |
6 | * | |
7 | * Licensed under the terms of the GNU General Public License, version 2. | |
8 | */ | |
9 | ||
10 | #include "dice.h" | |
11 | ||
288a8d0c TS |
12 | #define CALLBACK_TIMEOUT 200 |
13 | ||
6eb6c81e TS |
14 | const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { |
15 | /* mode 0 */ | |
16 | [0] = 32000, | |
17 | [1] = 44100, | |
18 | [2] = 48000, | |
19 | /* mode 1 */ | |
20 | [3] = 88200, | |
21 | [4] = 96000, | |
22 | /* mode 2 */ | |
23 | [5] = 176400, | |
24 | [6] = 192000, | |
25 | }; | |
26 | ||
27 | int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, | |
28 | unsigned int *mode) | |
29 | { | |
30 | int i; | |
31 | ||
32 | for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { | |
33 | if (!(dice->clock_caps & BIT(i))) | |
34 | continue; | |
35 | if (snd_dice_rates[i] != rate) | |
36 | continue; | |
37 | ||
38 | *mode = (i - 1) / 2; | |
39 | return 0; | |
40 | } | |
41 | return -EINVAL; | |
42 | } | |
43 | ||
9a02843c TS |
44 | static void release_resources(struct snd_dice *dice, |
45 | struct fw_iso_resources *resources) | |
6eb6c81e | 46 | { |
288a8d0c | 47 | unsigned int channel; |
6eb6c81e | 48 | |
288a8d0c TS |
49 | /* Reset channel number */ |
50 | channel = cpu_to_be32((u32)-1); | |
9a02843c TS |
51 | if (resources == &dice->tx_resources) |
52 | snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, | |
53 | &channel, 4); | |
54 | else | |
55 | snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, | |
56 | &channel, 4); | |
57 | ||
58 | fw_iso_resources_free(resources); | |
6eb6c81e TS |
59 | } |
60 | ||
9a02843c TS |
61 | static int keep_resources(struct snd_dice *dice, |
62 | struct fw_iso_resources *resources, | |
63 | unsigned int max_payload_bytes) | |
6eb6c81e | 64 | { |
288a8d0c | 65 | unsigned int channel; |
6eb6c81e TS |
66 | int err; |
67 | ||
9a02843c | 68 | err = fw_iso_resources_allocate(resources, max_payload_bytes, |
6eb6c81e | 69 | fw_parent_device(dice->unit)->max_speed); |
6eb6c81e | 70 | if (err < 0) |
288a8d0c | 71 | goto end; |
6eb6c81e | 72 | |
288a8d0c | 73 | /* Set channel number */ |
9a02843c TS |
74 | channel = cpu_to_be32(resources->channel); |
75 | if (resources == &dice->tx_resources) | |
76 | err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, | |
77 | &channel, 4); | |
78 | else | |
79 | err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, | |
80 | &channel, 4); | |
288a8d0c | 81 | if (err < 0) |
9a02843c | 82 | release_resources(dice, resources); |
288a8d0c | 83 | end: |
6eb6c81e TS |
84 | return err; |
85 | } | |
86 | ||
9a02843c | 87 | static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream) |
6eb6c81e | 88 | { |
9a02843c TS |
89 | amdtp_stream_pcm_abort(stream); |
90 | amdtp_stream_stop(stream); | |
c50fb91f | 91 | |
9a02843c TS |
92 | if (stream == &dice->tx_stream) |
93 | release_resources(dice, &dice->tx_resources); | |
94 | else | |
95 | release_resources(dice, &dice->rx_resources); | |
6eb6c81e TS |
96 | } |
97 | ||
9a02843c TS |
98 | static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, |
99 | unsigned int rate) | |
6eb6c81e | 100 | { |
9a02843c | 101 | struct fw_iso_resources *resources; |
288a8d0c TS |
102 | unsigned int i, mode, pcm_chs, midi_ports; |
103 | int err; | |
6eb6c81e | 104 | |
288a8d0c TS |
105 | err = snd_dice_stream_get_rate_mode(dice, rate, &mode); |
106 | if (err < 0) | |
107 | goto end; | |
9a02843c TS |
108 | if (stream == &dice->tx_stream) { |
109 | resources = &dice->tx_resources; | |
110 | pcm_chs = dice->tx_channels[mode]; | |
111 | midi_ports = dice->tx_midi_ports[mode]; | |
112 | } else { | |
113 | resources = &dice->rx_resources; | |
114 | pcm_chs = dice->rx_channels[mode]; | |
115 | midi_ports = dice->rx_midi_ports[mode]; | |
116 | } | |
6eb6c81e | 117 | |
288a8d0c TS |
118 | /* |
119 | * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in | |
120 | * one data block of AMDTP packet. Thus sampling transfer frequency is | |
121 | * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are | |
122 | * transferred on AMDTP packets at 96 kHz. Two successive samples of a | |
123 | * channel are stored consecutively in the packet. This quirk is called | |
124 | * as 'Dual Wire'. | |
125 | * For this quirk, blocking mode is required and PCM buffer size should | |
126 | * be aligned to SYT_INTERVAL. | |
127 | */ | |
288a8d0c TS |
128 | if (mode > 1) { |
129 | rate /= 2; | |
130 | pcm_chs *= 2; | |
9a02843c | 131 | stream->double_pcm_frames = true; |
288a8d0c | 132 | } else { |
9a02843c | 133 | stream->double_pcm_frames = false; |
288a8d0c | 134 | } |
6eb6c81e | 135 | |
9a02843c | 136 | amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); |
288a8d0c TS |
137 | if (mode > 1) { |
138 | pcm_chs /= 2; | |
6eb6c81e | 139 | |
288a8d0c | 140 | for (i = 0; i < pcm_chs; i++) { |
9a02843c TS |
141 | stream->pcm_positions[i] = i * 2; |
142 | stream->pcm_positions[i + pcm_chs] = i * 2 + 1; | |
288a8d0c TS |
143 | } |
144 | } | |
145 | ||
9a02843c TS |
146 | err = keep_resources(dice, resources, |
147 | amdtp_stream_get_max_payload(stream)); | |
288a8d0c TS |
148 | if (err < 0) { |
149 | dev_err(&dice->unit->device, | |
150 | "fail to keep isochronous resources\n"); | |
151 | goto end; | |
152 | } | |
153 | ||
9a02843c | 154 | err = amdtp_stream_start(stream, resources->channel, |
288a8d0c TS |
155 | fw_parent_device(dice->unit)->max_speed); |
156 | if (err < 0) | |
9a02843c | 157 | release_resources(dice, resources); |
288a8d0c TS |
158 | end: |
159 | return err; | |
160 | } | |
161 | ||
9a02843c TS |
162 | static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) |
163 | { | |
8fc01fc0 TS |
164 | u32 source; |
165 | int err; | |
166 | ||
167 | err = snd_dice_transaction_get_clock_source(dice, &source); | |
168 | if (err < 0) | |
169 | goto end; | |
170 | ||
171 | switch (source) { | |
172 | /* So-called 'SYT Match' modes, sync_to_syt value of packets received */ | |
173 | case CLOCK_SOURCE_ARX4: /* in 4th stream */ | |
174 | case CLOCK_SOURCE_ARX3: /* in 3rd stream */ | |
175 | case CLOCK_SOURCE_ARX2: /* in 2nd stream */ | |
176 | err = -ENOSYS; | |
177 | break; | |
178 | case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */ | |
179 | *sync_mode = 0; | |
180 | break; | |
181 | default: | |
182 | *sync_mode = CIP_SYNC_TO_DEVICE; | |
183 | break; | |
184 | } | |
185 | end: | |
186 | return err; | |
9a02843c TS |
187 | } |
188 | ||
189 | int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) | |
288a8d0c | 190 | { |
9a02843c | 191 | struct amdtp_stream *master, *slave; |
288a8d0c | 192 | unsigned int curr_rate; |
9a02843c TS |
193 | enum cip_flags sync_mode; |
194 | int err = 0; | |
195 | ||
196 | if (dice->substreams_counter == 0) | |
197 | goto end; | |
198 | ||
199 | err = get_sync_mode(dice, &sync_mode); | |
200 | if (err < 0) | |
201 | goto end; | |
202 | if (sync_mode == CIP_SYNC_TO_DEVICE) { | |
203 | master = &dice->tx_stream; | |
204 | slave = &dice->rx_stream; | |
205 | } else { | |
206 | master = &dice->rx_stream; | |
207 | slave = &dice->tx_stream; | |
208 | } | |
288a8d0c TS |
209 | |
210 | /* Some packet queueing errors. */ | |
9a02843c TS |
211 | if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) |
212 | stop_stream(dice, master); | |
288a8d0c TS |
213 | |
214 | /* Stop stream if rate is different. */ | |
215 | err = snd_dice_transaction_get_rate(dice, &curr_rate); | |
216 | if (err < 0) { | |
217 | dev_err(&dice->unit->device, | |
218 | "fail to get sampling rate\n"); | |
219 | goto end; | |
220 | } | |
a113ff88 TS |
221 | if (rate == 0) |
222 | rate = curr_rate; | |
288a8d0c | 223 | if (rate != curr_rate) |
9a02843c | 224 | stop_stream(dice, master); |
288a8d0c | 225 | |
9a02843c TS |
226 | if (!amdtp_stream_running(master)) { |
227 | stop_stream(dice, slave); | |
288a8d0c TS |
228 | snd_dice_transaction_clear_enable(dice); |
229 | ||
9a02843c TS |
230 | amdtp_stream_set_sync(sync_mode, master, slave); |
231 | ||
288a8d0c TS |
232 | err = snd_dice_transaction_set_rate(dice, rate); |
233 | if (err < 0) { | |
234 | dev_err(&dice->unit->device, | |
235 | "fail to set sampling rate\n"); | |
236 | goto end; | |
237 | } | |
238 | ||
9a02843c TS |
239 | /* Start both streams. */ |
240 | err = start_stream(dice, master, rate); | |
241 | if (err < 0) { | |
242 | dev_err(&dice->unit->device, | |
243 | "fail to start AMDTP master stream\n"); | |
244 | goto end; | |
245 | } | |
246 | err = start_stream(dice, slave, rate); | |
288a8d0c TS |
247 | if (err < 0) { |
248 | dev_err(&dice->unit->device, | |
9a02843c TS |
249 | "fail to start AMDTP slave stream\n"); |
250 | stop_stream(dice, master); | |
288a8d0c TS |
251 | goto end; |
252 | } | |
253 | err = snd_dice_transaction_set_enable(dice); | |
254 | if (err < 0) { | |
255 | dev_err(&dice->unit->device, | |
256 | "fail to enable interface\n"); | |
9a02843c TS |
257 | stop_stream(dice, master); |
258 | stop_stream(dice, slave); | |
288a8d0c TS |
259 | goto end; |
260 | } | |
261 | ||
9a02843c TS |
262 | /* Wait first callbacks */ |
263 | if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) || | |
264 | !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { | |
288a8d0c | 265 | snd_dice_transaction_clear_enable(dice); |
9a02843c TS |
266 | stop_stream(dice, master); |
267 | stop_stream(dice, slave); | |
288a8d0c TS |
268 | err = -ETIMEDOUT; |
269 | } | |
270 | } | |
271 | end: | |
272 | return err; | |
273 | } | |
274 | ||
9a02843c | 275 | void snd_dice_stream_stop_duplex(struct snd_dice *dice) |
288a8d0c | 276 | { |
9a02843c TS |
277 | if (dice->substreams_counter > 0) |
278 | return; | |
279 | ||
288a8d0c | 280 | snd_dice_transaction_clear_enable(dice); |
9a02843c TS |
281 | |
282 | stop_stream(dice, &dice->tx_stream); | |
283 | stop_stream(dice, &dice->rx_stream); | |
6eb6c81e TS |
284 | } |
285 | ||
9a02843c | 286 | static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) |
6eb6c81e TS |
287 | { |
288 | int err; | |
9a02843c TS |
289 | struct fw_iso_resources *resources; |
290 | enum amdtp_stream_direction dir; | |
291 | ||
292 | if (stream == &dice->tx_stream) { | |
293 | resources = &dice->tx_resources; | |
294 | dir = AMDTP_IN_STREAM; | |
295 | } else { | |
296 | resources = &dice->rx_resources; | |
297 | dir = AMDTP_OUT_STREAM; | |
298 | } | |
6eb6c81e | 299 | |
9a02843c | 300 | err = fw_iso_resources_init(resources, dice->unit); |
6eb6c81e TS |
301 | if (err < 0) |
302 | goto end; | |
9a02843c | 303 | resources->channels_mask = 0x00000000ffffffffuLL; |
6eb6c81e | 304 | |
9a02843c TS |
305 | err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING); |
306 | if (err < 0) { | |
307 | amdtp_stream_destroy(stream); | |
308 | fw_iso_resources_destroy(resources); | |
309 | } | |
310 | end: | |
311 | return err; | |
312 | } | |
313 | ||
d23c2cc4 TS |
314 | /* |
315 | * This function should be called before starting streams or after stopping | |
316 | * streams. | |
317 | */ | |
9a02843c TS |
318 | static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) |
319 | { | |
d23c2cc4 | 320 | struct fw_iso_resources *resources; |
9a02843c TS |
321 | |
322 | if (stream == &dice->tx_stream) | |
d23c2cc4 | 323 | resources = &dice->tx_resources; |
9a02843c | 324 | else |
d23c2cc4 TS |
325 | resources = &dice->rx_resources; |
326 | ||
327 | amdtp_stream_destroy(stream); | |
328 | fw_iso_resources_destroy(resources); | |
9a02843c TS |
329 | } |
330 | ||
331 | int snd_dice_stream_init_duplex(struct snd_dice *dice) | |
332 | { | |
333 | int err; | |
334 | ||
335 | dice->substreams_counter = 0; | |
336 | ||
337 | err = init_stream(dice, &dice->tx_stream); | |
6eb6c81e | 338 | if (err < 0) |
9a02843c | 339 | goto end; |
6eb6c81e | 340 | |
9a02843c | 341 | err = init_stream(dice, &dice->rx_stream); |
d23c2cc4 TS |
342 | if (err < 0) |
343 | destroy_stream(dice, &dice->tx_stream); | |
6eb6c81e TS |
344 | end: |
345 | return err; | |
6eb6c81e TS |
346 | } |
347 | ||
9a02843c | 348 | void snd_dice_stream_destroy_duplex(struct snd_dice *dice) |
6eb6c81e | 349 | { |
288a8d0c | 350 | snd_dice_transaction_clear_enable(dice); |
9a02843c | 351 | |
9a02843c | 352 | destroy_stream(dice, &dice->tx_stream); |
9a02843c TS |
353 | destroy_stream(dice, &dice->rx_stream); |
354 | ||
355 | dice->substreams_counter = 0; | |
6eb6c81e TS |
356 | } |
357 | ||
9a02843c | 358 | void snd_dice_stream_update_duplex(struct snd_dice *dice) |
6eb6c81e TS |
359 | { |
360 | /* | |
361 | * On a bus reset, the DICE firmware disables streaming and then goes | |
362 | * off contemplating its own navel for hundreds of milliseconds before | |
363 | * it can react to any of our attempts to reenable streaming. This | |
364 | * means that we lose synchronization anyway, so we force our streams | |
365 | * to stop so that the application can restart them in an orderly | |
366 | * manner. | |
367 | */ | |
368 | dice->global_enabled = false; | |
369 | ||
9a02843c TS |
370 | stop_stream(dice, &dice->rx_stream); |
371 | stop_stream(dice, &dice->tx_stream); | |
288a8d0c | 372 | |
6eb6c81e | 373 | fw_iso_resources_update(&dice->rx_resources); |
9a02843c | 374 | fw_iso_resources_update(&dice->tx_resources); |
6eb6c81e TS |
375 | } |
376 | ||
377 | static void dice_lock_changed(struct snd_dice *dice) | |
378 | { | |
379 | dice->dev_lock_changed = true; | |
380 | wake_up(&dice->hwdep_wait); | |
381 | } | |
382 | ||
383 | int snd_dice_stream_lock_try(struct snd_dice *dice) | |
384 | { | |
385 | int err; | |
386 | ||
387 | spin_lock_irq(&dice->lock); | |
388 | ||
389 | if (dice->dev_lock_count < 0) { | |
390 | err = -EBUSY; | |
391 | goto out; | |
392 | } | |
393 | ||
394 | if (dice->dev_lock_count++ == 0) | |
395 | dice_lock_changed(dice); | |
396 | err = 0; | |
397 | out: | |
398 | spin_unlock_irq(&dice->lock); | |
399 | return err; | |
400 | } | |
401 | ||
402 | void snd_dice_stream_lock_release(struct snd_dice *dice) | |
403 | { | |
404 | spin_lock_irq(&dice->lock); | |
405 | ||
406 | if (WARN_ON(dice->dev_lock_count <= 0)) | |
407 | goto out; | |
408 | ||
409 | if (--dice->dev_lock_count == 0) | |
410 | dice_lock_changed(dice); | |
411 | out: | |
412 | spin_unlock_irq(&dice->lock); | |
413 | } |