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