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 | ||
29 | static int start_both_streams(struct snd_motu *motu, unsigned int rate) | |
30 | { | |
9e796e7d | 31 | unsigned int midi_ports = 0; |
9b2bb4f2 TS |
32 | __be32 reg; |
33 | u32 data; | |
34 | int err; | |
35 | ||
9e796e7d TS |
36 | if (motu->spec->flags & SND_MOTU_SPEC_HAS_MIDI) |
37 | midi_ports = 1; | |
38 | ||
9b2bb4f2 | 39 | /* Set packet formation to our packet streaming engine. */ |
9e796e7d | 40 | err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports, |
9b2bb4f2 TS |
41 | &motu->rx_packet_formats); |
42 | if (err < 0) | |
43 | return err; | |
44 | ||
9e796e7d | 45 | err = amdtp_motu_set_parameters(&motu->tx_stream, rate, midi_ports, |
9b2bb4f2 TS |
46 | &motu->tx_packet_formats); |
47 | if (err < 0) | |
48 | return err; | |
49 | ||
9b2bb4f2 TS |
50 | /* Get isochronous resources on the bus. */ |
51 | err = fw_iso_resources_allocate(&motu->rx_resources, | |
52 | amdtp_stream_get_max_payload(&motu->rx_stream), | |
53 | fw_parent_device(motu->unit)->max_speed); | |
54 | if (err < 0) | |
55 | return err; | |
56 | ||
57 | err = fw_iso_resources_allocate(&motu->tx_resources, | |
58 | amdtp_stream_get_max_payload(&motu->tx_stream), | |
59 | fw_parent_device(motu->unit)->max_speed); | |
60 | if (err < 0) | |
61 | return err; | |
62 | ||
63 | /* Configure the unit to start isochronous communication. */ | |
64 | err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, ®, | |
65 | sizeof(reg)); | |
66 | if (err < 0) | |
67 | return err; | |
68 | data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK; | |
69 | ||
70 | data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED | | |
71 | (motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) | | |
72 | CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED | | |
73 | (motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT); | |
74 | ||
75 | reg = cpu_to_be32(data); | |
76 | return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, ®, | |
77 | sizeof(reg)); | |
78 | } | |
79 | ||
80 | static void stop_both_streams(struct snd_motu *motu) | |
81 | { | |
82 | __be32 reg; | |
83 | u32 data; | |
84 | int err; | |
85 | ||
86 | err = motu->spec->protocol->switch_fetching_mode(motu, false); | |
87 | if (err < 0) | |
88 | return; | |
89 | ||
90 | err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, ®, | |
91 | sizeof(reg)); | |
92 | if (err < 0) | |
93 | return; | |
94 | data = be32_to_cpu(reg); | |
95 | ||
96 | data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED); | |
97 | data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE; | |
98 | ||
99 | reg = cpu_to_be32(data); | |
100 | snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, ®, | |
101 | sizeof(reg)); | |
102 | ||
103 | fw_iso_resources_free(&motu->tx_resources); | |
104 | fw_iso_resources_free(&motu->rx_resources); | |
105 | } | |
106 | ||
107 | static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream) | |
108 | { | |
109 | struct fw_iso_resources *resources; | |
110 | int err; | |
111 | ||
112 | if (stream == &motu->rx_stream) | |
113 | resources = &motu->rx_resources; | |
114 | else | |
115 | resources = &motu->tx_resources; | |
116 | ||
117 | err = amdtp_stream_start(stream, resources->channel, | |
118 | fw_parent_device(motu->unit)->max_speed); | |
119 | if (err < 0) | |
120 | return err; | |
121 | ||
122 | if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { | |
123 | amdtp_stream_stop(stream); | |
124 | fw_iso_resources_free(resources); | |
125 | return -ETIMEDOUT; | |
126 | } | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
131 | static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream) | |
132 | { | |
133 | struct fw_iso_resources *resources; | |
134 | ||
135 | if (stream == &motu->rx_stream) | |
136 | resources = &motu->rx_resources; | |
137 | else | |
138 | resources = &motu->tx_resources; | |
139 | ||
140 | amdtp_stream_stop(stream); | |
141 | fw_iso_resources_free(resources); | |
142 | } | |
143 | ||
144 | static int ensure_packet_formats(struct snd_motu *motu) | |
145 | { | |
146 | __be32 reg; | |
147 | u32 data; | |
148 | int err; | |
149 | ||
150 | err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, ®, | |
151 | sizeof(reg)); | |
152 | if (err < 0) | |
153 | return err; | |
154 | data = be32_to_cpu(reg); | |
155 | ||
156 | data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS | | |
157 | RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS| | |
158 | TX_PACKET_TRANSMISSION_SPEED_MASK); | |
159 | if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0) | |
160 | data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS; | |
161 | if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0) | |
162 | data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS; | |
163 | data |= fw_parent_device(motu->unit)->max_speed; | |
164 | ||
165 | reg = cpu_to_be32(data); | |
166 | return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, ®, | |
167 | sizeof(reg)); | |
168 | } | |
169 | ||
170 | int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate) | |
171 | { | |
172 | const struct snd_motu_protocol *protocol = motu->spec->protocol; | |
173 | unsigned int curr_rate; | |
174 | int err = 0; | |
175 | ||
176 | if (motu->capture_substreams == 0 && motu->playback_substreams == 0) | |
177 | return 0; | |
178 | ||
179 | /* Some packet queueing errors. */ | |
180 | if (amdtp_streaming_error(&motu->rx_stream) || | |
181 | amdtp_streaming_error(&motu->tx_stream)) { | |
182 | amdtp_stream_stop(&motu->rx_stream); | |
183 | amdtp_stream_stop(&motu->tx_stream); | |
184 | stop_both_streams(motu); | |
185 | } | |
186 | ||
187 | err = protocol->cache_packet_formats(motu); | |
188 | if (err < 0) | |
189 | return err; | |
190 | ||
191 | /* Stop stream if rate is different. */ | |
192 | err = protocol->get_clock_rate(motu, &curr_rate); | |
193 | if (err < 0) { | |
194 | dev_err(&motu->unit->device, | |
195 | "fail to get sampling rate: %d\n", err); | |
196 | return err; | |
197 | } | |
198 | if (rate == 0) | |
199 | rate = curr_rate; | |
200 | if (rate != curr_rate) { | |
201 | amdtp_stream_stop(&motu->rx_stream); | |
202 | amdtp_stream_stop(&motu->tx_stream); | |
203 | stop_both_streams(motu); | |
204 | } | |
205 | ||
206 | if (!amdtp_stream_running(&motu->rx_stream)) { | |
207 | err = protocol->set_clock_rate(motu, rate); | |
208 | if (err < 0) { | |
209 | dev_err(&motu->unit->device, | |
210 | "fail to set sampling rate: %d\n", err); | |
211 | return err; | |
212 | } | |
213 | ||
214 | err = ensure_packet_formats(motu); | |
215 | if (err < 0) | |
216 | return err; | |
217 | ||
218 | err = start_both_streams(motu, rate); | |
219 | if (err < 0) { | |
220 | dev_err(&motu->unit->device, | |
221 | "fail to start isochronous comm: %d\n", err); | |
222 | stop_both_streams(motu); | |
223 | return err; | |
224 | } | |
225 | ||
226 | err = start_isoc_ctx(motu, &motu->rx_stream); | |
227 | if (err < 0) { | |
228 | dev_err(&motu->unit->device, | |
229 | "fail to start IT context: %d\n", err); | |
230 | stop_both_streams(motu); | |
231 | return err; | |
232 | } | |
233 | ||
234 | err = protocol->switch_fetching_mode(motu, true); | |
235 | if (err < 0) { | |
236 | dev_err(&motu->unit->device, | |
237 | "fail to enable frame fetching: %d\n", err); | |
238 | stop_both_streams(motu); | |
239 | return err; | |
240 | } | |
241 | } | |
242 | ||
243 | if (!amdtp_stream_running(&motu->tx_stream) && | |
244 | motu->capture_substreams > 0) { | |
245 | err = start_isoc_ctx(motu, &motu->tx_stream); | |
246 | if (err < 0) { | |
247 | dev_err(&motu->unit->device, | |
248 | "fail to start IR context: %d", err); | |
249 | amdtp_stream_stop(&motu->rx_stream); | |
250 | stop_both_streams(motu); | |
251 | return err; | |
252 | } | |
253 | } | |
254 | ||
255 | return 0; | |
256 | } | |
257 | ||
258 | void snd_motu_stream_stop_duplex(struct snd_motu *motu) | |
259 | { | |
260 | if (motu->capture_substreams == 0) { | |
261 | if (amdtp_stream_running(&motu->tx_stream)) | |
262 | stop_isoc_ctx(motu, &motu->tx_stream); | |
263 | ||
264 | if (motu->playback_substreams == 0) { | |
265 | if (amdtp_stream_running(&motu->rx_stream)) | |
266 | stop_isoc_ctx(motu, &motu->rx_stream); | |
267 | stop_both_streams(motu); | |
268 | } | |
269 | } | |
270 | } | |
271 | ||
272 | static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir) | |
273 | { | |
274 | int err; | |
275 | struct amdtp_stream *stream; | |
276 | struct fw_iso_resources *resources; | |
277 | ||
278 | if (dir == AMDTP_IN_STREAM) { | |
279 | stream = &motu->tx_stream; | |
280 | resources = &motu->tx_resources; | |
281 | } else { | |
282 | stream = &motu->rx_stream; | |
283 | resources = &motu->rx_resources; | |
284 | } | |
285 | ||
286 | err = fw_iso_resources_init(resources, motu->unit); | |
287 | if (err < 0) | |
288 | return err; | |
289 | ||
290 | err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol); | |
291 | if (err < 0) { | |
292 | amdtp_stream_destroy(stream); | |
293 | fw_iso_resources_destroy(resources); | |
294 | } | |
295 | ||
296 | return err; | |
297 | } | |
298 | ||
299 | static void destroy_stream(struct snd_motu *motu, | |
300 | enum amdtp_stream_direction dir) | |
301 | { | |
302 | struct amdtp_stream *stream; | |
303 | struct fw_iso_resources *resources; | |
304 | ||
305 | if (dir == AMDTP_IN_STREAM) { | |
306 | stream = &motu->tx_stream; | |
307 | resources = &motu->tx_resources; | |
308 | } else { | |
309 | stream = &motu->rx_stream; | |
310 | resources = &motu->rx_resources; | |
311 | } | |
312 | ||
313 | amdtp_stream_destroy(stream); | |
314 | fw_iso_resources_free(resources); | |
315 | } | |
316 | ||
317 | int snd_motu_stream_init_duplex(struct snd_motu *motu) | |
318 | { | |
319 | int err; | |
320 | ||
321 | err = init_stream(motu, AMDTP_IN_STREAM); | |
322 | if (err < 0) | |
323 | return err; | |
324 | ||
325 | err = init_stream(motu, AMDTP_OUT_STREAM); | |
326 | if (err < 0) | |
327 | destroy_stream(motu, AMDTP_IN_STREAM); | |
328 | ||
329 | return err; | |
330 | } | |
331 | ||
332 | /* | |
333 | * This function should be called before starting streams or after stopping | |
334 | * streams. | |
335 | */ | |
336 | void snd_motu_stream_destroy_duplex(struct snd_motu *motu) | |
337 | { | |
338 | destroy_stream(motu, AMDTP_IN_STREAM); | |
339 | destroy_stream(motu, AMDTP_OUT_STREAM); | |
340 | ||
341 | motu->playback_substreams = 0; | |
342 | motu->capture_substreams = 0; | |
343 | } | |
71c37977 TS |
344 | |
345 | static void motu_lock_changed(struct snd_motu *motu) | |
346 | { | |
347 | motu->dev_lock_changed = true; | |
348 | wake_up(&motu->hwdep_wait); | |
349 | } | |
350 | ||
351 | int snd_motu_stream_lock_try(struct snd_motu *motu) | |
352 | { | |
353 | int err; | |
354 | ||
355 | spin_lock_irq(&motu->lock); | |
356 | ||
357 | if (motu->dev_lock_count < 0) { | |
358 | err = -EBUSY; | |
359 | goto out; | |
360 | } | |
361 | ||
362 | if (motu->dev_lock_count++ == 0) | |
363 | motu_lock_changed(motu); | |
364 | err = 0; | |
365 | out: | |
366 | spin_unlock_irq(&motu->lock); | |
367 | return err; | |
368 | } | |
369 | ||
370 | void snd_motu_stream_lock_release(struct snd_motu *motu) | |
371 | { | |
372 | spin_lock_irq(&motu->lock); | |
373 | ||
374 | if (WARN_ON(motu->dev_lock_count <= 0)) | |
375 | goto out; | |
376 | ||
377 | if (--motu->dev_lock_count == 0) | |
378 | motu_lock_changed(motu); | |
379 | out: | |
380 | spin_unlock_irq(&motu->lock); | |
381 | } |