aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2017-03-31 09:06:09 -0400
committerTakashi Iwai <tiwai@suse.de>2017-04-05 15:31:44 -0400
commit75d6d898977830d6d789083bf0a63ea6826124c8 (patch)
tree88dfec144484d3ce0eb0ca7291f065aec0138e18
parent6fb7db902bbe6358b39f359b917f10e3c923058c (diff)
ALSA: fireface: add stream management functionality
This commit adds management functionality for packet streaming. As long as investigating Fireface 400, there're three modes depending on sampling transmission frequency. The number of data channels in each data block is different depending on the mode. The set of available data channels for each mode might be different for each protocol and model. The length of registers for the number of isochronous channel is just three bits, therefore 0-7ch are available. When bus reset occurs on IEEE 1394 bus, the device discontinues to transmit packets. This commit aborts PCM substreams at bus reset handler. As I described in followed commits, The device manages its sampling clock independently of sampling transmission frequency against IEC 61883-6. Thus, it's a lower cost to change the sampling transmission frequency, while data fetch between streaming layer and DSP require larger buffer for resampling. As a result, device latency might tend to be larger than ASICs for IEC 61883-1/6 such as DM1000/DM1100/DM1500 (BeBoB), DiceII/TCD2210/TCD2220/TCD3070 and OXFW970/971. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/firewire/fireface/Makefile3
-rw-r--r--sound/firewire/fireface/ff-stream.c243
-rw-r--r--sound/firewire/fireface/ff.c9
-rw-r--r--sound/firewire/fireface/ff.h13
4 files changed, 267 insertions, 1 deletions
diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile
index e06e9da36581..b772fdc20101 100644
--- a/sound/firewire/fireface/Makefile
+++ b/sound/firewire/fireface/Makefile
@@ -1,2 +1,3 @@
1snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o 1snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \
2 ff-stream.o
2obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o 3obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o
diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c
new file mode 100644
index 000000000000..0ef6177aff20
--- /dev/null
+++ b/sound/firewire/fireface/ff-stream.c
@@ -0,0 +1,243 @@
1/*
2 * ff-stream.c - a part of driver for RME Fireface series
3 *
4 * Copyright (c) 2015-2017 Takashi Sakamoto
5 *
6 * Licensed under the terms of the GNU General Public License, version 2.
7 */
8
9#include "ff.h"
10
11#define CALLBACK_TIMEOUT_MS 200
12
13static int get_rate_mode(unsigned int rate, unsigned int *mode)
14{
15 int i;
16
17 for (i = 0; i < CIP_SFC_COUNT; i++) {
18 if (amdtp_rate_table[i] == rate)
19 break;
20 }
21
22 if (i == CIP_SFC_COUNT)
23 return -EINVAL;
24
25 *mode = ((int)i - 1) / 2;
26
27 return 0;
28}
29
30/*
31 * Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
32 * we can allocate between 0 and 7 channel.
33 */
34static int keep_resources(struct snd_ff *ff, unsigned int rate)
35{
36 int mode;
37 int err;
38
39 err = get_rate_mode(rate, &mode);
40 if (err < 0)
41 return err;
42
43 /* Keep resources for in-stream. */
44 err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
45 ff->spec->pcm_capture_channels[mode]);
46 if (err < 0)
47 return err;
48 ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
49 err = fw_iso_resources_allocate(&ff->tx_resources,
50 amdtp_stream_get_max_payload(&ff->tx_stream),
51 fw_parent_device(ff->unit)->max_speed);
52 if (err < 0)
53 return err;
54
55 /* Keep resources for out-stream. */
56 err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
57 ff->spec->pcm_playback_channels[mode]);
58 if (err < 0)
59 return err;
60 ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
61 err = fw_iso_resources_allocate(&ff->rx_resources,
62 amdtp_stream_get_max_payload(&ff->rx_stream),
63 fw_parent_device(ff->unit)->max_speed);
64 if (err < 0)
65 fw_iso_resources_free(&ff->tx_resources);
66
67 return err;
68}
69
70static void release_resources(struct snd_ff *ff)
71{
72 fw_iso_resources_free(&ff->tx_resources);
73 fw_iso_resources_free(&ff->rx_resources);
74}
75
76static inline void finish_session(struct snd_ff *ff)
77{
78 ff->spec->protocol->finish_session(ff);
79 ff->spec->protocol->switch_fetching_mode(ff, false);
80}
81
82static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
83{
84 int err;
85 struct fw_iso_resources *resources;
86 struct amdtp_stream *stream;
87
88 if (dir == AMDTP_IN_STREAM) {
89 resources = &ff->tx_resources;
90 stream = &ff->tx_stream;
91 } else {
92 resources = &ff->rx_resources;
93 stream = &ff->rx_stream;
94 }
95
96 err = fw_iso_resources_init(resources, ff->unit);
97 if (err < 0)
98 return err;
99
100 err = amdtp_ff_init(stream, ff->unit, dir);
101 if (err < 0)
102 fw_iso_resources_destroy(resources);
103
104 return err;
105}
106
107static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
108{
109 if (dir == AMDTP_IN_STREAM) {
110 amdtp_stream_destroy(&ff->tx_stream);
111 fw_iso_resources_destroy(&ff->tx_resources);
112 } else {
113 amdtp_stream_destroy(&ff->rx_stream);
114 fw_iso_resources_destroy(&ff->rx_resources);
115 }
116}
117
118int snd_ff_stream_init_duplex(struct snd_ff *ff)
119{
120 int err;
121
122 err = init_stream(ff, AMDTP_OUT_STREAM);
123 if (err < 0)
124 goto end;
125
126 err = init_stream(ff, AMDTP_IN_STREAM);
127 if (err < 0)
128 destroy_stream(ff, AMDTP_OUT_STREAM);
129end:
130 return err;
131}
132
133/*
134 * This function should be called before starting streams or after stopping
135 * streams.
136 */
137void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
138{
139 destroy_stream(ff, AMDTP_IN_STREAM);
140 destroy_stream(ff, AMDTP_OUT_STREAM);
141}
142
143int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
144{
145 unsigned int curr_rate;
146 enum snd_ff_clock_src src;
147 int err;
148
149 if (ff->substreams_counter == 0)
150 return 0;
151
152 err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
153 if (err < 0)
154 return err;
155 if (curr_rate != rate ||
156 amdtp_streaming_error(&ff->tx_stream) ||
157 amdtp_streaming_error(&ff->rx_stream)) {
158 finish_session(ff);
159
160 amdtp_stream_stop(&ff->tx_stream);
161 amdtp_stream_stop(&ff->rx_stream);
162
163 release_resources(ff);
164 }
165
166 /*
167 * Regardless of current source of clock signal, drivers transfer some
168 * packets. Then, the device transfers packets.
169 */
170 if (!amdtp_stream_running(&ff->rx_stream)) {
171 err = keep_resources(ff, rate);
172 if (err < 0)
173 goto error;
174
175 err = ff->spec->protocol->begin_session(ff, rate);
176 if (err < 0)
177 goto error;
178
179 err = amdtp_stream_start(&ff->rx_stream,
180 ff->rx_resources.channel,
181 fw_parent_device(ff->unit)->max_speed);
182 if (err < 0)
183 goto error;
184
185 if (!amdtp_stream_wait_callback(&ff->rx_stream,
186 CALLBACK_TIMEOUT_MS)) {
187 err = -ETIMEDOUT;
188 goto error;
189 }
190
191 err = ff->spec->protocol->switch_fetching_mode(ff, true);
192 if (err < 0)
193 goto error;
194 }
195
196 if (!amdtp_stream_running(&ff->tx_stream)) {
197 err = amdtp_stream_start(&ff->tx_stream,
198 ff->tx_resources.channel,
199 fw_parent_device(ff->unit)->max_speed);
200 if (err < 0)
201 goto error;
202
203 if (!amdtp_stream_wait_callback(&ff->tx_stream,
204 CALLBACK_TIMEOUT_MS)) {
205 err = -ETIMEDOUT;
206 goto error;
207 }
208 }
209
210 return 0;
211error:
212 amdtp_stream_stop(&ff->tx_stream);
213 amdtp_stream_stop(&ff->rx_stream);
214
215 finish_session(ff);
216 release_resources(ff);
217
218 return err;
219}
220
221void snd_ff_stream_stop_duplex(struct snd_ff *ff)
222{
223 if (ff->substreams_counter > 0)
224 return;
225
226 amdtp_stream_stop(&ff->tx_stream);
227 amdtp_stream_stop(&ff->rx_stream);
228 finish_session(ff);
229 release_resources(ff);
230}
231
232void snd_ff_stream_update_duplex(struct snd_ff *ff)
233{
234 /* The device discontinue to transfer packets. */
235 amdtp_stream_pcm_abort(&ff->tx_stream);
236 amdtp_stream_stop(&ff->tx_stream);
237
238 amdtp_stream_pcm_abort(&ff->rx_stream);
239 amdtp_stream_stop(&ff->rx_stream);
240
241 fw_iso_resources_update(&ff->tx_resources);
242 fw_iso_resources_update(&ff->rx_resources);
243}
diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c
index 22e7bcb4bd51..6bdbebd9f61b 100644
--- a/sound/firewire/fireface/ff.c
+++ b/sound/firewire/fireface/ff.c
@@ -29,6 +29,7 @@ static void name_card(struct snd_ff *ff)
29 29
30static void ff_free(struct snd_ff *ff) 30static void ff_free(struct snd_ff *ff)
31{ 31{
32 snd_ff_stream_destroy_duplex(ff);
32 snd_ff_transaction_unregister(ff); 33 snd_ff_transaction_unregister(ff);
33 34
34 fw_unit_put(ff->unit); 35 fw_unit_put(ff->unit);
@@ -61,6 +62,10 @@ static void do_registration(struct work_struct *work)
61 62
62 name_card(ff); 63 name_card(ff);
63 64
65 err = snd_ff_stream_init_duplex(ff);
66 if (err < 0)
67 goto error;
68
64 snd_ff_proc_init(ff); 69 snd_ff_proc_init(ff);
65 70
66 err = snd_ff_create_midi_devices(ff); 71 err = snd_ff_create_midi_devices(ff);
@@ -78,6 +83,7 @@ static void do_registration(struct work_struct *work)
78 return; 83 return;
79error: 84error:
80 snd_ff_transaction_unregister(ff); 85 snd_ff_transaction_unregister(ff);
86 snd_ff_stream_destroy_duplex(ff);
81 snd_card_free(ff->card); 87 snd_card_free(ff->card);
82 dev_info(&ff->unit->device, 88 dev_info(&ff->unit->device,
83 "Sound card registration failed: %d\n", err); 89 "Sound card registration failed: %d\n", err);
@@ -117,6 +123,9 @@ static void snd_ff_update(struct fw_unit *unit)
117 snd_fw_schedule_registration(unit, &ff->dwork); 123 snd_fw_schedule_registration(unit, &ff->dwork);
118 124
119 snd_ff_transaction_reregister(ff); 125 snd_ff_transaction_reregister(ff);
126
127 if (ff->registered)
128 snd_ff_stream_update_duplex(ff);
120} 129}
121 130
122static void snd_ff_remove(struct fw_unit *unit) 131static void snd_ff_remove(struct fw_unit *unit)
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
index fa7242fd9b8c..6599c11744ae 100644
--- a/sound/firewire/fireface/ff.h
+++ b/sound/firewire/fireface/ff.h
@@ -24,6 +24,7 @@
24 24
25#include "../lib.h" 25#include "../lib.h"
26#include "../amdtp-stream.h" 26#include "../amdtp-stream.h"
27#include "../iso-resources.h"
27 28
28#define SND_FF_STREAM_MODES 3 29#define SND_FF_STREAM_MODES 3
29 30
@@ -68,6 +69,12 @@ struct snd_ff {
68 ktime_t next_ktime[SND_FF_OUT_MIDI_PORTS]; 69 ktime_t next_ktime[SND_FF_OUT_MIDI_PORTS];
69 bool rx_midi_error[SND_FF_OUT_MIDI_PORTS]; 70 bool rx_midi_error[SND_FF_OUT_MIDI_PORTS];
70 unsigned int rx_bytes[SND_FF_OUT_MIDI_PORTS]; 71 unsigned int rx_bytes[SND_FF_OUT_MIDI_PORTS];
72
73 unsigned int substreams_counter;
74 struct amdtp_stream tx_stream;
75 struct amdtp_stream rx_stream;
76 struct fw_iso_resources tx_resources;
77 struct fw_iso_resources rx_resources;
71}; 78};
72 79
73enum snd_ff_clock_src { 80enum snd_ff_clock_src {
@@ -107,6 +114,12 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
107int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit, 114int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
108 enum amdtp_stream_direction dir); 115 enum amdtp_stream_direction dir);
109 116
117int snd_ff_stream_init_duplex(struct snd_ff *ff);
118void snd_ff_stream_destroy_duplex(struct snd_ff *ff);
119int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);
120void snd_ff_stream_stop_duplex(struct snd_ff *ff);
121void snd_ff_stream_update_duplex(struct snd_ff *ff);
122
110void snd_ff_proc_init(struct snd_ff *ff); 123void snd_ff_proc_init(struct snd_ff *ff);
111 124
112int snd_ff_create_midi_devices(struct snd_ff *ff); 125int snd_ff_create_midi_devices(struct snd_ff *ff);