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 | { | |
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, ®, | |
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, ®, | |
74 | sizeof(reg)); | |
75 | } | |
76 | ||
77 | static 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, ®, | |
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, ®, | |
98 | sizeof(reg)); | |
99 | ||
100 | fw_iso_resources_free(&motu->tx_resources); | |
101 | fw_iso_resources_free(&motu->rx_resources); | |
102 | } | |
103 | ||
104 | static 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 | ||
128 | static 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 | ||
141 | static 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, ®, | |
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, ®, | |
164 | sizeof(reg)); | |
165 | } | |
166 | ||
167 | int 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 | ||
255 | void 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 | ||
269 | static 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 | ||
296 | static 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 | ||
314 | int 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 | */ | |
333 | void 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 | } |