1
2
3
4
5
6
7#include "./fireworks.h"
8
9#define CALLBACK_TIMEOUT 100
10
11static int
12init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
13{
14 struct cmp_connection *conn;
15 enum cmp_direction c_dir;
16 enum amdtp_stream_direction s_dir;
17 int err;
18
19 if (stream == &efw->tx_stream) {
20 conn = &efw->out_conn;
21 c_dir = CMP_OUTPUT;
22 s_dir = AMDTP_IN_STREAM;
23 } else {
24 conn = &efw->in_conn;
25 c_dir = CMP_INPUT;
26 s_dir = AMDTP_OUT_STREAM;
27 }
28
29 err = cmp_connection_init(conn, efw->unit, c_dir, 0);
30 if (err < 0)
31 goto end;
32
33 err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
34 if (err < 0) {
35 amdtp_stream_destroy(stream);
36 cmp_connection_destroy(conn);
37 }
38end:
39 return err;
40}
41
42static void
43stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
44{
45 amdtp_stream_stop(stream);
46
47 if (stream == &efw->tx_stream)
48 cmp_connection_break(&efw->out_conn);
49 else
50 cmp_connection_break(&efw->in_conn);
51}
52
53static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
54 unsigned int rate)
55{
56 struct cmp_connection *conn;
57 int err;
58
59 if (stream == &efw->tx_stream)
60 conn = &efw->out_conn;
61 else
62 conn = &efw->in_conn;
63
64
65 err = cmp_connection_establish(conn);
66 if (err < 0)
67 return err;
68
69
70 err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
71 if (err < 0) {
72 cmp_connection_break(conn);
73 return err;
74 }
75
76
77 if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
78 amdtp_stream_stop(stream);
79 cmp_connection_break(conn);
80 return -ETIMEDOUT;
81 }
82
83 return 0;
84}
85
86
87
88
89
90static void
91destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
92{
93 struct cmp_connection *conn;
94
95 if (stream == &efw->tx_stream)
96 conn = &efw->out_conn;
97 else
98 conn = &efw->in_conn;
99
100 amdtp_stream_destroy(stream);
101 cmp_connection_destroy(conn);
102}
103
104static int
105check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
106{
107 struct cmp_connection *conn;
108 bool used;
109 int err;
110
111 if (s == &efw->tx_stream)
112 conn = &efw->out_conn;
113 else
114 conn = &efw->in_conn;
115
116 err = cmp_connection_check_used(conn, &used);
117 if ((err >= 0) && used && !amdtp_stream_running(s)) {
118 dev_err(&efw->unit->device,
119 "Connection established by others: %cPCR[%d]\n",
120 (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
121 conn->pcr_index);
122 err = -EBUSY;
123 }
124
125 return err;
126}
127
128int snd_efw_stream_init_duplex(struct snd_efw *efw)
129{
130 int err;
131
132 err = init_stream(efw, &efw->tx_stream);
133 if (err < 0)
134 goto end;
135
136 efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
137
138 efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
139
140 efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
141
142
143
144
145 if (efw->is_fireworks3 &&
146 (efw->firmware_version == 0x5070000 ||
147 efw->firmware_version == 0x5070300 ||
148 efw->firmware_version == 0x5080000))
149 efw->tx_stream.ctx_data.tx.first_dbc = 0x02;
150
151 if (efw->is_af9)
152 efw->tx_stream.flags |= CIP_WRONG_DBS;
153
154 if (efw->firmware_version == 0x5050000)
155 efw->tx_stream.ctx_data.tx.dbc_interval = 8;
156
157 err = init_stream(efw, &efw->rx_stream);
158 if (err < 0) {
159 destroy_stream(efw, &efw->tx_stream);
160 goto end;
161 }
162
163
164 err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
165 if (err < 0) {
166 destroy_stream(efw, &efw->tx_stream);
167 destroy_stream(efw, &efw->rx_stream);
168 }
169end:
170 return err;
171}
172
173static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,
174 unsigned int rate, unsigned int mode)
175{
176 unsigned int pcm_channels;
177 unsigned int midi_ports;
178 struct cmp_connection *conn;
179 int err;
180
181 if (stream == &efw->tx_stream) {
182 pcm_channels = efw->pcm_capture_channels[mode];
183 midi_ports = efw->midi_out_ports;
184 conn = &efw->out_conn;
185 } else {
186 pcm_channels = efw->pcm_playback_channels[mode];
187 midi_ports = efw->midi_in_ports;
188 conn = &efw->in_conn;
189 }
190
191 err = amdtp_am824_set_parameters(stream, rate, pcm_channels,
192 midi_ports, false);
193 if (err < 0)
194 return err;
195
196 return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
197}
198
199int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
200{
201 unsigned int curr_rate;
202 int err;
203
204
205
206 err = check_connection_used_by_others(efw, &efw->rx_stream);
207 if (err < 0)
208 return err;
209
210
211 err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
212 if (err < 0)
213 return err;
214 if (rate == 0)
215 rate = curr_rate;
216 if (rate != curr_rate) {
217 stop_stream(efw, &efw->tx_stream);
218 stop_stream(efw, &efw->rx_stream);
219
220 cmp_connection_release(&efw->out_conn);
221 cmp_connection_release(&efw->in_conn);
222 }
223
224 if (efw->substreams_counter == 0 || rate != curr_rate) {
225 unsigned int mode;
226
227 err = snd_efw_command_set_sampling_rate(efw, rate);
228 if (err < 0)
229 return err;
230
231 err = snd_efw_get_multiplier_mode(rate, &mode);
232 if (err < 0)
233 return err;
234
235 err = keep_resources(efw, &efw->tx_stream, rate, mode);
236 if (err < 0)
237 return err;
238
239 err = keep_resources(efw, &efw->rx_stream, rate, mode);
240 if (err < 0) {
241 cmp_connection_release(&efw->in_conn);
242 return err;
243 }
244 }
245
246 return 0;
247}
248
249int snd_efw_stream_start_duplex(struct snd_efw *efw)
250{
251 unsigned int rate;
252 int err = 0;
253
254
255 if (efw->substreams_counter == 0)
256 return -EIO;
257
258 err = snd_efw_command_get_sampling_rate(efw, &rate);
259 if (err < 0)
260 return err;
261
262 if (amdtp_streaming_error(&efw->rx_stream) ||
263 amdtp_streaming_error(&efw->tx_stream)) {
264 stop_stream(efw, &efw->rx_stream);
265 stop_stream(efw, &efw->tx_stream);
266 }
267
268
269 if (!amdtp_stream_running(&efw->rx_stream)) {
270 err = start_stream(efw, &efw->rx_stream, rate);
271 if (err < 0) {
272 dev_err(&efw->unit->device,
273 "fail to start AMDTP master stream:%d\n", err);
274 goto error;
275 }
276 }
277
278 if (!amdtp_stream_running(&efw->tx_stream)) {
279 err = start_stream(efw, &efw->tx_stream, rate);
280 if (err < 0) {
281 dev_err(&efw->unit->device,
282 "fail to start AMDTP slave stream:%d\n", err);
283 goto error;
284 }
285 }
286
287 return 0;
288error:
289 stop_stream(efw, &efw->rx_stream);
290 stop_stream(efw, &efw->tx_stream);
291 return err;
292}
293
294void snd_efw_stream_stop_duplex(struct snd_efw *efw)
295{
296 if (efw->substreams_counter == 0) {
297 stop_stream(efw, &efw->tx_stream);
298 stop_stream(efw, &efw->rx_stream);
299
300 cmp_connection_release(&efw->out_conn);
301 cmp_connection_release(&efw->in_conn);
302 }
303}
304
305void snd_efw_stream_update_duplex(struct snd_efw *efw)
306{
307 if (cmp_connection_update(&efw->out_conn) < 0 ||
308 cmp_connection_update(&efw->in_conn) < 0) {
309 stop_stream(efw, &efw->rx_stream);
310 stop_stream(efw, &efw->tx_stream);
311 } else {
312 amdtp_stream_update(&efw->rx_stream);
313 amdtp_stream_update(&efw->tx_stream);
314 }
315}
316
317void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
318{
319 destroy_stream(efw, &efw->rx_stream);
320 destroy_stream(efw, &efw->tx_stream);
321}
322
323void snd_efw_stream_lock_changed(struct snd_efw *efw)
324{
325 efw->dev_lock_changed = true;
326 wake_up(&efw->hwdep_wait);
327}
328
329int snd_efw_stream_lock_try(struct snd_efw *efw)
330{
331 int err;
332
333 spin_lock_irq(&efw->lock);
334
335
336 if (efw->dev_lock_count < 0) {
337 err = -EBUSY;
338 goto end;
339 }
340
341
342 if (efw->dev_lock_count++ == 0)
343 snd_efw_stream_lock_changed(efw);
344 err = 0;
345end:
346 spin_unlock_irq(&efw->lock);
347 return err;
348}
349
350void snd_efw_stream_lock_release(struct snd_efw *efw)
351{
352 spin_lock_irq(&efw->lock);
353
354 if (WARN_ON(efw->dev_lock_count <= 0))
355 goto end;
356 if (--efw->dev_lock_count == 0)
357 snd_efw_stream_lock_changed(efw);
358end:
359 spin_unlock_irq(&efw->lock);
360}
361