Merge tag 'asoc-v5.3' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[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 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, enum amdtp_stream_direction dir)
43 {
44         int err;
45         struct fw_iso_resources *resources;
46         struct amdtp_stream *stream;
47
48         if (dir == AMDTP_IN_STREAM) {
49                 resources = &ff->tx_resources;
50                 stream = &ff->tx_stream;
51         } else {
52                 resources = &ff->rx_resources;
53                 stream = &ff->rx_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(stream, 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, enum amdtp_stream_direction dir)
68 {
69         if (dir == AMDTP_IN_STREAM) {
70                 amdtp_stream_destroy(&ff->tx_stream);
71                 fw_iso_resources_destroy(&ff->tx_resources);
72         } else {
73                 amdtp_stream_destroy(&ff->rx_stream);
74                 fw_iso_resources_destroy(&ff->rx_resources);
75         }
76 }
77
78 int snd_ff_stream_init_duplex(struct snd_ff *ff)
79 {
80         int err;
81
82         err = init_stream(ff, AMDTP_OUT_STREAM);
83         if (err < 0)
84                 goto end;
85
86         err = init_stream(ff, AMDTP_IN_STREAM);
87         if (err < 0)
88                 destroy_stream(ff, AMDTP_OUT_STREAM);
89 end:
90         return err;
91 }
92
93 /*
94  * This function should be called before starting streams or after stopping
95  * streams.
96  */
97 void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
98 {
99         destroy_stream(ff, AMDTP_IN_STREAM);
100         destroy_stream(ff, AMDTP_OUT_STREAM);
101 }
102
103 int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
104 {
105         unsigned int curr_rate;
106         enum snd_ff_clock_src src;
107         int err;
108
109         err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
110         if (err < 0)
111                 return err;
112
113         if (ff->substreams_counter == 0 || curr_rate != rate) {
114                 enum snd_ff_stream_mode mode;
115                 int i;
116
117                 finish_session(ff);
118
119                 fw_iso_resources_free(&ff->tx_resources);
120                 fw_iso_resources_free(&ff->rx_resources);
121
122                 for (i = 0; i < CIP_SFC_COUNT; ++i) {
123                         if (amdtp_rate_table[i] == rate)
124                                 break;
125                 }
126                 if (i >= CIP_SFC_COUNT)
127                         return -EINVAL;
128
129                 err = snd_ff_stream_get_multiplier_mode(i, &mode);
130                 if (err < 0)
131                         return err;
132
133                 err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
134                                         ff->spec->pcm_capture_channels[mode]);
135                 if (err < 0)
136                         return err;
137
138                 err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
139                                         ff->spec->pcm_playback_channels[mode]);
140                 if (err < 0)
141                         return err;
142
143                 err = ff->spec->protocol->allocate_resources(ff, rate);
144                 if (err < 0)
145                         return err;
146         }
147
148         return 0;
149 }
150
151 int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
152 {
153         int err;
154
155         if (ff->substreams_counter == 0)
156                 return 0;
157
158         if (amdtp_streaming_error(&ff->tx_stream) ||
159             amdtp_streaming_error(&ff->rx_stream))
160                 finish_session(ff);
161
162         /*
163          * Regardless of current source of clock signal, drivers transfer some
164          * packets. Then, the device transfers packets.
165          */
166         if (!amdtp_stream_running(&ff->rx_stream)) {
167                 err = ff->spec->protocol->begin_session(ff, rate);
168                 if (err < 0)
169                         goto error;
170
171                 err = amdtp_stream_start(&ff->rx_stream,
172                                          ff->rx_resources.channel,
173                                          fw_parent_device(ff->unit)->max_speed);
174                 if (err < 0)
175                         goto error;
176
177                 if (!amdtp_stream_wait_callback(&ff->rx_stream,
178                                                 CALLBACK_TIMEOUT_MS)) {
179                         err = -ETIMEDOUT;
180                         goto error;
181                 }
182
183                 err = ff->spec->protocol->switch_fetching_mode(ff, true);
184                 if (err < 0)
185                         goto error;
186         }
187
188         if (!amdtp_stream_running(&ff->tx_stream)) {
189                 err = amdtp_stream_start(&ff->tx_stream,
190                                          ff->tx_resources.channel,
191                                          fw_parent_device(ff->unit)->max_speed);
192                 if (err < 0)
193                         goto error;
194
195                 if (!amdtp_stream_wait_callback(&ff->tx_stream,
196                                                 CALLBACK_TIMEOUT_MS)) {
197                         err = -ETIMEDOUT;
198                         goto error;
199                 }
200         }
201
202         return 0;
203 error:
204         finish_session(ff);
205
206         return err;
207 }
208
209 void snd_ff_stream_stop_duplex(struct snd_ff *ff)
210 {
211         if (ff->substreams_counter == 0) {
212                 finish_session(ff);
213
214                 fw_iso_resources_free(&ff->tx_resources);
215                 fw_iso_resources_free(&ff->rx_resources);
216         }
217 }
218
219 void snd_ff_stream_update_duplex(struct snd_ff *ff)
220 {
221         // The device discontinue to transfer packets.
222         amdtp_stream_pcm_abort(&ff->tx_stream);
223         amdtp_stream_stop(&ff->tx_stream);
224
225         amdtp_stream_pcm_abort(&ff->rx_stream);
226         amdtp_stream_stop(&ff->rx_stream);
227 }
228
229 void snd_ff_stream_lock_changed(struct snd_ff *ff)
230 {
231         ff->dev_lock_changed = true;
232         wake_up(&ff->hwdep_wait);
233 }
234
235 int snd_ff_stream_lock_try(struct snd_ff *ff)
236 {
237         int err;
238
239         spin_lock_irq(&ff->lock);
240
241         /* user land lock this */
242         if (ff->dev_lock_count < 0) {
243                 err = -EBUSY;
244                 goto end;
245         }
246
247         /* this is the first time */
248         if (ff->dev_lock_count++ == 0)
249                 snd_ff_stream_lock_changed(ff);
250         err = 0;
251 end:
252         spin_unlock_irq(&ff->lock);
253         return err;
254 }
255
256 void snd_ff_stream_lock_release(struct snd_ff *ff)
257 {
258         spin_lock_irq(&ff->lock);
259
260         if (WARN_ON(ff->dev_lock_count <= 0))
261                 goto end;
262         if (--ff->dev_lock_count == 0)
263                 snd_ff_stream_lock_changed(ff);
264 end:
265         spin_unlock_irq(&ff->lock);
266 }