ALSA: control: use counting semaphore as write lock for ELEM_WRITE operation
[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{
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, &reg,
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, &reg,
77 sizeof(reg));
78}
79
80static 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, &reg,
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, &reg,
101 sizeof(reg));
102
103 fw_iso_resources_free(&motu->tx_resources);
104 fw_iso_resources_free(&motu->rx_resources);
105}
106
107static 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
131static 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
144static 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, &reg,
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, &reg,
167 sizeof(reg));
168}
169
170int 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
258void 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
272static 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
299static 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
317int 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 */
336void 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
345static void motu_lock_changed(struct snd_motu *motu)
346{
347 motu->dev_lock_changed = true;
348 wake_up(&motu->hwdep_wait);
349}
350
351int 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;
365out:
366 spin_unlock_irq(&motu->lock);
367 return err;
368}
369
370void 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);
379out:
380 spin_unlock_irq(&motu->lock);
381}