1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * motu-stream.c - a part of driver for MOTU FireWire series |
4 | * |
5 | * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp> |
6 | */ |
7 | |
8 | #include "motu.h" |
9 | |
10 | #define READY_TIMEOUT_MS 200 |
11 | |
12 | #define ISOC_COMM_CONTROL_OFFSET 0x0b00 |
13 | #define ISOC_COMM_CONTROL_MASK 0xffff0000 |
14 | #define CHANGE_RX_ISOC_COMM_STATE 0x80000000 |
15 | #define RX_ISOC_COMM_IS_ACTIVATED 0x40000000 |
16 | #define RX_ISOC_COMM_CHANNEL_MASK 0x3f000000 |
17 | #define RX_ISOC_COMM_CHANNEL_SHIFT 24 |
18 | #define CHANGE_TX_ISOC_COMM_STATE 0x00800000 |
19 | #define TX_ISOC_COMM_IS_ACTIVATED 0x00400000 |
20 | #define TX_ISOC_COMM_CHANNEL_MASK 0x003f0000 |
21 | #define TX_ISOC_COMM_CHANNEL_SHIFT 16 |
22 | |
23 | #define PACKET_FORMAT_OFFSET 0x0b10 |
24 | #define TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000080 |
25 | #define RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040 |
26 | #define TX_PACKET_TRANSMISSION_SPEED_MASK 0x0000000f |
27 | |
28 | static int keep_resources(struct snd_motu *motu, unsigned int rate, |
29 | struct amdtp_stream *stream) |
30 | { |
31 | struct fw_iso_resources *resources; |
32 | struct snd_motu_packet_format *packet_format; |
33 | unsigned int midi_ports = 0; |
34 | int err; |
35 | |
36 | if (stream == &motu->rx_stream) { |
37 | resources = &motu->rx_resources; |
38 | packet_format = &motu->rx_packet_formats; |
39 | |
40 | if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) || |
41 | (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q)) |
42 | midi_ports = 1; |
43 | } else { |
44 | resources = &motu->tx_resources; |
45 | packet_format = &motu->tx_packet_formats; |
46 | |
47 | if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) || |
48 | (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q)) |
49 | midi_ports = 1; |
50 | } |
51 | |
52 | err = amdtp_motu_set_parameters(s: stream, rate, midi_ports, |
53 | formats: packet_format); |
54 | if (err < 0) |
55 | return err; |
56 | |
57 | return fw_iso_resources_allocate(r: resources, |
58 | max_payload_bytes: amdtp_stream_get_max_payload(s: stream), |
59 | fw_parent_device(motu->unit)->max_speed); |
60 | } |
61 | |
62 | static int begin_session(struct snd_motu *motu) |
63 | { |
64 | __be32 reg; |
65 | u32 data; |
66 | int err; |
67 | |
68 | // Configure the unit to start isochronous communication. |
69 | err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, reg: ®, |
70 | size: sizeof(reg)); |
71 | if (err < 0) |
72 | return err; |
73 | data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK; |
74 | |
75 | data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED | |
76 | (motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) | |
77 | CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED | |
78 | (motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT); |
79 | |
80 | reg = cpu_to_be32(data); |
81 | return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, reg: ®, |
82 | size: sizeof(reg)); |
83 | } |
84 | |
85 | static void finish_session(struct snd_motu *motu) |
86 | { |
87 | __be32 reg; |
88 | u32 data; |
89 | int err; |
90 | |
91 | err = snd_motu_protocol_switch_fetching_mode(motu, enable: false); |
92 | if (err < 0) |
93 | return; |
94 | |
95 | err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, reg: ®, |
96 | size: sizeof(reg)); |
97 | if (err < 0) |
98 | return; |
99 | data = be32_to_cpu(reg); |
100 | |
101 | data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED); |
102 | data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE; |
103 | |
104 | reg = cpu_to_be32(data); |
105 | snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, reg: ®, |
106 | size: sizeof(reg)); |
107 | } |
108 | |
109 | int snd_motu_stream_cache_packet_formats(struct snd_motu *motu) |
110 | { |
111 | int err; |
112 | |
113 | err = snd_motu_protocol_cache_packet_formats(motu); |
114 | if (err < 0) |
115 | return err; |
116 | |
117 | if (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) { |
118 | motu->tx_packet_formats.midi_flag_offset = 4; |
119 | motu->tx_packet_formats.midi_byte_offset = 6; |
120 | } else if (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q) { |
121 | motu->tx_packet_formats.midi_flag_offset = 8; |
122 | motu->tx_packet_formats.midi_byte_offset = 7; |
123 | } |
124 | |
125 | if (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) { |
126 | motu->rx_packet_formats.midi_flag_offset = 4; |
127 | motu->rx_packet_formats.midi_byte_offset = 6; |
128 | } else if (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q) { |
129 | motu->rx_packet_formats.midi_flag_offset = 8; |
130 | motu->rx_packet_formats.midi_byte_offset = 7; |
131 | } |
132 | |
133 | return 0; |
134 | } |
135 | |
136 | int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, |
137 | unsigned int frames_per_period, |
138 | unsigned int frames_per_buffer) |
139 | { |
140 | unsigned int curr_rate; |
141 | int err; |
142 | |
143 | err = snd_motu_protocol_get_clock_rate(motu, rate: &curr_rate); |
144 | if (err < 0) |
145 | return err; |
146 | if (rate == 0) |
147 | rate = curr_rate; |
148 | |
149 | if (motu->substreams_counter == 0 || curr_rate != rate) { |
150 | amdtp_domain_stop(d: &motu->domain); |
151 | finish_session(motu); |
152 | |
153 | fw_iso_resources_free(r: &motu->tx_resources); |
154 | fw_iso_resources_free(r: &motu->rx_resources); |
155 | |
156 | kfree(objp: motu->cache.event_offsets); |
157 | motu->cache.event_offsets = NULL; |
158 | |
159 | err = snd_motu_protocol_set_clock_rate(motu, rate); |
160 | if (err < 0) { |
161 | dev_err(&motu->unit->device, |
162 | "fail to set sampling rate: %d\n" , err); |
163 | return err; |
164 | } |
165 | |
166 | err = snd_motu_stream_cache_packet_formats(motu); |
167 | if (err < 0) |
168 | return err; |
169 | |
170 | err = keep_resources(motu, rate, stream: &motu->tx_stream); |
171 | if (err < 0) |
172 | return err; |
173 | |
174 | err = keep_resources(motu, rate, stream: &motu->rx_stream); |
175 | if (err < 0) { |
176 | fw_iso_resources_free(r: &motu->tx_resources); |
177 | return err; |
178 | } |
179 | |
180 | err = amdtp_domain_set_events_per_period(d: &motu->domain, |
181 | events_per_period: frames_per_period, events_per_buffer: frames_per_buffer); |
182 | if (err < 0) { |
183 | fw_iso_resources_free(r: &motu->tx_resources); |
184 | fw_iso_resources_free(r: &motu->rx_resources); |
185 | return err; |
186 | } |
187 | |
188 | motu->cache.size = motu->tx_stream.syt_interval * frames_per_buffer; |
189 | motu->cache.event_offsets = kcalloc(n: motu->cache.size, size: sizeof(*motu->cache.event_offsets), |
190 | GFP_KERNEL); |
191 | if (!motu->cache.event_offsets) { |
192 | fw_iso_resources_free(r: &motu->tx_resources); |
193 | fw_iso_resources_free(r: &motu->rx_resources); |
194 | return -ENOMEM; |
195 | } |
196 | } |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | static int ensure_packet_formats(struct snd_motu *motu) |
202 | { |
203 | __be32 reg; |
204 | u32 data; |
205 | int err; |
206 | |
207 | err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, reg: ®, |
208 | size: sizeof(reg)); |
209 | if (err < 0) |
210 | return err; |
211 | data = be32_to_cpu(reg); |
212 | |
213 | data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS | |
214 | RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS| |
215 | TX_PACKET_TRANSMISSION_SPEED_MASK); |
216 | if (motu->spec->tx_fixed_pcm_chunks[0] == motu->tx_packet_formats.pcm_chunks[0]) |
217 | data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS; |
218 | if (motu->spec->rx_fixed_pcm_chunks[0] == motu->rx_packet_formats.pcm_chunks[0]) |
219 | data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS; |
220 | data |= fw_parent_device(motu->unit)->max_speed; |
221 | |
222 | reg = cpu_to_be32(data); |
223 | return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, reg: ®, |
224 | size: sizeof(reg)); |
225 | } |
226 | |
227 | int snd_motu_stream_start_duplex(struct snd_motu *motu) |
228 | { |
229 | unsigned int generation = motu->rx_resources.generation; |
230 | int err = 0; |
231 | |
232 | if (motu->substreams_counter == 0) |
233 | return 0; |
234 | |
235 | if (amdtp_streaming_error(s: &motu->rx_stream) || |
236 | amdtp_streaming_error(s: &motu->tx_stream)) { |
237 | amdtp_domain_stop(d: &motu->domain); |
238 | finish_session(motu); |
239 | } |
240 | |
241 | if (generation != fw_parent_device(motu->unit)->card->generation) { |
242 | err = fw_iso_resources_update(r: &motu->rx_resources); |
243 | if (err < 0) |
244 | return err; |
245 | |
246 | err = fw_iso_resources_update(r: &motu->tx_resources); |
247 | if (err < 0) |
248 | return err; |
249 | } |
250 | |
251 | if (!amdtp_stream_running(s: &motu->rx_stream)) { |
252 | int spd = fw_parent_device(motu->unit)->max_speed; |
253 | |
254 | err = ensure_packet_formats(motu); |
255 | if (err < 0) |
256 | return err; |
257 | |
258 | if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) { |
259 | err = snd_motu_register_dsp_message_parser_init(motu); |
260 | if (err < 0) |
261 | return err; |
262 | } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) { |
263 | err = snd_motu_command_dsp_message_parser_init(motu, sfc: motu->tx_stream.sfc); |
264 | if (err < 0) |
265 | return err; |
266 | } |
267 | |
268 | err = begin_session(motu); |
269 | if (err < 0) { |
270 | dev_err(&motu->unit->device, |
271 | "fail to start isochronous comm: %d\n" , err); |
272 | goto stop_streams; |
273 | } |
274 | |
275 | err = amdtp_domain_add_stream(d: &motu->domain, s: &motu->tx_stream, |
276 | channel: motu->tx_resources.channel, speed: spd); |
277 | if (err < 0) |
278 | goto stop_streams; |
279 | |
280 | err = amdtp_domain_add_stream(d: &motu->domain, s: &motu->rx_stream, |
281 | channel: motu->rx_resources.channel, speed: spd); |
282 | if (err < 0) |
283 | goto stop_streams; |
284 | |
285 | motu->cache.tail = 0; |
286 | motu->cache.tx_cycle_count = UINT_MAX; |
287 | motu->cache.head = 0; |
288 | motu->cache.rx_cycle_count = UINT_MAX; |
289 | |
290 | // NOTE: The device requires both of replay; the sequence of the number of data |
291 | // blocks per packet, and the sequence of source packet header per data block as |
292 | // presentation time. |
293 | err = amdtp_domain_start(d: &motu->domain, tx_init_skip_cycles: 0, replay_seq: true, replay_on_the_fly: false); |
294 | if (err < 0) |
295 | goto stop_streams; |
296 | |
297 | if (!amdtp_domain_wait_ready(d: &motu->domain, READY_TIMEOUT_MS)) { |
298 | err = -ETIMEDOUT; |
299 | goto stop_streams; |
300 | } |
301 | |
302 | err = snd_motu_protocol_switch_fetching_mode(motu, enable: true); |
303 | if (err < 0) { |
304 | dev_err(&motu->unit->device, |
305 | "fail to enable frame fetching: %d\n" , err); |
306 | goto stop_streams; |
307 | } |
308 | } |
309 | |
310 | return 0; |
311 | |
312 | stop_streams: |
313 | amdtp_domain_stop(d: &motu->domain); |
314 | finish_session(motu); |
315 | return err; |
316 | } |
317 | |
318 | void snd_motu_stream_stop_duplex(struct snd_motu *motu) |
319 | { |
320 | if (motu->substreams_counter == 0) { |
321 | amdtp_domain_stop(d: &motu->domain); |
322 | finish_session(motu); |
323 | |
324 | fw_iso_resources_free(r: &motu->tx_resources); |
325 | fw_iso_resources_free(r: &motu->rx_resources); |
326 | |
327 | kfree(objp: motu->cache.event_offsets); |
328 | motu->cache.event_offsets = NULL; |
329 | } |
330 | } |
331 | |
332 | static int init_stream(struct snd_motu *motu, struct amdtp_stream *s) |
333 | { |
334 | struct fw_iso_resources *resources; |
335 | enum amdtp_stream_direction dir; |
336 | int err; |
337 | |
338 | if (s == &motu->tx_stream) { |
339 | resources = &motu->tx_resources; |
340 | dir = AMDTP_IN_STREAM; |
341 | } else { |
342 | resources = &motu->rx_resources; |
343 | dir = AMDTP_OUT_STREAM; |
344 | } |
345 | |
346 | err = fw_iso_resources_init(r: resources, unit: motu->unit); |
347 | if (err < 0) |
348 | return err; |
349 | |
350 | err = amdtp_motu_init(s, unit: motu->unit, dir, spec: motu->spec, cache: &motu->cache); |
351 | if (err < 0) |
352 | fw_iso_resources_destroy(r: resources); |
353 | |
354 | return err; |
355 | } |
356 | |
357 | static void destroy_stream(struct snd_motu *motu, struct amdtp_stream *s) |
358 | { |
359 | amdtp_stream_destroy(s); |
360 | |
361 | if (s == &motu->tx_stream) |
362 | fw_iso_resources_destroy(r: &motu->tx_resources); |
363 | else |
364 | fw_iso_resources_destroy(r: &motu->rx_resources); |
365 | } |
366 | |
367 | int snd_motu_stream_init_duplex(struct snd_motu *motu) |
368 | { |
369 | int err; |
370 | |
371 | err = init_stream(motu, s: &motu->tx_stream); |
372 | if (err < 0) |
373 | return err; |
374 | |
375 | err = init_stream(motu, s: &motu->rx_stream); |
376 | if (err < 0) { |
377 | destroy_stream(motu, s: &motu->tx_stream); |
378 | return err; |
379 | } |
380 | |
381 | err = amdtp_domain_init(d: &motu->domain); |
382 | if (err < 0) { |
383 | destroy_stream(motu, s: &motu->tx_stream); |
384 | destroy_stream(motu, s: &motu->rx_stream); |
385 | } |
386 | |
387 | return err; |
388 | } |
389 | |
390 | // This function should be called before starting streams or after stopping |
391 | // streams. |
392 | void snd_motu_stream_destroy_duplex(struct snd_motu *motu) |
393 | { |
394 | amdtp_domain_destroy(d: &motu->domain); |
395 | |
396 | destroy_stream(motu, s: &motu->rx_stream); |
397 | destroy_stream(motu, s: &motu->tx_stream); |
398 | |
399 | motu->substreams_counter = 0; |
400 | } |
401 | |
402 | static void motu_lock_changed(struct snd_motu *motu) |
403 | { |
404 | motu->dev_lock_changed = true; |
405 | wake_up(&motu->hwdep_wait); |
406 | } |
407 | |
408 | int snd_motu_stream_lock_try(struct snd_motu *motu) |
409 | { |
410 | int err; |
411 | |
412 | spin_lock_irq(lock: &motu->lock); |
413 | |
414 | if (motu->dev_lock_count < 0) { |
415 | err = -EBUSY; |
416 | goto out; |
417 | } |
418 | |
419 | if (motu->dev_lock_count++ == 0) |
420 | motu_lock_changed(motu); |
421 | err = 0; |
422 | out: |
423 | spin_unlock_irq(lock: &motu->lock); |
424 | return err; |
425 | } |
426 | |
427 | void snd_motu_stream_lock_release(struct snd_motu *motu) |
428 | { |
429 | spin_lock_irq(lock: &motu->lock); |
430 | |
431 | if (WARN_ON(motu->dev_lock_count <= 0)) |
432 | goto out; |
433 | |
434 | if (--motu->dev_lock_count == 0) |
435 | motu_lock_changed(motu); |
436 | out: |
437 | spin_unlock_irq(lock: &motu->lock); |
438 | } |
439 | |