Path: blob/master/sound/firewire/fireworks/fireworks_stream.c
26424 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* fireworks_stream.c - a part of driver for Fireworks based devices3*4* Copyright (c) 2013-2014 Takashi Sakamoto5*/6#include "./fireworks.h"78#define READY_TIMEOUT_MS 1000910static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)11{12struct cmp_connection *conn;13enum cmp_direction c_dir;14enum amdtp_stream_direction s_dir;15int err;1617if (stream == &efw->tx_stream) {18conn = &efw->out_conn;19c_dir = CMP_OUTPUT;20s_dir = AMDTP_IN_STREAM;21} else {22conn = &efw->in_conn;23c_dir = CMP_INPUT;24s_dir = AMDTP_OUT_STREAM;25}2627err = cmp_connection_init(conn, efw->unit, c_dir, 0);28if (err < 0)29return err;3031err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING | CIP_UNAWARE_SYT);32if (err < 0) {33amdtp_stream_destroy(stream);34cmp_connection_destroy(conn);35return err;36}3738if (stream == &efw->tx_stream) {39// Fireworks transmits NODATA packets with TAG0.40efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;41// Fireworks has its own meaning for dbc.42efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;43// Fireworks reset dbc at bus reset.44efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;45// But Recent firmwares starts packets with non-zero dbc.46// Driver version 5.7.6 installs firmware version 5.7.3.47if (efw->is_fireworks3 &&48(efw->firmware_version == 0x5070000 ||49efw->firmware_version == 0x5070300 ||50efw->firmware_version == 0x5080000))51efw->tx_stream.flags |= CIP_UNALIGHED_DBC;52// AudioFire9 always reports wrong dbs. Onyx 1200F with the latest firmware (v4.6.0)53// also report wrong dbs at 88.2 kHz or greater.54if (efw->is_af9 || efw->firmware_version == 0x4060000)55efw->tx_stream.flags |= CIP_WRONG_DBS;56// Firmware version 5.5 reports fixed interval for dbc.57if (efw->firmware_version == 0x5050000)58efw->tx_stream.ctx_data.tx.dbc_interval = 8;59}6061return err;62}6364static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,65unsigned int rate)66{67struct cmp_connection *conn;68int err;6970if (stream == &efw->tx_stream)71conn = &efw->out_conn;72else73conn = &efw->in_conn;7475// Establish connection via CMP.76err = cmp_connection_establish(conn);77if (err < 0)78return err;7980// Start amdtp stream.81err = amdtp_domain_add_stream(&efw->domain, stream,82conn->resources.channel, conn->speed);83if (err < 0) {84cmp_connection_break(conn);85return err;86}8788return 0;89}9091// This function should be called before starting the stream or after stopping92// the streams.93static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)94{95amdtp_stream_destroy(stream);9697if (stream == &efw->tx_stream)98cmp_connection_destroy(&efw->out_conn);99else100cmp_connection_destroy(&efw->in_conn);101}102103static int104check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)105{106struct cmp_connection *conn;107bool used;108int err;109110if (s == &efw->tx_stream)111conn = &efw->out_conn;112else113conn = &efw->in_conn;114115err = cmp_connection_check_used(conn, &used);116if ((err >= 0) && used && !amdtp_stream_running(s)) {117dev_err(&efw->unit->device,118"Connection established by others: %cPCR[%d]\n",119(conn->direction == CMP_OUTPUT) ? 'o' : 'i',120conn->pcr_index);121err = -EBUSY;122}123124return err;125}126127int snd_efw_stream_init_duplex(struct snd_efw *efw)128{129int err;130131err = init_stream(efw, &efw->tx_stream);132if (err < 0)133return err;134135err = init_stream(efw, &efw->rx_stream);136if (err < 0) {137destroy_stream(efw, &efw->tx_stream);138return err;139}140141err = amdtp_domain_init(&efw->domain);142if (err < 0) {143destroy_stream(efw, &efw->tx_stream);144destroy_stream(efw, &efw->rx_stream);145return err;146}147148// set IEC61883 compliant mode (actually not fully compliant...).149err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);150if (err < 0) {151destroy_stream(efw, &efw->tx_stream);152destroy_stream(efw, &efw->rx_stream);153}154155return err;156}157158static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,159unsigned int rate, unsigned int mode)160{161unsigned int pcm_channels;162unsigned int midi_ports;163struct cmp_connection *conn;164int err;165166if (stream == &efw->tx_stream) {167pcm_channels = efw->pcm_capture_channels[mode];168midi_ports = efw->midi_out_ports;169conn = &efw->out_conn;170} else {171pcm_channels = efw->pcm_playback_channels[mode];172midi_ports = efw->midi_in_ports;173conn = &efw->in_conn;174}175176err = amdtp_am824_set_parameters(stream, rate, pcm_channels,177midi_ports, false);178if (err < 0)179return err;180181return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));182}183184int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate,185unsigned int frames_per_period,186unsigned int frames_per_buffer)187{188unsigned int curr_rate;189int err;190191// Considering JACK/FFADO streaming:192// TODO: This can be removed hwdep functionality becomes popular.193err = check_connection_used_by_others(efw, &efw->rx_stream);194if (err < 0)195return err;196197// stop streams if rate is different.198err = snd_efw_command_get_sampling_rate(efw, &curr_rate);199if (err < 0)200return err;201if (rate == 0)202rate = curr_rate;203if (rate != curr_rate) {204amdtp_domain_stop(&efw->domain);205206cmp_connection_break(&efw->out_conn);207cmp_connection_break(&efw->in_conn);208209cmp_connection_release(&efw->out_conn);210cmp_connection_release(&efw->in_conn);211}212213if (efw->substreams_counter == 0 || rate != curr_rate) {214unsigned int mode;215216err = snd_efw_command_set_sampling_rate(efw, rate);217if (err < 0)218return err;219220err = snd_efw_get_multiplier_mode(rate, &mode);221if (err < 0)222return err;223224err = keep_resources(efw, &efw->tx_stream, rate, mode);225if (err < 0)226return err;227228err = keep_resources(efw, &efw->rx_stream, rate, mode);229if (err < 0) {230cmp_connection_release(&efw->in_conn);231return err;232}233234err = amdtp_domain_set_events_per_period(&efw->domain,235frames_per_period, frames_per_buffer);236if (err < 0) {237cmp_connection_release(&efw->in_conn);238cmp_connection_release(&efw->out_conn);239return err;240}241}242243return 0;244}245246int snd_efw_stream_start_duplex(struct snd_efw *efw)247{248unsigned int rate;249int err = 0;250251// Need no substreams.252if (efw->substreams_counter == 0)253return -EIO;254255if (amdtp_streaming_error(&efw->rx_stream) ||256amdtp_streaming_error(&efw->tx_stream)) {257amdtp_domain_stop(&efw->domain);258cmp_connection_break(&efw->out_conn);259cmp_connection_break(&efw->in_conn);260}261262err = snd_efw_command_get_sampling_rate(efw, &rate);263if (err < 0)264return err;265266if (!amdtp_stream_running(&efw->rx_stream)) {267unsigned int tx_init_skip_cycles;268269// Audiofire 2/4 skip an isochronous cycle several thousands after starting270// packet transmission.271if (efw->is_fireworks3 && !efw->is_af9)272tx_init_skip_cycles = 6000;273else274tx_init_skip_cycles = 0;275276err = start_stream(efw, &efw->rx_stream, rate);277if (err < 0)278goto error;279280err = start_stream(efw, &efw->tx_stream, rate);281if (err < 0)282goto error;283284// NOTE: The device ignores presentation time expressed by the value of syt field285// of CIP header in received packets. The sequence of the number of data blocks per286// packet is important for media clock recovery.287err = amdtp_domain_start(&efw->domain, tx_init_skip_cycles, true, false);288if (err < 0)289goto error;290291if (!amdtp_domain_wait_ready(&efw->domain, READY_TIMEOUT_MS)) {292err = -ETIMEDOUT;293goto error;294}295}296297return 0;298error:299amdtp_domain_stop(&efw->domain);300301cmp_connection_break(&efw->out_conn);302cmp_connection_break(&efw->in_conn);303304return err;305}306307void snd_efw_stream_stop_duplex(struct snd_efw *efw)308{309if (efw->substreams_counter == 0) {310amdtp_domain_stop(&efw->domain);311312cmp_connection_break(&efw->out_conn);313cmp_connection_break(&efw->in_conn);314315cmp_connection_release(&efw->out_conn);316cmp_connection_release(&efw->in_conn);317}318}319320void snd_efw_stream_update_duplex(struct snd_efw *efw)321{322amdtp_domain_stop(&efw->domain);323324cmp_connection_break(&efw->out_conn);325cmp_connection_break(&efw->in_conn);326327amdtp_stream_pcm_abort(&efw->rx_stream);328amdtp_stream_pcm_abort(&efw->tx_stream);329}330331void snd_efw_stream_destroy_duplex(struct snd_efw *efw)332{333amdtp_domain_destroy(&efw->domain);334335destroy_stream(efw, &efw->rx_stream);336destroy_stream(efw, &efw->tx_stream);337}338339void snd_efw_stream_lock_changed(struct snd_efw *efw)340{341efw->dev_lock_changed = true;342wake_up(&efw->hwdep_wait);343}344345int snd_efw_stream_lock_try(struct snd_efw *efw)346{347int err;348349spin_lock_irq(&efw->lock);350351/* user land lock this */352if (efw->dev_lock_count < 0) {353err = -EBUSY;354goto end;355}356357/* this is the first time */358if (efw->dev_lock_count++ == 0)359snd_efw_stream_lock_changed(efw);360err = 0;361end:362spin_unlock_irq(&efw->lock);363return err;364}365366void snd_efw_stream_lock_release(struct snd_efw *efw)367{368spin_lock_irq(&efw->lock);369370if (WARN_ON(efw->dev_lock_count <= 0))371goto end;372if (--efw->dev_lock_count == 0)373snd_efw_stream_lock_changed(efw);374end:375spin_unlock_irq(&efw->lock);376}377378379