x86/kprobes: Remove trampoline_handler() prototype
[sfrench/cifs-2.6.git] / sound / firewire / fireface / ff-stream.c
1 /*
2  * ff-stream.c - a part of driver for RME Fireface series
3  *
4  * Copyright (c) 2015-2017 Takashi Sakamoto
5  *
6  * Licensed under the terms of the GNU General Public License, version 2.
7  */
8
9 #include "ff.h"
10
11 #define CALLBACK_TIMEOUT_MS     200
12
13 static int get_rate_mode(unsigned int rate, unsigned int *mode)
14 {
15         int i;
16
17         for (i = 0; i < CIP_SFC_COUNT; i++) {
18                 if (amdtp_rate_table[i] == rate)
19                         break;
20         }
21
22         if (i == CIP_SFC_COUNT)
23                 return -EINVAL;
24
25         *mode = ((int)i - 1) / 2;
26
27         return 0;
28 }
29
30 /*
31  * Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
32  * we can allocate between 0 and 7 channel.
33  */
34 static int keep_resources(struct snd_ff *ff, unsigned int rate)
35 {
36         int mode;
37         int err;
38
39         err = get_rate_mode(rate, &mode);
40         if (err < 0)
41                 return err;
42
43         /* Keep resources for in-stream. */
44         err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
45                                       ff->spec->pcm_capture_channels[mode]);
46         if (err < 0)
47                 return err;
48         ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
49         err = fw_iso_resources_allocate(&ff->tx_resources,
50                         amdtp_stream_get_max_payload(&ff->tx_stream),
51                         fw_parent_device(ff->unit)->max_speed);
52         if (err < 0)
53                 return err;
54
55         /* Keep resources for out-stream. */
56         err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
57                                       ff->spec->pcm_playback_channels[mode]);
58         if (err < 0)
59                 return err;
60         ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
61         err = fw_iso_resources_allocate(&ff->rx_resources,
62                         amdtp_stream_get_max_payload(&ff->rx_stream),
63                         fw_parent_device(ff->unit)->max_speed);
64         if (err < 0)
65                 fw_iso_resources_free(&ff->tx_resources);
66
67         return err;
68 }
69
70 static void release_resources(struct snd_ff *ff)
71 {
72         fw_iso_resources_free(&ff->tx_resources);
73         fw_iso_resources_free(&ff->rx_resources);
74 }
75
76 static inline void finish_session(struct snd_ff *ff)
77 {
78         ff->spec->protocol->finish_session(ff);
79         ff->spec->protocol->switch_fetching_mode(ff, false);
80 }
81
82 static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
83 {
84         int err;
85         struct fw_iso_resources *resources;
86         struct amdtp_stream *stream;
87
88         if (dir == AMDTP_IN_STREAM) {
89                 resources = &ff->tx_resources;
90                 stream = &ff->tx_stream;
91         } else {
92                 resources = &ff->rx_resources;
93                 stream = &ff->rx_stream;
94         }
95
96         err = fw_iso_resources_init(resources, ff->unit);
97         if (err < 0)
98                 return err;
99
100         err = amdtp_ff_init(stream, ff->unit, dir);
101         if (err < 0)
102                 fw_iso_resources_destroy(resources);
103
104         return err;
105 }
106
107 static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
108 {
109         if (dir == AMDTP_IN_STREAM) {
110                 amdtp_stream_destroy(&ff->tx_stream);
111                 fw_iso_resources_destroy(&ff->tx_resources);
112         } else {
113                 amdtp_stream_destroy(&ff->rx_stream);
114                 fw_iso_resources_destroy(&ff->rx_resources);
115         }
116 }
117
118 int snd_ff_stream_init_duplex(struct snd_ff *ff)
119 {
120         int err;
121
122         err = init_stream(ff, AMDTP_OUT_STREAM);
123         if (err < 0)
124                 goto end;
125
126         err = init_stream(ff, AMDTP_IN_STREAM);
127         if (err < 0)
128                 destroy_stream(ff, AMDTP_OUT_STREAM);
129 end:
130         return err;
131 }
132
133 /*
134  * This function should be called before starting streams or after stopping
135  * streams.
136  */
137 void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
138 {
139         destroy_stream(ff, AMDTP_IN_STREAM);
140         destroy_stream(ff, AMDTP_OUT_STREAM);
141 }
142
143 int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
144 {
145         unsigned int curr_rate;
146         enum snd_ff_clock_src src;
147         int err;
148
149         if (ff->substreams_counter == 0)
150                 return 0;
151
152         err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
153         if (err < 0)
154                 return err;
155         if (curr_rate != rate ||
156             amdtp_streaming_error(&ff->tx_stream) ||
157             amdtp_streaming_error(&ff->rx_stream)) {
158                 finish_session(ff);
159
160                 amdtp_stream_stop(&ff->tx_stream);
161                 amdtp_stream_stop(&ff->rx_stream);
162
163                 release_resources(ff);
164         }
165
166         /*
167          * Regardless of current source of clock signal, drivers transfer some
168          * packets. Then, the device transfers packets.
169          */
170         if (!amdtp_stream_running(&ff->rx_stream)) {
171                 err = keep_resources(ff, rate);
172                 if (err < 0)
173                         goto error;
174
175                 err = ff->spec->protocol->begin_session(ff, rate);
176                 if (err < 0)
177                         goto error;
178
179                 err = amdtp_stream_start(&ff->rx_stream,
180                                          ff->rx_resources.channel,
181                                          fw_parent_device(ff->unit)->max_speed);
182                 if (err < 0)
183                         goto error;
184
185                 if (!amdtp_stream_wait_callback(&ff->rx_stream,
186                                                 CALLBACK_TIMEOUT_MS)) {
187                         err = -ETIMEDOUT;
188                         goto error;
189                 }
190
191                 err = ff->spec->protocol->switch_fetching_mode(ff, true);
192                 if (err < 0)
193                         goto error;
194         }
195
196         if (!amdtp_stream_running(&ff->tx_stream)) {
197                 err = amdtp_stream_start(&ff->tx_stream,
198                                          ff->tx_resources.channel,
199                                          fw_parent_device(ff->unit)->max_speed);
200                 if (err < 0)
201                         goto error;
202
203                 if (!amdtp_stream_wait_callback(&ff->tx_stream,
204                                                 CALLBACK_TIMEOUT_MS)) {
205                         err = -ETIMEDOUT;
206                         goto error;
207                 }
208         }
209
210         return 0;
211 error:
212         amdtp_stream_stop(&ff->tx_stream);
213         amdtp_stream_stop(&ff->rx_stream);
214
215         finish_session(ff);
216         release_resources(ff);
217
218         return err;
219 }
220
221 void snd_ff_stream_stop_duplex(struct snd_ff *ff)
222 {
223         if (ff->substreams_counter > 0)
224                 return;
225
226         amdtp_stream_stop(&ff->tx_stream);
227         amdtp_stream_stop(&ff->rx_stream);
228         finish_session(ff);
229         release_resources(ff);
230 }
231
232 void snd_ff_stream_update_duplex(struct snd_ff *ff)
233 {
234         /* The device discontinue to transfer packets.  */
235         amdtp_stream_pcm_abort(&ff->tx_stream);
236         amdtp_stream_stop(&ff->tx_stream);
237
238         amdtp_stream_pcm_abort(&ff->rx_stream);
239         amdtp_stream_stop(&ff->rx_stream);
240
241         fw_iso_resources_update(&ff->tx_resources);
242         fw_iso_resources_update(&ff->rx_resources);
243 }
244
245 void snd_ff_stream_lock_changed(struct snd_ff *ff)
246 {
247         ff->dev_lock_changed = true;
248         wake_up(&ff->hwdep_wait);
249 }
250
251 int snd_ff_stream_lock_try(struct snd_ff *ff)
252 {
253         int err;
254
255         spin_lock_irq(&ff->lock);
256
257         /* user land lock this */
258         if (ff->dev_lock_count < 0) {
259                 err = -EBUSY;
260                 goto end;
261         }
262
263         /* this is the first time */
264         if (ff->dev_lock_count++ == 0)
265                 snd_ff_stream_lock_changed(ff);
266         err = 0;
267 end:
268         spin_unlock_irq(&ff->lock);
269         return err;
270 }
271
272 void snd_ff_stream_lock_release(struct snd_ff *ff)
273 {
274         spin_lock_irq(&ff->lock);
275
276         if (WARN_ON(ff->dev_lock_count <= 0))
277                 goto end;
278         if (--ff->dev_lock_count == 0)
279                 snd_ff_stream_lock_changed(ff);
280 end:
281         spin_unlock_irq(&ff->lock);
282 }