summaryrefslogtreecommitdiffstats
path: root/sound/firewire/motu
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2017-03-22 08:30:20 -0400
committerTakashi Iwai <tiwai@suse.de>2017-03-28 06:33:34 -0400
commit9b2bb4f2f4a213a768a84fa25c14be54844f5bb6 (patch)
treee21d07ea7d4fce43739fd0236b95e41f9e962330 /sound/firewire/motu
parent2e76701bbb1fbe55f7d8538ae7f6869070eb3446 (diff)
ALSA: firewire-motu: add stream management functionality
This commit adds a functionality to manage packet streaming for MOTU FireWire series. The streaming is not controlled by CMP, thus against IEC 61883-1. Write transaction to certain addresses start/stop packet streaming. Transactions to 0x'ffff'f000'0b00 results in isochronous channel number for both directions and starting/stopping transmission of packets. The isochronous channel number is represented in 6 bit field, thus units can identify the channels up to 64, as IEEE 1394 bus specification described. Transactions to 0x'ffff'f000'0b10 results in packet format for both directions and transmission speed. When each of data block includes fixed part of data chunks only, corresponding flags stand. When bus reset occurs, the units continue to transmit packets with non-contiguous data block counter. This causes discontinuity detection in packet streaming engine and ALSA PCM applications receives EPIPE from any I/O operation. In this case, typical applications manage to recover corresponding PCM substream. This behaviour is kicked much earlier than callback of bus reset handler by Linux FireWire subsystem, therefore status of packet streaming is not changed in the handler. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/firewire/motu')
-rw-r--r--sound/firewire/motu/Makefile2
-rw-r--r--sound/firewire/motu/motu-stream.c340
-rw-r--r--sound/firewire/motu/motu.c5
-rw-r--r--sound/firewire/motu/motu.h10
4 files changed, 356 insertions, 1 deletions
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 03b07694df66..504a4f9dea6d 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,2 +1,2 @@
1snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o 1snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o
2obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o 2obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
new file mode 100644
index 000000000000..9aa698fc8da2
--- /dev/null
+++ b/sound/firewire/motu/motu-stream.c
@@ -0,0 +1,340 @@
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
29static int start_both_streams(struct snd_motu *motu, unsigned int rate)
30{
31 __be32 reg;
32 u32 data;
33 int err;
34
35 /* Set packet formation to our packet streaming engine. */
36 err = amdtp_motu_set_parameters(&motu->rx_stream, rate,
37 &motu->rx_packet_formats);
38 if (err < 0)
39 return err;
40
41 err = amdtp_motu_set_parameters(&motu->tx_stream, rate,
42 &motu->tx_packet_formats);
43 if (err < 0)
44 return err;
45
46
47 /* Get isochronous resources on the bus. */
48 err = fw_iso_resources_allocate(&motu->rx_resources,
49 amdtp_stream_get_max_payload(&motu->rx_stream),
50 fw_parent_device(motu->unit)->max_speed);
51 if (err < 0)
52 return err;
53
54 err = fw_iso_resources_allocate(&motu->tx_resources,
55 amdtp_stream_get_max_payload(&motu->tx_stream),
56 fw_parent_device(motu->unit)->max_speed);
57 if (err < 0)
58 return err;
59
60 /* Configure the unit to start isochronous communication. */
61 err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
62 sizeof(reg));
63 if (err < 0)
64 return err;
65 data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK;
66
67 data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED |
68 (motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) |
69 CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED |
70 (motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT);
71
72 reg = cpu_to_be32(data);
73 return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
74 sizeof(reg));
75}
76
77static void stop_both_streams(struct snd_motu *motu)
78{
79 __be32 reg;
80 u32 data;
81 int err;
82
83 err = motu->spec->protocol->switch_fetching_mode(motu, false);
84 if (err < 0)
85 return;
86
87 err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
88 sizeof(reg));
89 if (err < 0)
90 return;
91 data = be32_to_cpu(reg);
92
93 data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED);
94 data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE;
95
96 reg = cpu_to_be32(data);
97 snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
98 sizeof(reg));
99
100 fw_iso_resources_free(&motu->tx_resources);
101 fw_iso_resources_free(&motu->rx_resources);
102}
103
104static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
105{
106 struct fw_iso_resources *resources;
107 int err;
108
109 if (stream == &motu->rx_stream)
110 resources = &motu->rx_resources;
111 else
112 resources = &motu->tx_resources;
113
114 err = amdtp_stream_start(stream, resources->channel,
115 fw_parent_device(motu->unit)->max_speed);
116 if (err < 0)
117 return err;
118
119 if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
120 amdtp_stream_stop(stream);
121 fw_iso_resources_free(resources);
122 return -ETIMEDOUT;
123 }
124
125 return 0;
126}
127
128static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
129{
130 struct fw_iso_resources *resources;
131
132 if (stream == &motu->rx_stream)
133 resources = &motu->rx_resources;
134 else
135 resources = &motu->tx_resources;
136
137 amdtp_stream_stop(stream);
138 fw_iso_resources_free(resources);
139}
140
141static int ensure_packet_formats(struct snd_motu *motu)
142{
143 __be32 reg;
144 u32 data;
145 int err;
146
147 err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, &reg,
148 sizeof(reg));
149 if (err < 0)
150 return err;
151 data = be32_to_cpu(reg);
152
153 data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
154 RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
155 TX_PACKET_TRANSMISSION_SPEED_MASK);
156 if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0)
157 data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
158 if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0)
159 data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
160 data |= fw_parent_device(motu->unit)->max_speed;
161
162 reg = cpu_to_be32(data);
163 return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, &reg,
164 sizeof(reg));
165}
166
167int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
168{
169 const struct snd_motu_protocol *protocol = motu->spec->protocol;
170 unsigned int curr_rate;
171 int err = 0;
172
173 if (motu->capture_substreams == 0 && motu->playback_substreams == 0)
174 return 0;
175
176 /* Some packet queueing errors. */
177 if (amdtp_streaming_error(&motu->rx_stream) ||
178 amdtp_streaming_error(&motu->tx_stream)) {
179 amdtp_stream_stop(&motu->rx_stream);
180 amdtp_stream_stop(&motu->tx_stream);
181 stop_both_streams(motu);
182 }
183
184 err = protocol->cache_packet_formats(motu);
185 if (err < 0)
186 return err;
187
188 /* Stop stream if rate is different. */
189 err = protocol->get_clock_rate(motu, &curr_rate);
190 if (err < 0) {
191 dev_err(&motu->unit->device,
192 "fail to get sampling rate: %d\n", err);
193 return err;
194 }
195 if (rate == 0)
196 rate = curr_rate;
197 if (rate != curr_rate) {
198 amdtp_stream_stop(&motu->rx_stream);
199 amdtp_stream_stop(&motu->tx_stream);
200 stop_both_streams(motu);
201 }
202
203 if (!amdtp_stream_running(&motu->rx_stream)) {
204 err = protocol->set_clock_rate(motu, rate);
205 if (err < 0) {
206 dev_err(&motu->unit->device,
207 "fail to set sampling rate: %d\n", err);
208 return err;
209 }
210
211 err = ensure_packet_formats(motu);
212 if (err < 0)
213 return err;
214
215 err = start_both_streams(motu, rate);
216 if (err < 0) {
217 dev_err(&motu->unit->device,
218 "fail to start isochronous comm: %d\n", err);
219 stop_both_streams(motu);
220 return err;
221 }
222
223 err = start_isoc_ctx(motu, &motu->rx_stream);
224 if (err < 0) {
225 dev_err(&motu->unit->device,
226 "fail to start IT context: %d\n", err);
227 stop_both_streams(motu);
228 return err;
229 }
230
231 err = protocol->switch_fetching_mode(motu, true);
232 if (err < 0) {
233 dev_err(&motu->unit->device,
234 "fail to enable frame fetching: %d\n", err);
235 stop_both_streams(motu);
236 return err;
237 }
238 }
239
240 if (!amdtp_stream_running(&motu->tx_stream) &&
241 motu->capture_substreams > 0) {
242 err = start_isoc_ctx(motu, &motu->tx_stream);
243 if (err < 0) {
244 dev_err(&motu->unit->device,
245 "fail to start IR context: %d", err);
246 amdtp_stream_stop(&motu->rx_stream);
247 stop_both_streams(motu);
248 return err;
249 }
250 }
251
252 return 0;
253}
254
255void snd_motu_stream_stop_duplex(struct snd_motu *motu)
256{
257 if (motu->capture_substreams == 0) {
258 if (amdtp_stream_running(&motu->tx_stream))
259 stop_isoc_ctx(motu, &motu->tx_stream);
260
261 if (motu->playback_substreams == 0) {
262 if (amdtp_stream_running(&motu->rx_stream))
263 stop_isoc_ctx(motu, &motu->rx_stream);
264 stop_both_streams(motu);
265 }
266 }
267}
268
269static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir)
270{
271 int err;
272 struct amdtp_stream *stream;
273 struct fw_iso_resources *resources;
274
275 if (dir == AMDTP_IN_STREAM) {
276 stream = &motu->tx_stream;
277 resources = &motu->tx_resources;
278 } else {
279 stream = &motu->rx_stream;
280 resources = &motu->rx_resources;
281 }
282
283 err = fw_iso_resources_init(resources, motu->unit);
284 if (err < 0)
285 return err;
286
287 err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol);
288 if (err < 0) {
289 amdtp_stream_destroy(stream);
290 fw_iso_resources_destroy(resources);
291 }
292
293 return err;
294}
295
296static void destroy_stream(struct snd_motu *motu,
297 enum amdtp_stream_direction dir)
298{
299 struct amdtp_stream *stream;
300 struct fw_iso_resources *resources;
301
302 if (dir == AMDTP_IN_STREAM) {
303 stream = &motu->tx_stream;
304 resources = &motu->tx_resources;
305 } else {
306 stream = &motu->rx_stream;
307 resources = &motu->rx_resources;
308 }
309
310 amdtp_stream_destroy(stream);
311 fw_iso_resources_free(resources);
312}
313
314int snd_motu_stream_init_duplex(struct snd_motu *motu)
315{
316 int err;
317
318 err = init_stream(motu, AMDTP_IN_STREAM);
319 if (err < 0)
320 return err;
321
322 err = init_stream(motu, AMDTP_OUT_STREAM);
323 if (err < 0)
324 destroy_stream(motu, AMDTP_IN_STREAM);
325
326 return err;
327}
328
329/*
330 * This function should be called before starting streams or after stopping
331 * streams.
332 */
333void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
334{
335 destroy_stream(motu, AMDTP_IN_STREAM);
336 destroy_stream(motu, AMDTP_OUT_STREAM);
337
338 motu->playback_substreams = 0;
339 motu->capture_substreams = 0;
340}
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index db6014c2f16d..9d52238d898e 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -56,6 +56,7 @@ static void motu_free(struct snd_motu *motu)
56{ 56{
57 snd_motu_transaction_unregister(motu); 57 snd_motu_transaction_unregister(motu);
58 58
59 snd_motu_stream_destroy_duplex(motu);
59 fw_unit_put(motu->unit); 60 fw_unit_put(motu->unit);
60 61
61 mutex_destroy(&motu->mutex); 62 mutex_destroy(&motu->mutex);
@@ -92,6 +93,10 @@ static void do_registration(struct work_struct *work)
92 if (err < 0) 93 if (err < 0)
93 goto error; 94 goto error;
94 95
96 err = snd_motu_stream_init_duplex(motu);
97 if (err < 0)
98 goto error;
99
95 err = snd_card_register(motu->card); 100 err = snd_card_register(motu->card);
96 if (err < 0) 101 if (err < 0)
97 goto error; 102 goto error;
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index ed1d779c0dcc..90d274167a4a 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -23,6 +23,7 @@
23 23
24#include "../lib.h" 24#include "../lib.h"
25#include "../amdtp-stream.h" 25#include "../amdtp-stream.h"
26#include "../iso-resources.h"
26 27
27struct snd_motu_packet_format { 28struct snd_motu_packet_format {
28 unsigned char pcm_byte_offset; 29 unsigned char pcm_byte_offset;
@@ -48,6 +49,10 @@ struct snd_motu {
48 struct snd_motu_packet_format rx_packet_formats; 49 struct snd_motu_packet_format rx_packet_formats;
49 struct amdtp_stream tx_stream; 50 struct amdtp_stream tx_stream;
50 struct amdtp_stream rx_stream; 51 struct amdtp_stream rx_stream;
52 struct fw_iso_resources tx_resources;
53 struct fw_iso_resources rx_resources;
54 unsigned int capture_substreams;
55 unsigned int playback_substreams;
51 56
52 /* For notification. */ 57 /* For notification. */
53 struct fw_address_handler async_handler; 58 struct fw_address_handler async_handler;
@@ -118,4 +123,9 @@ int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
118int snd_motu_transaction_register(struct snd_motu *motu); 123int snd_motu_transaction_register(struct snd_motu *motu);
119int snd_motu_transaction_reregister(struct snd_motu *motu); 124int snd_motu_transaction_reregister(struct snd_motu *motu);
120void snd_motu_transaction_unregister(struct snd_motu *motu); 125void snd_motu_transaction_unregister(struct snd_motu *motu);
126
127int snd_motu_stream_init_duplex(struct snd_motu *motu);
128void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
129int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate);
130void snd_motu_stream_stop_duplex(struct snd_motu *motu);
121#endif 131#endif