aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2014-04-25 09:45:02 -0400
committerTakashi Iwai <tiwai@suse.de>2014-05-26 08:24:19 -0400
commit315fd41fe9d43838ab5afd26c58d908d18313d9a (patch)
tree4dbd346fcffb9df2dccd9110793ad38d5e27e2b9 /sound
parentbde8a8f23bbe6db51fa4e81644273af18fef3d7a (diff)
ALSA: fireworks: Add connection and stream management
Fireworks manages connections by CMP and can transmit/receive AMDTP streams with a few quirks. This commit adds functionality to start/stop the streams. Major Fireworks products don't support 'SYT-Match' clock source mode, except for AudioFire12/8(till 2009 July) with firmware version 1.0. Already in previous commit, this driver don't support such old firmwares. So this commit adds support for non 'SYT-Match' clock source modes. I note that this driver has a short gap for MIDI streams when starting PCM stream. When AMDTP streams are running only for MIDI data and PCM data is going to be joined at different sampling rate, then AMDTP streams are stopped once and started again after changing sampling rate. Unfortunately, Fireworks is not fully compliant to IEC 61883-1/6. Some commits following to this commit add these quirks. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/firewire/fireworks/Makefile3
-rw-r--r--sound/firewire/fireworks/fireworks.c64
-rw-r--r--sound/firewire/fireworks/fireworks.h27
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c349
4 files changed, 441 insertions, 2 deletions
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index a6ce2147e0ab..1bccb653e326 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,2 +1,3 @@
1snd-fireworks-objs := fireworks_transaction.o fireworks_command.o fireworks.o 1snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
2 fireworks_stream.o fireworks.o
2obj-m += snd-fireworks.o 3obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index 6fa3a5d725d5..307fb2f250da 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -98,6 +98,57 @@ get_hardware_info(struct snd_efw *efw)
98 98
99 if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE)) 99 if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE))
100 efw->resp_addr_changable = true; 100 efw->resp_addr_changable = true;
101
102 efw->supported_sampling_rate = 0;
103 if ((hwinfo->min_sample_rate <= 22050)
104 && (22050 <= hwinfo->max_sample_rate))
105 efw->supported_sampling_rate |= SNDRV_PCM_RATE_22050;
106 if ((hwinfo->min_sample_rate <= 32000)
107 && (32000 <= hwinfo->max_sample_rate))
108 efw->supported_sampling_rate |= SNDRV_PCM_RATE_32000;
109 if ((hwinfo->min_sample_rate <= 44100)
110 && (44100 <= hwinfo->max_sample_rate))
111 efw->supported_sampling_rate |= SNDRV_PCM_RATE_44100;
112 if ((hwinfo->min_sample_rate <= 48000)
113 && (48000 <= hwinfo->max_sample_rate))
114 efw->supported_sampling_rate |= SNDRV_PCM_RATE_48000;
115 if ((hwinfo->min_sample_rate <= 88200)
116 && (88200 <= hwinfo->max_sample_rate))
117 efw->supported_sampling_rate |= SNDRV_PCM_RATE_88200;
118 if ((hwinfo->min_sample_rate <= 96000)
119 && (96000 <= hwinfo->max_sample_rate))
120 efw->supported_sampling_rate |= SNDRV_PCM_RATE_96000;
121 if ((hwinfo->min_sample_rate <= 176400)
122 && (176400 <= hwinfo->max_sample_rate))
123 efw->supported_sampling_rate |= SNDRV_PCM_RATE_176400;
124 if ((hwinfo->min_sample_rate <= 192000)
125 && (192000 <= hwinfo->max_sample_rate))
126 efw->supported_sampling_rate |= SNDRV_PCM_RATE_192000;
127
128 /* the number of MIDI ports, not of MIDI conformant data channels */
129 if (hwinfo->midi_out_ports > SND_EFW_MAX_MIDI_OUT_PORTS ||
130 hwinfo->midi_in_ports > SND_EFW_MAX_MIDI_IN_PORTS) {
131 err = -EIO;
132 goto end;
133 }
134 efw->midi_out_ports = hwinfo->midi_out_ports;
135 efw->midi_in_ports = hwinfo->midi_in_ports;
136
137 if (hwinfo->amdtp_tx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
138 hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
139 hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM ||
140 hwinfo->amdtp_rx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
141 hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
142 hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) {
143 err = -ENOSYS;
144 goto end;
145 }
146 efw->pcm_capture_channels[0] = hwinfo->amdtp_tx_pcm_channels;
147 efw->pcm_capture_channels[1] = hwinfo->amdtp_tx_pcm_channels_2x;
148 efw->pcm_capture_channels[2] = hwinfo->amdtp_tx_pcm_channels_4x;
149 efw->pcm_playback_channels[0] = hwinfo->amdtp_rx_pcm_channels;
150 efw->pcm_playback_channels[1] = hwinfo->amdtp_rx_pcm_channels_2x;
151 efw->pcm_playback_channels[2] = hwinfo->amdtp_rx_pcm_channels_4x;
101end: 152end:
102 kfree(hwinfo); 153 kfree(hwinfo);
103 return err; 154 return err;
@@ -155,10 +206,16 @@ efw_probe(struct fw_unit *unit,
155 if (err < 0) 206 if (err < 0)
156 goto error; 207 goto error;
157 208
158 err = snd_card_register(card); 209 err = snd_efw_stream_init_duplex(efw);
159 if (err < 0) 210 if (err < 0)
160 goto error; 211 goto error;
161 212
213 err = snd_card_register(card);
214 if (err < 0) {
215 snd_efw_stream_destroy_duplex(efw);
216 goto error;
217 }
218
162 dev_set_drvdata(&unit->device, efw); 219 dev_set_drvdata(&unit->device, efw);
163end: 220end:
164 mutex_unlock(&devices_mutex); 221 mutex_unlock(&devices_mutex);
@@ -172,12 +229,17 @@ error:
172static void efw_update(struct fw_unit *unit) 229static void efw_update(struct fw_unit *unit)
173{ 230{
174 struct snd_efw *efw = dev_get_drvdata(&unit->device); 231 struct snd_efw *efw = dev_get_drvdata(&unit->device);
232
175 snd_efw_transaction_bus_reset(efw->unit); 233 snd_efw_transaction_bus_reset(efw->unit);
234 snd_efw_stream_update_duplex(efw);
176} 235}
177 236
178static void efw_remove(struct fw_unit *unit) 237static void efw_remove(struct fw_unit *unit)
179{ 238{
180 struct snd_efw *efw = dev_get_drvdata(&unit->device); 239 struct snd_efw *efw = dev_get_drvdata(&unit->device);
240
241 snd_efw_stream_destroy_duplex(efw);
242
181 snd_card_disconnect(efw->card); 243 snd_card_disconnect(efw->card);
182 snd_card_free_when_closed(efw->card); 244 snd_card_free_when_closed(efw->card);
183} 245}
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index e999802ab470..ce511be4611b 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -22,9 +22,15 @@
22#include <sound/initval.h> 22#include <sound/initval.h>
23#include <sound/pcm.h> 23#include <sound/pcm.h>
24 24
25#include "../packets-buffer.h"
26#include "../iso-resources.h"
27#include "../amdtp.h"
25#include "../cmp.h" 28#include "../cmp.h"
26#include "../lib.h" 29#include "../lib.h"
27 30
31#define SND_EFW_MAX_MIDI_OUT_PORTS 2
32#define SND_EFW_MAX_MIDI_IN_PORTS 2
33
28#define SND_EFW_MULTIPLIER_MODES 3 34#define SND_EFW_MULTIPLIER_MODES 3
29#define HWINFO_NAME_SIZE_BYTES 32 35#define HWINFO_NAME_SIZE_BYTES 32
30#define HWINFO_MAX_CAPS_GROUPS 8 36#define HWINFO_MAX_CAPS_GROUPS 8
@@ -54,6 +60,21 @@ struct snd_efw {
54 /* for transaction */ 60 /* for transaction */
55 u32 seqnum; 61 u32 seqnum;
56 bool resp_addr_changable; 62 bool resp_addr_changable;
63
64 unsigned int midi_in_ports;
65 unsigned int midi_out_ports;
66
67 unsigned int supported_sampling_rate;
68 unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
69 unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
70
71 struct amdtp_stream *master;
72 struct amdtp_stream tx_stream;
73 struct amdtp_stream rx_stream;
74 struct cmp_connection out_conn;
75 struct cmp_connection in_conn;
76 atomic_t capture_substreams;
77 atomic_t playback_substreams;
57}; 78};
58 79
59struct snd_efw_transaction { 80struct snd_efw_transaction {
@@ -156,6 +177,12 @@ int snd_efw_command_get_clock_source(struct snd_efw *efw,
156int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate); 177int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
157int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate); 178int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
158 179
180int snd_efw_stream_init_duplex(struct snd_efw *efw);
181int snd_efw_stream_start_duplex(struct snd_efw *efw, int sampling_rate);
182void snd_efw_stream_stop_duplex(struct snd_efw *efw);
183void snd_efw_stream_update_duplex(struct snd_efw *efw);
184void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
185
159#define SND_EFW_DEV_ENTRY(vendor, model) \ 186#define SND_EFW_DEV_ENTRY(vendor, model) \
160{ \ 187{ \
161 .match_flags = IEEE1394_MATCH_VENDOR_ID | \ 188 .match_flags = IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
new file mode 100644
index 000000000000..ec62aa65cfae
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -0,0 +1,349 @@
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
12static unsigned int freq_table[] = {
13 /* multiplier mode 0 */
14 [0] = 32000,
15 [1] = 44100,
16 [2] = 48000,
17 /* multiplier mode 1 */
18 [3] = 88200,
19 [4] = 96000,
20 /* multiplier mode 2 */
21 [5] = 176400,
22 [6] = 192000,
23};
24
25static inline unsigned int
26get_multiplier_mode_with_index(unsigned int index)
27{
28 return ((int)index - 1) / 2;
29}
30
31int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode)
32{
33 unsigned int i;
34
35 for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
36 if (freq_table[i] == sampling_rate) {
37 *mode = get_multiplier_mode_with_index(i);
38 return 0;
39 }
40 }
41
42 return -EINVAL;
43}
44
45static int
46init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
47{
48 struct cmp_connection *conn;
49 enum cmp_direction c_dir;
50 enum amdtp_stream_direction s_dir;
51 int err;
52
53 if (stream == &efw->tx_stream) {
54 conn = &efw->out_conn;
55 c_dir = CMP_OUTPUT;
56 s_dir = AMDTP_IN_STREAM;
57 } else {
58 conn = &efw->in_conn;
59 c_dir = CMP_INPUT;
60 s_dir = AMDTP_OUT_STREAM;
61 }
62
63 err = cmp_connection_init(conn, efw->unit, c_dir, 0);
64 if (err < 0)
65 goto end;
66
67 err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
68 if (err < 0) {
69 amdtp_stream_destroy(stream);
70 cmp_connection_destroy(conn);
71 }
72end:
73 return err;
74}
75
76static void
77stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
78{
79 amdtp_stream_pcm_abort(stream);
80 amdtp_stream_stop(stream);
81
82 if (stream == &efw->tx_stream)
83 cmp_connection_break(&efw->out_conn);
84 else
85 cmp_connection_break(&efw->in_conn);
86}
87
88static int
89start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
90 unsigned int sampling_rate)
91{
92 struct cmp_connection *conn;
93 unsigned int mode, pcm_channels, midi_ports;
94 int err;
95
96 err = snd_efw_get_multiplier_mode(sampling_rate, &mode);
97 if (err < 0)
98 goto end;
99 if (stream == &efw->tx_stream) {
100 conn = &efw->out_conn;
101 pcm_channels = efw->pcm_capture_channels[mode];
102 midi_ports = efw->midi_out_ports;
103 } else {
104 conn = &efw->in_conn;
105 pcm_channels = efw->pcm_playback_channels[mode];
106 midi_ports = efw->midi_in_ports;
107 }
108
109 amdtp_stream_set_parameters(stream, sampling_rate,
110 pcm_channels, midi_ports);
111
112 /* establish connection via CMP */
113 err = cmp_connection_establish(conn,
114 amdtp_stream_get_max_payload(stream));
115 if (err < 0)
116 goto end;
117
118 /* start amdtp stream */
119 err = amdtp_stream_start(stream,
120 conn->resources.channel,
121 conn->speed);
122 if (err < 0) {
123 stop_stream(efw, stream);
124 goto end;
125 }
126
127 /* wait first callback */
128 if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
129 stop_stream(efw, stream);
130 err = -ETIMEDOUT;
131 }
132end:
133 return err;
134}
135
136static void
137destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
138{
139 stop_stream(efw, stream);
140
141 amdtp_stream_destroy(stream);
142
143 if (stream == &efw->tx_stream)
144 cmp_connection_destroy(&efw->out_conn);
145 else
146 cmp_connection_destroy(&efw->in_conn);
147}
148
149static int
150get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode)
151{
152 enum snd_efw_clock_source clock_source;
153 int err;
154
155 err = snd_efw_command_get_clock_source(efw, &clock_source);
156 if (err < 0)
157 return err;
158
159 if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH)
160 return -ENOSYS;
161
162 *sync_mode = CIP_SYNC_TO_DEVICE;
163 return 0;
164}
165
166static int
167check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
168{
169 struct cmp_connection *conn;
170 bool used;
171 int err;
172
173 if (s == &efw->tx_stream)
174 conn = &efw->out_conn;
175 else
176 conn = &efw->in_conn;
177
178 err = cmp_connection_check_used(conn, &used);
179 if ((err >= 0) && used && !amdtp_stream_running(s)) {
180 dev_err(&efw->unit->device,
181 "Connection established by others: %cPCR[%d]\n",
182 (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
183 conn->pcr_index);
184 err = -EBUSY;
185 }
186
187 return err;
188}
189
190int snd_efw_stream_init_duplex(struct snd_efw *efw)
191{
192 int err;
193
194 err = init_stream(efw, &efw->tx_stream);
195 if (err < 0)
196 goto end;
197
198 err = init_stream(efw, &efw->rx_stream);
199 if (err < 0) {
200 destroy_stream(efw, &efw->tx_stream);
201 goto end;
202 }
203
204 /* set IEC61883 compliant mode (actually not fully compliant...) */
205 err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
206 if (err < 0) {
207 destroy_stream(efw, &efw->tx_stream);
208 destroy_stream(efw, &efw->rx_stream);
209 }
210end:
211 return err;
212}
213
214int snd_efw_stream_start_duplex(struct snd_efw *efw, int rate)
215{
216 struct amdtp_stream *master, *slave;
217 atomic_t *slave_substreams;
218 enum cip_flags sync_mode;
219 unsigned int curr_rate;
220 int err = 0;
221
222 mutex_lock(&efw->mutex);
223
224 /* Need no substreams */
225 if ((atomic_read(&efw->playback_substreams) == 0) &&
226 (atomic_read(&efw->capture_substreams) == 0))
227 goto end;
228
229 err = get_sync_mode(efw, &sync_mode);
230 if (err < 0)
231 goto end;
232 if (sync_mode == CIP_SYNC_TO_DEVICE) {
233 master = &efw->tx_stream;
234 slave = &efw->rx_stream;
235 slave_substreams = &efw->playback_substreams;
236 } else {
237 master = &efw->rx_stream;
238 slave = &efw->tx_stream;
239 slave_substreams = &efw->capture_substreams;
240 }
241
242 /*
243 * Considering JACK/FFADO streaming:
244 * TODO: This can be removed hwdep functionality becomes popular.
245 */
246 err = check_connection_used_by_others(efw, master);
247 if (err < 0)
248 goto end;
249
250 /* packet queueing error */
251 if (amdtp_streaming_error(slave))
252 stop_stream(efw, slave);
253 if (amdtp_streaming_error(master))
254 stop_stream(efw, master);
255
256 /* stop streams if rate is different */
257 err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
258 if (err < 0)
259 goto end;
260 if (rate == 0)
261 rate = curr_rate;
262 if (rate != curr_rate) {
263 stop_stream(efw, slave);
264 stop_stream(efw, master);
265 }
266
267 /* master should be always running */
268 if (!amdtp_stream_running(master)) {
269 amdtp_stream_set_sync(sync_mode, master, slave);
270 efw->master = master;
271
272 err = snd_efw_command_set_sampling_rate(efw, rate);
273 if (err < 0)
274 goto end;
275
276 err = start_stream(efw, master, rate);
277 if (err < 0) {
278 dev_err(&efw->unit->device,
279 "fail to start AMDTP master stream:%d\n", err);
280 goto end;
281 }
282 }
283
284 /* start slave if needed */
285 if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
286 err = start_stream(efw, slave, rate);
287 if (err < 0) {
288 dev_err(&efw->unit->device,
289 "fail to start AMDTP slave stream:%d\n", err);
290 stop_stream(efw, master);
291 }
292 }
293end:
294 mutex_unlock(&efw->mutex);
295 return err;
296}
297
298void snd_efw_stream_stop_duplex(struct snd_efw *efw)
299{
300 struct amdtp_stream *master, *slave;
301 atomic_t *master_substreams, *slave_substreams;
302
303 mutex_lock(&efw->mutex);
304
305 if (efw->master == &efw->rx_stream) {
306 slave = &efw->tx_stream;
307 master = &efw->rx_stream;
308 slave_substreams = &efw->capture_substreams;
309 master_substreams = &efw->playback_substreams;
310 } else {
311 slave = &efw->rx_stream;
312 master = &efw->tx_stream;
313 slave_substreams = &efw->playback_substreams;
314 master_substreams = &efw->capture_substreams;
315 }
316
317 if (atomic_read(slave_substreams) == 0) {
318 stop_stream(efw, slave);
319
320 if (atomic_read(master_substreams) == 0)
321 stop_stream(efw, master);
322 }
323
324 mutex_unlock(&efw->mutex);
325}
326
327void snd_efw_stream_update_duplex(struct snd_efw *efw)
328{
329 if ((cmp_connection_update(&efw->out_conn) < 0) ||
330 (cmp_connection_update(&efw->in_conn) < 0)) {
331 mutex_lock(&efw->mutex);
332 stop_stream(efw, &efw->rx_stream);
333 stop_stream(efw, &efw->tx_stream);
334 mutex_unlock(&efw->mutex);
335 } else {
336 amdtp_stream_update(&efw->rx_stream);
337 amdtp_stream_update(&efw->tx_stream);
338 }
339}
340
341void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
342{
343 mutex_lock(&efw->mutex);
344
345 destroy_stream(efw, &efw->rx_stream);
346 destroy_stream(efw, &efw->tx_stream);
347
348 mutex_unlock(&efw->mutex);
349}