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