1
2
3
4
5
6
7
8#include "ff.h"
9
10#define CALLBACK_TIMEOUT_MS 200
11
12int 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
33static 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
42static 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
67static 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
78int 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);
89end:
90 return err;
91}
92
93
94
95
96
97void 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
103int 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
151int 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
164
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;
203error:
204 finish_session(ff);
205
206 return err;
207}
208
209void 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
219void snd_ff_stream_update_duplex(struct snd_ff *ff)
220{
221
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
229void snd_ff_stream_lock_changed(struct snd_ff *ff)
230{
231 ff->dev_lock_changed = true;
232 wake_up(&ff->hwdep_wait);
233}
234
235int snd_ff_stream_lock_try(struct snd_ff *ff)
236{
237 int err;
238
239 spin_lock_irq(&ff->lock);
240
241
242 if (ff->dev_lock_count < 0) {
243 err = -EBUSY;
244 goto end;
245 }
246
247
248 if (ff->dev_lock_count++ == 0)
249 snd_ff_stream_lock_changed(ff);
250 err = 0;
251end:
252 spin_unlock_irq(&ff->lock);
253 return err;
254}
255
256void 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);
264end:
265 spin_unlock_irq(&ff->lock);
266}
267