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