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 int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, 14 enum snd_ff_stream_mode *mode) 15 { 16 static const enum snd_ff_stream_mode modes[] = { 17 [CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW, 18 [CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW, 19 [CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW, 20 [CIP_SFC_88200] = SND_FF_STREAM_MODE_MID, 21 [CIP_SFC_96000] = SND_FF_STREAM_MODE_MID, 22 [CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH, 23 [CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH, 24 }; 25 26 if (sfc >= CIP_SFC_COUNT) 27 return -EINVAL; 28 29 *mode = modes[sfc]; 30 31 return 0; 32 } 33 34 static void release_resources(struct snd_ff *ff) 35 { 36 fw_iso_resources_free(&ff->tx_resources); 37 fw_iso_resources_free(&ff->rx_resources); 38 } 39 40 static inline void finish_session(struct snd_ff *ff) 41 { 42 ff->spec->protocol->finish_session(ff); 43 ff->spec->protocol->switch_fetching_mode(ff, false); 44 } 45 46 static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) 47 { 48 int err; 49 struct fw_iso_resources *resources; 50 struct amdtp_stream *stream; 51 52 if (dir == AMDTP_IN_STREAM) { 53 resources = &ff->tx_resources; 54 stream = &ff->tx_stream; 55 } else { 56 resources = &ff->rx_resources; 57 stream = &ff->rx_stream; 58 } 59 60 err = fw_iso_resources_init(resources, ff->unit); 61 if (err < 0) 62 return err; 63 64 err = amdtp_ff_init(stream, ff->unit, dir); 65 if (err < 0) 66 fw_iso_resources_destroy(resources); 67 68 return err; 69 } 70 71 static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) 72 { 73 if (dir == AMDTP_IN_STREAM) { 74 amdtp_stream_destroy(&ff->tx_stream); 75 fw_iso_resources_destroy(&ff->tx_resources); 76 } else { 77 amdtp_stream_destroy(&ff->rx_stream); 78 fw_iso_resources_destroy(&ff->rx_resources); 79 } 80 } 81 82 int snd_ff_stream_init_duplex(struct snd_ff *ff) 83 { 84 int err; 85 86 err = init_stream(ff, AMDTP_OUT_STREAM); 87 if (err < 0) 88 goto end; 89 90 err = init_stream(ff, AMDTP_IN_STREAM); 91 if (err < 0) 92 destroy_stream(ff, AMDTP_OUT_STREAM); 93 end: 94 return err; 95 } 96 97 /* 98 * This function should be called before starting streams or after stopping 99 * streams. 100 */ 101 void snd_ff_stream_destroy_duplex(struct snd_ff *ff) 102 { 103 destroy_stream(ff, AMDTP_IN_STREAM); 104 destroy_stream(ff, AMDTP_OUT_STREAM); 105 } 106 107 int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) 108 { 109 unsigned int curr_rate; 110 enum snd_ff_clock_src src; 111 int err; 112 113 if (ff->substreams_counter == 0) 114 return 0; 115 116 err = ff->spec->protocol->get_clock(ff, &curr_rate, &src); 117 if (err < 0) 118 return err; 119 if (curr_rate != rate || 120 amdtp_streaming_error(&ff->tx_stream) || 121 amdtp_streaming_error(&ff->rx_stream)) { 122 finish_session(ff); 123 124 amdtp_stream_stop(&ff->tx_stream); 125 amdtp_stream_stop(&ff->rx_stream); 126 127 release_resources(ff); 128 } 129 130 /* 131 * Regardless of current source of clock signal, drivers transfer some 132 * packets. Then, the device transfers packets. 133 */ 134 if (!amdtp_stream_running(&ff->rx_stream)) { 135 enum snd_ff_stream_mode mode; 136 int i; 137 138 for (i = 0; i < CIP_SFC_COUNT; ++i) { 139 if (amdtp_rate_table[i] == rate) 140 break; 141 } 142 if (i >= CIP_SFC_COUNT) 143 return -EINVAL; 144 145 err = snd_ff_stream_get_multiplier_mode(i, &mode); 146 if (err < 0) 147 return err; 148 149 err = amdtp_ff_set_parameters(&ff->tx_stream, rate, 150 ff->spec->pcm_capture_channels[mode]); 151 if (err < 0) 152 return err; 153 154 err = amdtp_ff_set_parameters(&ff->rx_stream, rate, 155 ff->spec->pcm_playback_channels[mode]); 156 if (err < 0) 157 return err; 158 159 err = ff->spec->protocol->begin_session(ff, rate); 160 if (err < 0) 161 goto error; 162 163 err = amdtp_stream_start(&ff->rx_stream, 164 ff->rx_resources.channel, 165 fw_parent_device(ff->unit)->max_speed); 166 if (err < 0) 167 goto error; 168 169 if (!amdtp_stream_wait_callback(&ff->rx_stream, 170 CALLBACK_TIMEOUT_MS)) { 171 err = -ETIMEDOUT; 172 goto error; 173 } 174 175 err = ff->spec->protocol->switch_fetching_mode(ff, true); 176 if (err < 0) 177 goto error; 178 } 179 180 if (!amdtp_stream_running(&ff->tx_stream)) { 181 err = amdtp_stream_start(&ff->tx_stream, 182 ff->tx_resources.channel, 183 fw_parent_device(ff->unit)->max_speed); 184 if (err < 0) 185 goto error; 186 187 if (!amdtp_stream_wait_callback(&ff->tx_stream, 188 CALLBACK_TIMEOUT_MS)) { 189 err = -ETIMEDOUT; 190 goto error; 191 } 192 } 193 194 return 0; 195 error: 196 amdtp_stream_stop(&ff->tx_stream); 197 amdtp_stream_stop(&ff->rx_stream); 198 199 finish_session(ff); 200 release_resources(ff); 201 202 return err; 203 } 204 205 void snd_ff_stream_stop_duplex(struct snd_ff *ff) 206 { 207 if (ff->substreams_counter > 0) 208 return; 209 210 amdtp_stream_stop(&ff->tx_stream); 211 amdtp_stream_stop(&ff->rx_stream); 212 finish_session(ff); 213 release_resources(ff); 214 } 215 216 void snd_ff_stream_update_duplex(struct snd_ff *ff) 217 { 218 /* The device discontinue to transfer packets. */ 219 amdtp_stream_pcm_abort(&ff->tx_stream); 220 amdtp_stream_stop(&ff->tx_stream); 221 222 amdtp_stream_pcm_abort(&ff->rx_stream); 223 amdtp_stream_stop(&ff->rx_stream); 224 225 fw_iso_resources_update(&ff->tx_resources); 226 fw_iso_resources_update(&ff->rx_resources); 227 } 228 229 void snd_ff_stream_lock_changed(struct snd_ff *ff) 230 { 231 ff->dev_lock_changed = true; 232 wake_up(&ff->hwdep_wait); 233 } 234 235 int snd_ff_stream_lock_try(struct snd_ff *ff) 236 { 237 int err; 238 239 spin_lock_irq(&ff->lock); 240 241 /* user land lock this */ 242 if (ff->dev_lock_count < 0) { 243 err = -EBUSY; 244 goto end; 245 } 246 247 /* this is the first time */ 248 if (ff->dev_lock_count++ == 0) 249 snd_ff_stream_lock_changed(ff); 250 err = 0; 251 end: 252 spin_unlock_irq(&ff->lock); 253 return err; 254 } 255 256 void snd_ff_stream_lock_release(struct snd_ff *ff) 257 { 258 spin_lock_irq(&ff->lock); 259 260 if (WARN_ON(ff->dev_lock_count <= 0)) 261 goto end; 262 if (--ff->dev_lock_count == 0) 263 snd_ff_stream_lock_changed(ff); 264 end: 265 spin_unlock_irq(&ff->lock); 266 } 267