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