Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Takashi Sakamoto and committed by
Takashi Iwai
75d6d898 6fb7db90

+267 -1
+2 -1
sound/firewire/fireface/Makefile
··· 1 - snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o 1 + snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \ 2 + ff-stream.o 2 3 obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o
+243
sound/firewire/fireface/ff-stream.c
··· 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 + 13 + static 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 + */ 34 + static 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 + 70 + static 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 + 76 + static 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 + 82 + static 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 + 107 + static 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 + 118 + int 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); 129 + end: 130 + return err; 131 + } 132 + 133 + /* 134 + * This function should be called before starting streams or after stopping 135 + * streams. 136 + */ 137 + void 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 + 143 + int 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; 211 + error: 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 + 221 + void 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 + 232 + void 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 + }
+9
sound/firewire/fireface/ff.c
··· 29 29 30 30 static void ff_free(struct snd_ff *ff) 31 31 { 32 + snd_ff_stream_destroy_duplex(ff); 32 33 snd_ff_transaction_unregister(ff); 33 34 34 35 fw_unit_put(ff->unit); ··· 62 61 63 62 name_card(ff); 64 63 64 + err = snd_ff_stream_init_duplex(ff); 65 + if (err < 0) 66 + goto error; 67 + 65 68 snd_ff_proc_init(ff); 66 69 67 70 err = snd_ff_create_midi_devices(ff); ··· 83 78 return; 84 79 error: 85 80 snd_ff_transaction_unregister(ff); 81 + snd_ff_stream_destroy_duplex(ff); 86 82 snd_card_free(ff->card); 87 83 dev_info(&ff->unit->device, 88 84 "Sound card registration failed: %d\n", err); ··· 123 117 snd_fw_schedule_registration(unit, &ff->dwork); 124 118 125 119 snd_ff_transaction_reregister(ff); 120 + 121 + if (ff->registered) 122 + snd_ff_stream_update_duplex(ff); 126 123 } 127 124 128 125 static void snd_ff_remove(struct fw_unit *unit)
+13
sound/firewire/fireface/ff.h
··· 24 24 25 25 #include "../lib.h" 26 26 #include "../amdtp-stream.h" 27 + #include "../iso-resources.h" 27 28 28 29 #define SND_FF_STREAM_MODES 3 29 30 ··· 69 68 ktime_t next_ktime[SND_FF_OUT_MIDI_PORTS]; 70 69 bool rx_midi_error[SND_FF_OUT_MIDI_PORTS]; 71 70 unsigned int rx_bytes[SND_FF_OUT_MIDI_PORTS]; 71 + 72 + unsigned int substreams_counter; 73 + struct amdtp_stream tx_stream; 74 + struct amdtp_stream rx_stream; 75 + struct fw_iso_resources tx_resources; 76 + struct fw_iso_resources rx_resources; 72 77 }; 73 78 74 79 enum snd_ff_clock_src { ··· 113 106 struct snd_pcm_runtime *runtime); 114 107 int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit, 115 108 enum amdtp_stream_direction dir); 109 + 110 + int snd_ff_stream_init_duplex(struct snd_ff *ff); 111 + void snd_ff_stream_destroy_duplex(struct snd_ff *ff); 112 + int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate); 113 + void snd_ff_stream_stop_duplex(struct snd_ff *ff); 114 + void snd_ff_stream_update_duplex(struct snd_ff *ff); 116 115 117 116 void snd_ff_proc_init(struct snd_ff *ff); 118 117