ALSA: fireface: code refactoring for initialization/destruction of AMDTP stream
[linux-2.6-block.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 inline void finish_session(struct snd_ff *ff)
34 {
35         amdtp_stream_stop(&ff->tx_stream);
36         amdtp_stream_stop(&ff->rx_stream);
37
38         ff->spec->protocol->finish_session(ff);
39         ff->spec->protocol->switch_fetching_mode(ff, false);
40 }
41
42 static int init_stream(struct snd_ff *ff, struct amdtp_stream *s)
43 {
44         struct fw_iso_resources *resources;
45         enum amdtp_stream_direction dir;
46         int err;
47
48         if (s == &ff->tx_stream) {
49                 resources = &ff->tx_resources;
50                 dir = AMDTP_IN_STREAM;
51         } else {
52                 resources = &ff->rx_resources;
53                 dir = AMDTP_OUT_STREAM;
54         }
55
56         err = fw_iso_resources_init(resources, ff->unit);
57         if (err < 0)
58                 return err;
59
60         err = amdtp_ff_init(s, ff->unit, dir);
61         if (err < 0)
62                 fw_iso_resources_destroy(resources);
63
64         return err;
65 }
66
67 static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s)
68 {
69         amdtp_stream_destroy(s);
70
71         if (s == &ff->tx_stream)
72                 fw_iso_resources_destroy(&ff->tx_resources);
73         else
74                 fw_iso_resources_destroy(&ff->rx_resources);
75 }
76
77 int snd_ff_stream_init_duplex(struct snd_ff *ff)
78 {
79         int err;
80
81         err = init_stream(ff, &ff->rx_stream);
82         if (err < 0)
83                 return err;
84
85         err = init_stream(ff, &ff->tx_stream);
86         if (err < 0)
87                 destroy_stream(ff, &ff->rx_stream);
88
89         return err;
90 }
91
92 /*
93  * This function should be called before starting streams or after stopping
94  * streams.
95  */
96 void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
97 {
98         destroy_stream(ff, &ff->rx_stream);
99         destroy_stream(ff, &ff->tx_stream);
100 }
101
102 int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
103 {
104         unsigned int curr_rate;
105         enum snd_ff_clock_src src;
106         int err;
107
108         err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
109         if (err < 0)
110                 return err;
111
112         if (ff->substreams_counter == 0 || curr_rate != rate) {
113                 enum snd_ff_stream_mode mode;
114                 int i;
115
116                 finish_session(ff);
117
118                 fw_iso_resources_free(&ff->tx_resources);
119                 fw_iso_resources_free(&ff->rx_resources);
120
121                 for (i = 0; i < CIP_SFC_COUNT; ++i) {
122                         if (amdtp_rate_table[i] == rate)
123                                 break;
124                 }
125                 if (i >= CIP_SFC_COUNT)
126                         return -EINVAL;
127
128                 err = snd_ff_stream_get_multiplier_mode(i, &mode);
129                 if (err < 0)
130                         return err;
131
132                 err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
133                                         ff->spec->pcm_capture_channels[mode]);
134                 if (err < 0)
135                         return err;
136
137                 err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
138                                         ff->spec->pcm_playback_channels[mode]);
139                 if (err < 0)
140                         return err;
141
142                 err = ff->spec->protocol->allocate_resources(ff, rate);
143                 if (err < 0)
144                         return err;
145         }
146
147         return 0;
148 }
149
150 int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
151 {
152         int err;
153
154         if (ff->substreams_counter == 0)
155                 return 0;
156
157         if (amdtp_streaming_error(&ff->tx_stream) ||
158             amdtp_streaming_error(&ff->rx_stream))
159                 finish_session(ff);
160
161         /*
162          * Regardless of current source of clock signal, drivers transfer some
163          * packets. Then, the device transfers packets.
164          */
165         if (!amdtp_stream_running(&ff->rx_stream)) {
166                 err = ff->spec->protocol->begin_session(ff, rate);
167                 if (err < 0)
168                         goto error;
169
170                 err = amdtp_stream_start(&ff->rx_stream,
171                                          ff->rx_resources.channel,
172                                          fw_parent_device(ff->unit)->max_speed);
173                 if (err < 0)
174                         goto error;
175
176                 if (!amdtp_stream_wait_callback(&ff->rx_stream,
177                                                 CALLBACK_TIMEOUT_MS)) {
178                         err = -ETIMEDOUT;
179                         goto error;
180                 }
181
182                 err = ff->spec->protocol->switch_fetching_mode(ff, true);
183                 if (err < 0)
184                         goto error;
185         }
186
187         if (!amdtp_stream_running(&ff->tx_stream)) {
188                 err = amdtp_stream_start(&ff->tx_stream,
189                                          ff->tx_resources.channel,
190                                          fw_parent_device(ff->unit)->max_speed);
191                 if (err < 0)
192                         goto error;
193
194                 if (!amdtp_stream_wait_callback(&ff->tx_stream,
195                                                 CALLBACK_TIMEOUT_MS)) {
196                         err = -ETIMEDOUT;
197                         goto error;
198                 }
199         }
200
201         return 0;
202 error:
203         finish_session(ff);
204
205         return err;
206 }
207
208 void snd_ff_stream_stop_duplex(struct snd_ff *ff)
209 {
210         if (ff->substreams_counter == 0) {
211                 finish_session(ff);
212
213                 fw_iso_resources_free(&ff->tx_resources);
214                 fw_iso_resources_free(&ff->rx_resources);
215         }
216 }
217
218 void snd_ff_stream_update_duplex(struct snd_ff *ff)
219 {
220         // The device discontinue to transfer packets.
221         amdtp_stream_pcm_abort(&ff->tx_stream);
222         amdtp_stream_stop(&ff->tx_stream);
223
224         amdtp_stream_pcm_abort(&ff->rx_stream);
225         amdtp_stream_stop(&ff->rx_stream);
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 }