1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * tascam-stream.c - a part of driver for TASCAM FireWire series |
4 | * |
5 | * Copyright (c) 2015 Takashi Sakamoto |
6 | */ |
7 | |
8 | #include <linux/delay.h> |
9 | #include "tascam.h" |
10 | |
11 | #define CLOCK_STATUS_MASK 0xffff0000 |
12 | #define CLOCK_CONFIG_MASK 0x0000ffff |
13 | |
14 | #define READY_TIMEOUT_MS 4000 |
15 | |
16 | static int get_clock(struct snd_tscm *tscm, u32 *data) |
17 | { |
18 | int trial = 0; |
19 | __be32 reg; |
20 | int err; |
21 | |
22 | while (trial++ < 5) { |
23 | err = snd_fw_transaction(unit: tscm->unit, TCODE_READ_QUADLET_REQUEST, |
24 | TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS, |
25 | buffer: ®, length: sizeof(reg), flags: 0); |
26 | if (err < 0) |
27 | return err; |
28 | |
29 | *data = be32_to_cpu(reg); |
30 | if (*data & CLOCK_STATUS_MASK) |
31 | break; |
32 | |
33 | // In intermediate state after changing clock status. |
34 | msleep(msecs: 50); |
35 | } |
36 | |
37 | // Still in the intermediate state. |
38 | if (trial >= 5) |
39 | return -EAGAIN; |
40 | |
41 | return 0; |
42 | } |
43 | |
44 | static int set_clock(struct snd_tscm *tscm, unsigned int rate, |
45 | enum snd_tscm_clock clock) |
46 | { |
47 | u32 data; |
48 | __be32 reg; |
49 | int err; |
50 | |
51 | err = get_clock(tscm, data: &data); |
52 | if (err < 0) |
53 | return err; |
54 | data &= CLOCK_CONFIG_MASK; |
55 | |
56 | if (rate > 0) { |
57 | data &= 0x000000ff; |
58 | /* Base rate. */ |
59 | if ((rate % 44100) == 0) { |
60 | data |= 0x00000100; |
61 | /* Multiplier. */ |
62 | if (rate / 44100 == 2) |
63 | data |= 0x00008000; |
64 | } else if ((rate % 48000) == 0) { |
65 | data |= 0x00000200; |
66 | /* Multiplier. */ |
67 | if (rate / 48000 == 2) |
68 | data |= 0x00008000; |
69 | } else { |
70 | return -EAGAIN; |
71 | } |
72 | } |
73 | |
74 | if (clock != INT_MAX) { |
75 | data &= 0x0000ff00; |
76 | data |= clock + 1; |
77 | } |
78 | |
79 | reg = cpu_to_be32(data); |
80 | |
81 | err = snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
82 | TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS, |
83 | buffer: ®, length: sizeof(reg), flags: 0); |
84 | if (err < 0) |
85 | return err; |
86 | |
87 | if (data & 0x00008000) |
88 | reg = cpu_to_be32(0x0000001a); |
89 | else |
90 | reg = cpu_to_be32(0x0000000d); |
91 | |
92 | return snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
93 | TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE, |
94 | buffer: ®, length: sizeof(reg), flags: 0); |
95 | } |
96 | |
97 | int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate) |
98 | { |
99 | u32 data; |
100 | int err; |
101 | |
102 | err = get_clock(tscm, data: &data); |
103 | if (err < 0) |
104 | return err; |
105 | |
106 | data = (data & 0xff000000) >> 24; |
107 | |
108 | /* Check base rate. */ |
109 | if ((data & 0x0f) == 0x01) |
110 | *rate = 44100; |
111 | else if ((data & 0x0f) == 0x02) |
112 | *rate = 48000; |
113 | else |
114 | return -EAGAIN; |
115 | |
116 | /* Check multiplier. */ |
117 | if ((data & 0xf0) == 0x80) |
118 | *rate *= 2; |
119 | else if ((data & 0xf0) != 0x00) |
120 | return -EAGAIN; |
121 | |
122 | return err; |
123 | } |
124 | |
125 | int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock) |
126 | { |
127 | u32 data; |
128 | int err; |
129 | |
130 | err = get_clock(tscm, data: &data); |
131 | if (err < 0) |
132 | return err; |
133 | |
134 | *clock = ((data & 0x00ff0000) >> 16) - 1; |
135 | if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT) |
136 | return -EIO; |
137 | |
138 | return 0; |
139 | } |
140 | |
141 | static int enable_data_channels(struct snd_tscm *tscm) |
142 | { |
143 | __be32 reg; |
144 | u32 data; |
145 | unsigned int i; |
146 | int err; |
147 | |
148 | data = 0; |
149 | for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i) |
150 | data |= BIT(i); |
151 | if (tscm->spec->has_adat) |
152 | data |= 0x0000ff00; |
153 | if (tscm->spec->has_spdif) |
154 | data |= 0x00030000; |
155 | |
156 | reg = cpu_to_be32(data); |
157 | err = snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
158 | TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS, |
159 | buffer: ®, length: sizeof(reg), flags: 0); |
160 | if (err < 0) |
161 | return err; |
162 | |
163 | data = 0; |
164 | for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i) |
165 | data |= BIT(i); |
166 | if (tscm->spec->has_adat) |
167 | data |= 0x0000ff00; |
168 | if (tscm->spec->has_spdif) |
169 | data |= 0x00030000; |
170 | |
171 | reg = cpu_to_be32(data); |
172 | return snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
173 | TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS, |
174 | buffer: ®, length: sizeof(reg), flags: 0); |
175 | } |
176 | |
177 | static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate) |
178 | { |
179 | __be32 reg; |
180 | int err; |
181 | |
182 | // Set an option for unknown purpose. |
183 | reg = cpu_to_be32(0x00200000); |
184 | err = snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
185 | TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION, |
186 | buffer: ®, length: sizeof(reg), flags: 0); |
187 | if (err < 0) |
188 | return err; |
189 | |
190 | return enable_data_channels(tscm); |
191 | } |
192 | |
193 | static void finish_session(struct snd_tscm *tscm) |
194 | { |
195 | __be32 reg; |
196 | |
197 | reg = 0; |
198 | snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
199 | TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING, |
200 | buffer: ®, length: sizeof(reg), flags: 0); |
201 | |
202 | reg = 0; |
203 | snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
204 | TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON, |
205 | buffer: ®, length: sizeof(reg), flags: 0); |
206 | |
207 | // Unregister channels. |
208 | reg = cpu_to_be32(0x00000000); |
209 | snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
210 | TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH, |
211 | buffer: ®, length: sizeof(reg), flags: 0); |
212 | reg = cpu_to_be32(0x00000000); |
213 | snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
214 | TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN, |
215 | buffer: ®, length: sizeof(reg), flags: 0); |
216 | reg = cpu_to_be32(0x00000000); |
217 | snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
218 | TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH, |
219 | buffer: ®, length: sizeof(reg), flags: 0); |
220 | } |
221 | |
222 | static int begin_session(struct snd_tscm *tscm) |
223 | { |
224 | __be32 reg; |
225 | int err; |
226 | |
227 | // Register the isochronous channel for transmitting stream. |
228 | reg = cpu_to_be32(tscm->tx_resources.channel); |
229 | err = snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
230 | TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH, |
231 | buffer: ®, length: sizeof(reg), flags: 0); |
232 | if (err < 0) |
233 | return err; |
234 | |
235 | // Unknown. |
236 | reg = cpu_to_be32(0x00000002); |
237 | err = snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
238 | TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN, |
239 | buffer: ®, length: sizeof(reg), flags: 0); |
240 | if (err < 0) |
241 | return err; |
242 | |
243 | // Register the isochronous channel for receiving stream. |
244 | reg = cpu_to_be32(tscm->rx_resources.channel); |
245 | err = snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
246 | TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH, |
247 | buffer: ®, length: sizeof(reg), flags: 0); |
248 | if (err < 0) |
249 | return err; |
250 | |
251 | reg = cpu_to_be32(0x00000001); |
252 | err = snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
253 | TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING, |
254 | buffer: ®, length: sizeof(reg), flags: 0); |
255 | if (err < 0) |
256 | return err; |
257 | |
258 | reg = cpu_to_be32(0x00000001); |
259 | err = snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
260 | TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON, |
261 | buffer: ®, length: sizeof(reg), flags: 0); |
262 | if (err < 0) |
263 | return err; |
264 | |
265 | // Set an option for unknown purpose. |
266 | reg = cpu_to_be32(0x00002000); |
267 | err = snd_fw_transaction(unit: tscm->unit, TCODE_WRITE_QUADLET_REQUEST, |
268 | TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION, |
269 | buffer: ®, length: sizeof(reg), flags: 0); |
270 | if (err < 0) |
271 | return err; |
272 | |
273 | // Start multiplexing PCM samples on packets. |
274 | reg = cpu_to_be32(0x00000001); |
275 | return snd_fw_transaction(unit: tscm->unit, |
276 | TCODE_WRITE_QUADLET_REQUEST, |
277 | TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON, |
278 | buffer: ®, length: sizeof(reg), flags: 0); |
279 | } |
280 | |
281 | static int keep_resources(struct snd_tscm *tscm, unsigned int rate, |
282 | struct amdtp_stream *stream) |
283 | { |
284 | struct fw_iso_resources *resources; |
285 | int err; |
286 | |
287 | if (stream == &tscm->tx_stream) |
288 | resources = &tscm->tx_resources; |
289 | else |
290 | resources = &tscm->rx_resources; |
291 | |
292 | err = amdtp_tscm_set_parameters(s: stream, rate); |
293 | if (err < 0) |
294 | return err; |
295 | |
296 | return fw_iso_resources_allocate(r: resources, |
297 | max_payload_bytes: amdtp_stream_get_max_payload(s: stream), |
298 | fw_parent_device(tscm->unit)->max_speed); |
299 | } |
300 | |
301 | static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s) |
302 | { |
303 | struct fw_iso_resources *resources; |
304 | enum amdtp_stream_direction dir; |
305 | unsigned int pcm_channels; |
306 | int err; |
307 | |
308 | if (s == &tscm->tx_stream) { |
309 | resources = &tscm->tx_resources; |
310 | dir = AMDTP_IN_STREAM; |
311 | pcm_channels = tscm->spec->pcm_capture_analog_channels; |
312 | } else { |
313 | resources = &tscm->rx_resources; |
314 | dir = AMDTP_OUT_STREAM; |
315 | pcm_channels = tscm->spec->pcm_playback_analog_channels; |
316 | } |
317 | |
318 | if (tscm->spec->has_adat) |
319 | pcm_channels += 8; |
320 | if (tscm->spec->has_spdif) |
321 | pcm_channels += 2; |
322 | |
323 | err = fw_iso_resources_init(r: resources, unit: tscm->unit); |
324 | if (err < 0) |
325 | return err; |
326 | |
327 | err = amdtp_tscm_init(s, unit: tscm->unit, dir, pcm_channels); |
328 | if (err < 0) |
329 | fw_iso_resources_free(r: resources); |
330 | |
331 | return err; |
332 | } |
333 | |
334 | static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s) |
335 | { |
336 | amdtp_stream_destroy(s); |
337 | |
338 | if (s == &tscm->tx_stream) |
339 | fw_iso_resources_destroy(r: &tscm->tx_resources); |
340 | else |
341 | fw_iso_resources_destroy(r: &tscm->rx_resources); |
342 | } |
343 | |
344 | int snd_tscm_stream_init_duplex(struct snd_tscm *tscm) |
345 | { |
346 | int err; |
347 | |
348 | err = init_stream(tscm, s: &tscm->tx_stream); |
349 | if (err < 0) |
350 | return err; |
351 | |
352 | err = init_stream(tscm, s: &tscm->rx_stream); |
353 | if (err < 0) { |
354 | destroy_stream(tscm, s: &tscm->tx_stream); |
355 | return err; |
356 | } |
357 | |
358 | err = amdtp_domain_init(d: &tscm->domain); |
359 | if (err < 0) { |
360 | destroy_stream(tscm, s: &tscm->tx_stream); |
361 | destroy_stream(tscm, s: &tscm->rx_stream); |
362 | } |
363 | |
364 | return err; |
365 | } |
366 | |
367 | // At bus reset, streaming is stopped and some registers are clear. |
368 | void snd_tscm_stream_update_duplex(struct snd_tscm *tscm) |
369 | { |
370 | amdtp_domain_stop(d: &tscm->domain); |
371 | |
372 | amdtp_stream_pcm_abort(s: &tscm->tx_stream); |
373 | amdtp_stream_pcm_abort(s: &tscm->rx_stream); |
374 | } |
375 | |
376 | // This function should be called before starting streams or after stopping |
377 | // streams. |
378 | void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm) |
379 | { |
380 | amdtp_domain_destroy(d: &tscm->domain); |
381 | |
382 | destroy_stream(tscm, s: &tscm->rx_stream); |
383 | destroy_stream(tscm, s: &tscm->tx_stream); |
384 | } |
385 | |
386 | int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate, |
387 | unsigned int frames_per_period, |
388 | unsigned int frames_per_buffer) |
389 | { |
390 | unsigned int curr_rate; |
391 | int err; |
392 | |
393 | err = snd_tscm_stream_get_rate(tscm, rate: &curr_rate); |
394 | if (err < 0) |
395 | return err; |
396 | |
397 | if (tscm->substreams_counter == 0 || rate != curr_rate) { |
398 | amdtp_domain_stop(d: &tscm->domain); |
399 | |
400 | finish_session(tscm); |
401 | |
402 | fw_iso_resources_free(r: &tscm->tx_resources); |
403 | fw_iso_resources_free(r: &tscm->rx_resources); |
404 | |
405 | err = set_clock(tscm, rate, INT_MAX); |
406 | if (err < 0) |
407 | return err; |
408 | |
409 | err = keep_resources(tscm, rate, stream: &tscm->tx_stream); |
410 | if (err < 0) |
411 | return err; |
412 | |
413 | err = keep_resources(tscm, rate, stream: &tscm->rx_stream); |
414 | if (err < 0) { |
415 | fw_iso_resources_free(r: &tscm->tx_resources); |
416 | return err; |
417 | } |
418 | |
419 | err = amdtp_domain_set_events_per_period(d: &tscm->domain, |
420 | events_per_period: frames_per_period, events_per_buffer: frames_per_buffer); |
421 | if (err < 0) { |
422 | fw_iso_resources_free(r: &tscm->tx_resources); |
423 | fw_iso_resources_free(r: &tscm->rx_resources); |
424 | return err; |
425 | } |
426 | |
427 | tscm->need_long_tx_init_skip = (rate != curr_rate); |
428 | } |
429 | |
430 | return 0; |
431 | } |
432 | |
433 | int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) |
434 | { |
435 | unsigned int generation = tscm->rx_resources.generation; |
436 | int err; |
437 | |
438 | if (tscm->substreams_counter == 0) |
439 | return 0; |
440 | |
441 | if (amdtp_streaming_error(s: &tscm->rx_stream) || |
442 | amdtp_streaming_error(s: &tscm->tx_stream)) { |
443 | amdtp_domain_stop(d: &tscm->domain); |
444 | finish_session(tscm); |
445 | } |
446 | |
447 | if (generation != fw_parent_device(tscm->unit)->card->generation) { |
448 | err = fw_iso_resources_update(r: &tscm->tx_resources); |
449 | if (err < 0) |
450 | goto error; |
451 | |
452 | err = fw_iso_resources_update(r: &tscm->rx_resources); |
453 | if (err < 0) |
454 | goto error; |
455 | } |
456 | |
457 | if (!amdtp_stream_running(s: &tscm->rx_stream)) { |
458 | int spd = fw_parent_device(tscm->unit)->max_speed; |
459 | unsigned int tx_init_skip_cycles; |
460 | |
461 | err = set_stream_formats(tscm, rate); |
462 | if (err < 0) |
463 | goto error; |
464 | |
465 | err = begin_session(tscm); |
466 | if (err < 0) |
467 | goto error; |
468 | |
469 | err = amdtp_domain_add_stream(d: &tscm->domain, s: &tscm->rx_stream, |
470 | channel: tscm->rx_resources.channel, speed: spd); |
471 | if (err < 0) |
472 | goto error; |
473 | |
474 | err = amdtp_domain_add_stream(d: &tscm->domain, s: &tscm->tx_stream, |
475 | channel: tscm->tx_resources.channel, speed: spd); |
476 | if (err < 0) |
477 | goto error; |
478 | |
479 | if (tscm->need_long_tx_init_skip) |
480 | tx_init_skip_cycles = 16000; |
481 | else |
482 | tx_init_skip_cycles = 0; |
483 | |
484 | // MEMO: Just after starting packet streaming, it transfers packets without any |
485 | // event. Enough after receiving the sequence of packets, it multiplexes events into |
486 | // the packet. However, just after changing sampling transfer frequency, it stops |
487 | // multiplexing during packet transmission. Enough after, it restarts multiplexing |
488 | // again. The device ignores presentation time expressed by the value of syt field |
489 | // of CIP header in received packets. The sequence of the number of data blocks per |
490 | // packet is important for media clock recovery. |
491 | err = amdtp_domain_start(d: &tscm->domain, tx_init_skip_cycles, replay_seq: true, replay_on_the_fly: true); |
492 | if (err < 0) |
493 | goto error; |
494 | |
495 | if (!amdtp_domain_wait_ready(d: &tscm->domain, READY_TIMEOUT_MS)) { |
496 | err = -ETIMEDOUT; |
497 | goto error; |
498 | } |
499 | } |
500 | |
501 | return 0; |
502 | error: |
503 | amdtp_domain_stop(d: &tscm->domain); |
504 | finish_session(tscm); |
505 | |
506 | return err; |
507 | } |
508 | |
509 | void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm) |
510 | { |
511 | if (tscm->substreams_counter == 0) { |
512 | amdtp_domain_stop(d: &tscm->domain); |
513 | finish_session(tscm); |
514 | |
515 | fw_iso_resources_free(r: &tscm->tx_resources); |
516 | fw_iso_resources_free(r: &tscm->rx_resources); |
517 | |
518 | tscm->need_long_tx_init_skip = false; |
519 | } |
520 | } |
521 | |
522 | void snd_tscm_stream_lock_changed(struct snd_tscm *tscm) |
523 | { |
524 | tscm->dev_lock_changed = true; |
525 | wake_up(&tscm->hwdep_wait); |
526 | } |
527 | |
528 | int snd_tscm_stream_lock_try(struct snd_tscm *tscm) |
529 | { |
530 | int err; |
531 | |
532 | spin_lock_irq(lock: &tscm->lock); |
533 | |
534 | /* user land lock this */ |
535 | if (tscm->dev_lock_count < 0) { |
536 | err = -EBUSY; |
537 | goto end; |
538 | } |
539 | |
540 | /* this is the first time */ |
541 | if (tscm->dev_lock_count++ == 0) |
542 | snd_tscm_stream_lock_changed(tscm); |
543 | err = 0; |
544 | end: |
545 | spin_unlock_irq(lock: &tscm->lock); |
546 | return err; |
547 | } |
548 | |
549 | void snd_tscm_stream_lock_release(struct snd_tscm *tscm) |
550 | { |
551 | spin_lock_irq(lock: &tscm->lock); |
552 | |
553 | if (WARN_ON(tscm->dev_lock_count <= 0)) |
554 | goto end; |
555 | if (--tscm->dev_lock_count == 0) |
556 | snd_tscm_stream_lock_changed(tscm); |
557 | end: |
558 | spin_unlock_irq(lock: &tscm->lock); |
559 | } |
560 | |