378 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * fireworks_stream.c - a part of driver for Fireworks based devices4 *5 * Copyright (c) 2013-2014 Takashi Sakamoto6 */7#include "./fireworks.h"8 9#define READY_TIMEOUT_MS 100010 11static 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 65static 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 else74 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 stopping93// the streams.94static 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 else101 cmp_connection_destroy(&efw->in_conn);102}103 104static int105check_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 else114 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 128int 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 159static 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 185int 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 247int 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 starting271 // packet transmission.272 if (efw->is_fireworks3 && !efw->is_af9)273 tx_init_skip_cycles = 6000;274 else275 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 field286 // of CIP header in received packets. The sequence of the number of data blocks per287 // 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;299error: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 308void 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 321void 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 332void 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 340void snd_efw_stream_lock_changed(struct snd_efw *efw)341{342 efw->dev_lock_changed = true;343 wake_up(&efw->hwdep_wait);344}345 346int 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;362end:363 spin_unlock_irq(&efw->lock);364 return err;365}366 367void 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);375end:376 spin_unlock_irq(&efw->lock);377}378