ALSA: firewire-motu: code refactoring for condition to stop streaming
[linux-2.6-block.git] / sound / firewire / motu / motu-stream.c
CommitLineData
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
29static 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
63static 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, &reg,
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, &reg,
91 sizeof(reg));
92}
93
94static 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, &reg,
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, &reg,
115 sizeof(reg));
116
117 fw_iso_resources_free(&motu->tx_resources);
118 fw_iso_resources_free(&motu->rx_resources);
119}
120
121static 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
145static 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
158int 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
185static 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, &reg,
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, &reg,
208 sizeof(reg));
209}
210
211int 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
287stop_streams:
288 stop_both_streams(motu);
289 return err;
9b2bb4f2
TS
290}
291
292void 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
303static 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
330static 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
348int 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 */
367void 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
375static void motu_lock_changed(struct snd_motu *motu)
376{
377 motu->dev_lock_changed = true;
378 wake_up(&motu->hwdep_wait);
379}
380
381int 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;
395out:
396 spin_unlock_irq(&motu->lock);
397 return err;
398}
399
400void 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);
409out:
410 spin_unlock_irq(&motu->lock);
411}