1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * fireworks_stream.c - a part of driver for Fireworks based devices 4 * 5 * Copyright (c) 2013-2014 Takashi Sakamoto 6 */ 7 #include "./fireworks.h" 8 9 #define READY_TIMEOUT_MS 1000 10 11 static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream) 12 { 13 struct cmp_connection *conn; 14 enum cmp_direction c_dir; 15 enum amdtp_stream_direction s_dir; 16 int err; 17 18 if (stream == &efw->tx_stream) { 19 conn = &efw->out_conn; 20 c_dir = CMP_OUTPUT; 21 s_dir = AMDTP_IN_STREAM; 22 } else { 23 conn = &efw->in_conn; 24 c_dir = CMP_INPUT; 25 s_dir = AMDTP_OUT_STREAM; 26 } 27 28 err = cmp_connection_init(conn, efw->unit, c_dir, 0); 29 if (err < 0) 30 return err; 31 32 err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING | CIP_UNAWARE_SYT); 33 if (err < 0) { 34 amdtp_stream_destroy(stream); 35 cmp_connection_destroy(conn); 36 return err; 37 } 38 39 if (stream == &efw->tx_stream) { 40 // Fireworks transmits NODATA packets with TAG0. 41 efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0; 42 // Fireworks has its own meaning for dbc. 43 efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT; 44 // Fireworks reset dbc at bus reset. 45 efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK; 46 // But Recent firmwares starts packets with non-zero dbc. 47 // Driver version 5.7.6 installs firmware version 5.7.3. 48 if (efw->is_fireworks3 && 49 (efw->firmware_version == 0x5070000 || 50 efw->firmware_version == 0x5070300 || 51 efw->firmware_version == 0x5080000)) 52 efw->tx_stream.flags |= CIP_UNALIGHED_DBC; 53 // AudioFire9 always reports wrong dbs. Onyx 1200F with the latest firmware (v4.6.0) 54 // also report wrong dbs at 88.2 kHz or greater. 55 if (efw->is_af9 || efw->firmware_version == 0x4060000) 56 efw->tx_stream.flags |= CIP_WRONG_DBS; 57 // Firmware version 5.5 reports fixed interval for dbc. 58 if (efw->firmware_version == 0x5050000) 59 efw->tx_stream.ctx_data.tx.dbc_interval = 8; 60 } 61 62 return err; 63 } 64 65 static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream, 66 unsigned int rate) 67 { 68 struct cmp_connection *conn; 69 int err; 70 71 if (stream == &efw->tx_stream) 72 conn = &efw->out_conn; 73 else 74 conn = &efw->in_conn; 75 76 // Establish connection via CMP. 77 err = cmp_connection_establish(conn); 78 if (err < 0) 79 return err; 80 81 // Start amdtp stream. 82 err = amdtp_domain_add_stream(&efw->domain, stream, 83 conn->resources.channel, conn->speed); 84 if (err < 0) { 85 cmp_connection_break(conn); 86 return err; 87 } 88 89 return 0; 90 } 91 92 // This function should be called before starting the stream or after stopping 93 // the streams. 94 static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream) 95 { 96 amdtp_stream_destroy(stream); 97 98 if (stream == &efw->tx_stream) 99 cmp_connection_destroy(&efw->out_conn); 100 else 101 cmp_connection_destroy(&efw->in_conn); 102 } 103 104 static int 105 check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s) 106 { 107 struct cmp_connection *conn; 108 bool used; 109 int err; 110 111 if (s == &efw->tx_stream) 112 conn = &efw->out_conn; 113 else 114 conn = &efw->in_conn; 115 116 err = cmp_connection_check_used(conn, &used); 117 if ((err >= 0) && used && !amdtp_stream_running(s)) { 118 dev_err(&efw->unit->device, 119 "Connection established by others: %cPCR[%d]\n", 120 (conn->direction == CMP_OUTPUT) ? 'o' : 'i', 121 conn->pcr_index); 122 err = -EBUSY; 123 } 124 125 return err; 126 } 127 128 int snd_efw_stream_init_duplex(struct snd_efw *efw) 129 { 130 int err; 131 132 err = init_stream(efw, &efw->tx_stream); 133 if (err < 0) 134 return err; 135 136 err = init_stream(efw, &efw->rx_stream); 137 if (err < 0) { 138 destroy_stream(efw, &efw->tx_stream); 139 return err; 140 } 141 142 err = amdtp_domain_init(&efw->domain); 143 if (err < 0) { 144 destroy_stream(efw, &efw->tx_stream); 145 destroy_stream(efw, &efw->rx_stream); 146 return err; 147 } 148 149 // set IEC61883 compliant mode (actually not fully compliant...). 150 err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883); 151 if (err < 0) { 152 destroy_stream(efw, &efw->tx_stream); 153 destroy_stream(efw, &efw->rx_stream); 154 } 155 156 return err; 157 } 158 159 static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream, 160 unsigned int rate, unsigned int mode) 161 { 162 unsigned int pcm_channels; 163 unsigned int midi_ports; 164 struct cmp_connection *conn; 165 int err; 166 167 if (stream == &efw->tx_stream) { 168 pcm_channels = efw->pcm_capture_channels[mode]; 169 midi_ports = efw->midi_out_ports; 170 conn = &efw->out_conn; 171 } else { 172 pcm_channels = efw->pcm_playback_channels[mode]; 173 midi_ports = efw->midi_in_ports; 174 conn = &efw->in_conn; 175 } 176 177 err = amdtp_am824_set_parameters(stream, rate, pcm_channels, 178 midi_ports, false); 179 if (err < 0) 180 return err; 181 182 return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); 183 } 184 185 int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate, 186 unsigned int frames_per_period, 187 unsigned int frames_per_buffer) 188 { 189 unsigned int curr_rate; 190 int err; 191 192 // Considering JACK/FFADO streaming: 193 // TODO: This can be removed hwdep functionality becomes popular. 194 err = check_connection_used_by_others(efw, &efw->rx_stream); 195 if (err < 0) 196 return err; 197 198 // stop streams if rate is different. 199 err = snd_efw_command_get_sampling_rate(efw, &curr_rate); 200 if (err < 0) 201 return err; 202 if (rate == 0) 203 rate = curr_rate; 204 if (rate != curr_rate) { 205 amdtp_domain_stop(&efw->domain); 206 207 cmp_connection_break(&efw->out_conn); 208 cmp_connection_break(&efw->in_conn); 209 210 cmp_connection_release(&efw->out_conn); 211 cmp_connection_release(&efw->in_conn); 212 } 213 214 if (efw->substreams_counter == 0 || rate != curr_rate) { 215 unsigned int mode; 216 217 err = snd_efw_command_set_sampling_rate(efw, rate); 218 if (err < 0) 219 return err; 220 221 err = snd_efw_get_multiplier_mode(rate, &mode); 222 if (err < 0) 223 return err; 224 225 err = keep_resources(efw, &efw->tx_stream, rate, mode); 226 if (err < 0) 227 return err; 228 229 err = keep_resources(efw, &efw->rx_stream, rate, mode); 230 if (err < 0) { 231 cmp_connection_release(&efw->in_conn); 232 return err; 233 } 234 235 err = amdtp_domain_set_events_per_period(&efw->domain, 236 frames_per_period, frames_per_buffer); 237 if (err < 0) { 238 cmp_connection_release(&efw->in_conn); 239 cmp_connection_release(&efw->out_conn); 240 return err; 241 } 242 } 243 244 return 0; 245 } 246 247 int snd_efw_stream_start_duplex(struct snd_efw *efw) 248 { 249 unsigned int rate; 250 int err = 0; 251 252 // Need no substreams. 253 if (efw->substreams_counter == 0) 254 return -EIO; 255 256 if (amdtp_streaming_error(&efw->rx_stream) || 257 amdtp_streaming_error(&efw->tx_stream)) { 258 amdtp_domain_stop(&efw->domain); 259 cmp_connection_break(&efw->out_conn); 260 cmp_connection_break(&efw->in_conn); 261 } 262 263 err = snd_efw_command_get_sampling_rate(efw, &rate); 264 if (err < 0) 265 return err; 266 267 if (!amdtp_stream_running(&efw->rx_stream)) { 268 unsigned int tx_init_skip_cycles; 269 270 // Audiofire 2/4 skip an isochronous cycle several thousands after starting 271 // packet transmission. 272 if (efw->is_fireworks3 && !efw->is_af9) 273 tx_init_skip_cycles = 6000; 274 else 275 tx_init_skip_cycles = 0; 276 277 err = start_stream(efw, &efw->rx_stream, rate); 278 if (err < 0) 279 goto error; 280 281 err = start_stream(efw, &efw->tx_stream, rate); 282 if (err < 0) 283 goto error; 284 285 // NOTE: The device ignores presentation time expressed by the value of syt field 286 // of CIP header in received packets. The sequence of the number of data blocks per 287 // packet is important for media clock recovery. 288 err = amdtp_domain_start(&efw->domain, tx_init_skip_cycles, true, false); 289 if (err < 0) 290 goto error; 291 292 if (!amdtp_domain_wait_ready(&efw->domain, READY_TIMEOUT_MS)) { 293 err = -ETIMEDOUT; 294 goto error; 295 } 296 } 297 298 return 0; 299 error: 300 amdtp_domain_stop(&efw->domain); 301 302 cmp_connection_break(&efw->out_conn); 303 cmp_connection_break(&efw->in_conn); 304 305 return err; 306 } 307 308 void snd_efw_stream_stop_duplex(struct snd_efw *efw) 309 { 310 if (efw->substreams_counter == 0) { 311 amdtp_domain_stop(&efw->domain); 312 313 cmp_connection_break(&efw->out_conn); 314 cmp_connection_break(&efw->in_conn); 315 316 cmp_connection_release(&efw->out_conn); 317 cmp_connection_release(&efw->in_conn); 318 } 319 } 320 321 void snd_efw_stream_update_duplex(struct snd_efw *efw) 322 { 323 amdtp_domain_stop(&efw->domain); 324 325 cmp_connection_break(&efw->out_conn); 326 cmp_connection_break(&efw->in_conn); 327 328 amdtp_stream_pcm_abort(&efw->rx_stream); 329 amdtp_stream_pcm_abort(&efw->tx_stream); 330 } 331 332 void snd_efw_stream_destroy_duplex(struct snd_efw *efw) 333 { 334 amdtp_domain_destroy(&efw->domain); 335 336 destroy_stream(efw, &efw->rx_stream); 337 destroy_stream(efw, &efw->tx_stream); 338 } 339 340 void snd_efw_stream_lock_changed(struct snd_efw *efw) 341 { 342 efw->dev_lock_changed = true; 343 wake_up(&efw->hwdep_wait); 344 } 345 346 int snd_efw_stream_lock_try(struct snd_efw *efw) 347 { 348 int err; 349 350 spin_lock_irq(&efw->lock); 351 352 /* user land lock this */ 353 if (efw->dev_lock_count < 0) { 354 err = -EBUSY; 355 goto end; 356 } 357 358 /* this is the first time */ 359 if (efw->dev_lock_count++ == 0) 360 snd_efw_stream_lock_changed(efw); 361 err = 0; 362 end: 363 spin_unlock_irq(&efw->lock); 364 return err; 365 } 366 367 void snd_efw_stream_lock_release(struct snd_efw *efw) 368 { 369 spin_lock_irq(&efw->lock); 370 371 if (WARN_ON(efw->dev_lock_count <= 0)) 372 goto end; 373 if (--efw->dev_lock_count == 0) 374 snd_efw_stream_lock_changed(efw); 375 end: 376 spin_unlock_irq(&efw->lock); 377 } 378
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.