ALSA: firewire-motu: add stream management functionality
[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
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}