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