Commit | Line | Data |
---|---|---|
315fd41f TS |
1 | /* |
2 | * fireworks_stream.c - a part of driver for Fireworks based devices | |
3 | * | |
4 | * Copyright (c) 2013-2014 Takashi Sakamoto | |
5 | * | |
6 | * Licensed under the terms of the GNU General Public License, version 2. | |
7 | */ | |
8 | #include "./fireworks.h" | |
9 | ||
10 | #define CALLBACK_TIMEOUT 100 | |
11 | ||
315fd41f TS |
12 | static int |
13 | init_stream(struct snd_efw *efw, struct amdtp_stream *stream) | |
14 | { | |
15 | struct cmp_connection *conn; | |
16 | enum cmp_direction c_dir; | |
17 | enum amdtp_stream_direction s_dir; | |
18 | int err; | |
19 | ||
20 | if (stream == &efw->tx_stream) { | |
21 | conn = &efw->out_conn; | |
22 | c_dir = CMP_OUTPUT; | |
23 | s_dir = AMDTP_IN_STREAM; | |
24 | } else { | |
25 | conn = &efw->in_conn; | |
26 | c_dir = CMP_INPUT; | |
27 | s_dir = AMDTP_OUT_STREAM; | |
28 | } | |
29 | ||
30 | err = cmp_connection_init(conn, efw->unit, c_dir, 0); | |
31 | if (err < 0) | |
32 | goto end; | |
33 | ||
5955815e | 34 | err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING); |
315fd41f TS |
35 | if (err < 0) { |
36 | amdtp_stream_destroy(stream); | |
37 | cmp_connection_destroy(conn); | |
38 | } | |
39 | end: | |
40 | return err; | |
41 | } | |
42 | ||
43 | static void | |
44 | stop_stream(struct snd_efw *efw, struct amdtp_stream *stream) | |
45 | { | |
46 | amdtp_stream_pcm_abort(stream); | |
47 | amdtp_stream_stop(stream); | |
48 | ||
49 | if (stream == &efw->tx_stream) | |
50 | cmp_connection_break(&efw->out_conn); | |
51 | else | |
52 | cmp_connection_break(&efw->in_conn); | |
53 | } | |
54 | ||
55 | static int | |
56 | start_stream(struct snd_efw *efw, struct amdtp_stream *stream, | |
57 | unsigned int sampling_rate) | |
58 | { | |
59 | struct cmp_connection *conn; | |
60 | unsigned int mode, pcm_channels, midi_ports; | |
61 | int err; | |
62 | ||
63 | err = snd_efw_get_multiplier_mode(sampling_rate, &mode); | |
64 | if (err < 0) | |
65 | goto end; | |
66 | if (stream == &efw->tx_stream) { | |
67 | conn = &efw->out_conn; | |
68 | pcm_channels = efw->pcm_capture_channels[mode]; | |
69 | midi_ports = efw->midi_out_ports; | |
70 | } else { | |
71 | conn = &efw->in_conn; | |
72 | pcm_channels = efw->pcm_playback_channels[mode]; | |
73 | midi_ports = efw->midi_in_ports; | |
74 | } | |
75 | ||
51c29fd2 TS |
76 | err = amdtp_am824_set_parameters(stream, sampling_rate, |
77 | pcm_channels, midi_ports, false); | |
547e631c TS |
78 | if (err < 0) |
79 | goto end; | |
315fd41f TS |
80 | |
81 | /* establish connection via CMP */ | |
82 | err = cmp_connection_establish(conn, | |
83 | amdtp_stream_get_max_payload(stream)); | |
84 | if (err < 0) | |
85 | goto end; | |
86 | ||
87 | /* start amdtp stream */ | |
88 | err = amdtp_stream_start(stream, | |
89 | conn->resources.channel, | |
90 | conn->speed); | |
91 | if (err < 0) { | |
92 | stop_stream(efw, stream); | |
93 | goto end; | |
94 | } | |
95 | ||
96 | /* wait first callback */ | |
97 | if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { | |
98 | stop_stream(efw, stream); | |
99 | err = -ETIMEDOUT; | |
100 | } | |
101 | end: | |
102 | return err; | |
103 | } | |
104 | ||
d23c2cc4 TS |
105 | /* |
106 | * This function should be called before starting the stream or after stopping | |
107 | * the streams. | |
108 | */ | |
315fd41f TS |
109 | static void |
110 | destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream) | |
111 | { | |
d23c2cc4 | 112 | struct cmp_connection *conn; |
315fd41f TS |
113 | |
114 | if (stream == &efw->tx_stream) | |
d23c2cc4 | 115 | conn = &efw->out_conn; |
315fd41f | 116 | else |
d23c2cc4 TS |
117 | conn = &efw->in_conn; |
118 | ||
119 | amdtp_stream_destroy(stream); | |
120 | cmp_connection_destroy(&efw->out_conn); | |
315fd41f TS |
121 | } |
122 | ||
123 | static int | |
124 | get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode) | |
125 | { | |
126 | enum snd_efw_clock_source clock_source; | |
127 | int err; | |
128 | ||
129 | err = snd_efw_command_get_clock_source(efw, &clock_source); | |
130 | if (err < 0) | |
131 | return err; | |
132 | ||
133 | if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH) | |
134 | return -ENOSYS; | |
135 | ||
136 | *sync_mode = CIP_SYNC_TO_DEVICE; | |
137 | return 0; | |
138 | } | |
139 | ||
140 | static int | |
141 | check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s) | |
142 | { | |
143 | struct cmp_connection *conn; | |
144 | bool used; | |
145 | int err; | |
146 | ||
147 | if (s == &efw->tx_stream) | |
148 | conn = &efw->out_conn; | |
149 | else | |
150 | conn = &efw->in_conn; | |
151 | ||
152 | err = cmp_connection_check_used(conn, &used); | |
153 | if ((err >= 0) && used && !amdtp_stream_running(s)) { | |
154 | dev_err(&efw->unit->device, | |
155 | "Connection established by others: %cPCR[%d]\n", | |
156 | (conn->direction == CMP_OUTPUT) ? 'o' : 'i', | |
157 | conn->pcr_index); | |
158 | err = -EBUSY; | |
159 | } | |
160 | ||
161 | return err; | |
162 | } | |
163 | ||
164 | int snd_efw_stream_init_duplex(struct snd_efw *efw) | |
165 | { | |
166 | int err; | |
167 | ||
168 | err = init_stream(efw, &efw->tx_stream); | |
169 | if (err < 0) | |
170 | goto end; | |
7ab56645 TS |
171 | /* Fireworks transmits NODATA packets with TAG0. */ |
172 | efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0; | |
c8bdf49b TS |
173 | /* Fireworks has its own meaning for dbc. */ |
174 | efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT; | |
b84b1a27 TS |
175 | /* Fireworks reset dbc at bus reset. */ |
176 | efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK; | |
18f5ed36 TS |
177 | /* |
178 | * But Recent firmwares starts packets with non-zero dbc. | |
179 | * Driver version 5.7.6 installs firmware version 5.7.3. | |
180 | */ | |
181 | if (efw->is_fireworks3 && | |
182 | (efw->firmware_version == 0x5070000 || | |
183 | efw->firmware_version == 0x5070300 || | |
184 | efw->firmware_version == 0x5080000)) | |
185 | efw->tx_stream.tx_first_dbc = 0x02; | |
69702239 TS |
186 | /* AudioFire9 always reports wrong dbs. */ |
187 | if (efw->is_af9) | |
188 | efw->tx_stream.flags |= CIP_WRONG_DBS; | |
d9cd0065 TS |
189 | /* Firmware version 5.5 reports fixed interval for dbc. */ |
190 | if (efw->firmware_version == 0x5050000) | |
191 | efw->tx_stream.tx_dbc_interval = 8; | |
315fd41f TS |
192 | |
193 | err = init_stream(efw, &efw->rx_stream); | |
194 | if (err < 0) { | |
195 | destroy_stream(efw, &efw->tx_stream); | |
196 | goto end; | |
197 | } | |
198 | ||
199 | /* set IEC61883 compliant mode (actually not fully compliant...) */ | |
200 | err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883); | |
201 | if (err < 0) { | |
202 | destroy_stream(efw, &efw->tx_stream); | |
203 | destroy_stream(efw, &efw->rx_stream); | |
204 | } | |
205 | end: | |
206 | return err; | |
207 | } | |
208 | ||
4a286d55 | 209 | int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) |
315fd41f TS |
210 | { |
211 | struct amdtp_stream *master, *slave; | |
4d2c50a0 | 212 | unsigned int slave_substreams; |
315fd41f TS |
213 | enum cip_flags sync_mode; |
214 | unsigned int curr_rate; | |
215 | int err = 0; | |
216 | ||
315fd41f | 217 | /* Need no substreams */ |
4d2c50a0 | 218 | if (efw->playback_substreams == 0 && efw->capture_substreams == 0) |
315fd41f TS |
219 | goto end; |
220 | ||
221 | err = get_sync_mode(efw, &sync_mode); | |
222 | if (err < 0) | |
223 | goto end; | |
224 | if (sync_mode == CIP_SYNC_TO_DEVICE) { | |
225 | master = &efw->tx_stream; | |
226 | slave = &efw->rx_stream; | |
4d2c50a0 | 227 | slave_substreams = efw->playback_substreams; |
315fd41f TS |
228 | } else { |
229 | master = &efw->rx_stream; | |
230 | slave = &efw->tx_stream; | |
4d2c50a0 | 231 | slave_substreams = efw->capture_substreams; |
315fd41f TS |
232 | } |
233 | ||
234 | /* | |
235 | * Considering JACK/FFADO streaming: | |
236 | * TODO: This can be removed hwdep functionality becomes popular. | |
237 | */ | |
238 | err = check_connection_used_by_others(efw, master); | |
239 | if (err < 0) | |
240 | goto end; | |
241 | ||
242 | /* packet queueing error */ | |
243 | if (amdtp_streaming_error(slave)) | |
244 | stop_stream(efw, slave); | |
245 | if (amdtp_streaming_error(master)) | |
246 | stop_stream(efw, master); | |
247 | ||
248 | /* stop streams if rate is different */ | |
249 | err = snd_efw_command_get_sampling_rate(efw, &curr_rate); | |
250 | if (err < 0) | |
251 | goto end; | |
252 | if (rate == 0) | |
253 | rate = curr_rate; | |
254 | if (rate != curr_rate) { | |
255 | stop_stream(efw, slave); | |
256 | stop_stream(efw, master); | |
257 | } | |
258 | ||
259 | /* master should be always running */ | |
260 | if (!amdtp_stream_running(master)) { | |
261 | amdtp_stream_set_sync(sync_mode, master, slave); | |
262 | efw->master = master; | |
263 | ||
264 | err = snd_efw_command_set_sampling_rate(efw, rate); | |
265 | if (err < 0) | |
266 | goto end; | |
267 | ||
268 | err = start_stream(efw, master, rate); | |
269 | if (err < 0) { | |
270 | dev_err(&efw->unit->device, | |
271 | "fail to start AMDTP master stream:%d\n", err); | |
272 | goto end; | |
273 | } | |
274 | } | |
275 | ||
276 | /* start slave if needed */ | |
4d2c50a0 | 277 | if (slave_substreams > 0 && !amdtp_stream_running(slave)) { |
315fd41f TS |
278 | err = start_stream(efw, slave, rate); |
279 | if (err < 0) { | |
280 | dev_err(&efw->unit->device, | |
281 | "fail to start AMDTP slave stream:%d\n", err); | |
282 | stop_stream(efw, master); | |
283 | } | |
284 | } | |
285 | end: | |
315fd41f TS |
286 | return err; |
287 | } | |
288 | ||
289 | void snd_efw_stream_stop_duplex(struct snd_efw *efw) | |
290 | { | |
291 | struct amdtp_stream *master, *slave; | |
4d2c50a0 | 292 | unsigned int master_substreams, slave_substreams; |
315fd41f | 293 | |
315fd41f TS |
294 | if (efw->master == &efw->rx_stream) { |
295 | slave = &efw->tx_stream; | |
296 | master = &efw->rx_stream; | |
4d2c50a0 TS |
297 | slave_substreams = efw->capture_substreams; |
298 | master_substreams = efw->playback_substreams; | |
315fd41f TS |
299 | } else { |
300 | slave = &efw->rx_stream; | |
301 | master = &efw->tx_stream; | |
4d2c50a0 TS |
302 | slave_substreams = efw->playback_substreams; |
303 | master_substreams = efw->capture_substreams; | |
315fd41f TS |
304 | } |
305 | ||
4d2c50a0 | 306 | if (slave_substreams == 0) { |
315fd41f TS |
307 | stop_stream(efw, slave); |
308 | ||
4d2c50a0 | 309 | if (master_substreams == 0) |
315fd41f TS |
310 | stop_stream(efw, master); |
311 | } | |
315fd41f TS |
312 | } |
313 | ||
314 | void snd_efw_stream_update_duplex(struct snd_efw *efw) | |
315 | { | |
99d73559 TS |
316 | if (cmp_connection_update(&efw->out_conn) < 0 || |
317 | cmp_connection_update(&efw->in_conn) < 0) { | |
315fd41f TS |
318 | stop_stream(efw, &efw->rx_stream); |
319 | stop_stream(efw, &efw->tx_stream); | |
315fd41f TS |
320 | } else { |
321 | amdtp_stream_update(&efw->rx_stream); | |
322 | amdtp_stream_update(&efw->tx_stream); | |
323 | } | |
324 | } | |
325 | ||
326 | void snd_efw_stream_destroy_duplex(struct snd_efw *efw) | |
327 | { | |
315fd41f TS |
328 | destroy_stream(efw, &efw->rx_stream); |
329 | destroy_stream(efw, &efw->tx_stream); | |
315fd41f | 330 | } |
594ddced TS |
331 | |
332 | void snd_efw_stream_lock_changed(struct snd_efw *efw) | |
333 | { | |
334 | efw->dev_lock_changed = true; | |
335 | wake_up(&efw->hwdep_wait); | |
336 | } | |
337 | ||
338 | int snd_efw_stream_lock_try(struct snd_efw *efw) | |
339 | { | |
340 | int err; | |
341 | ||
342 | spin_lock_irq(&efw->lock); | |
343 | ||
344 | /* user land lock this */ | |
345 | if (efw->dev_lock_count < 0) { | |
346 | err = -EBUSY; | |
347 | goto end; | |
348 | } | |
349 | ||
350 | /* this is the first time */ | |
351 | if (efw->dev_lock_count++ == 0) | |
352 | snd_efw_stream_lock_changed(efw); | |
353 | err = 0; | |
354 | end: | |
355 | spin_unlock_irq(&efw->lock); | |
356 | return err; | |
357 | } | |
358 | ||
359 | void snd_efw_stream_lock_release(struct snd_efw *efw) | |
360 | { | |
361 | spin_lock_irq(&efw->lock); | |
362 | ||
363 | if (WARN_ON(efw->dev_lock_count <= 0)) | |
364 | goto end; | |
365 | if (--efw->dev_lock_count == 0) | |
366 | snd_efw_stream_lock_changed(efw); | |
367 | end: | |
368 | spin_unlock_irq(&efw->lock); | |
369 | } |