162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ff-stream.c - a part of driver for RME Fireface series 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015-2017 Takashi Sakamoto 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "ff.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define READY_TIMEOUT_MS 200 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ciint snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, 1362306a36Sopenharmony_ci enum snd_ff_stream_mode *mode) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci static const enum snd_ff_stream_mode modes[] = { 1662306a36Sopenharmony_ci [CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW, 1762306a36Sopenharmony_ci [CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW, 1862306a36Sopenharmony_ci [CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW, 1962306a36Sopenharmony_ci [CIP_SFC_88200] = SND_FF_STREAM_MODE_MID, 2062306a36Sopenharmony_ci [CIP_SFC_96000] = SND_FF_STREAM_MODE_MID, 2162306a36Sopenharmony_ci [CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH, 2262306a36Sopenharmony_ci [CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH, 2362306a36Sopenharmony_ci }; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci if (sfc >= CIP_SFC_COUNT) 2662306a36Sopenharmony_ci return -EINVAL; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci *mode = modes[sfc]; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return 0; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic inline void finish_session(struct snd_ff *ff) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci ff->spec->protocol->finish_session(ff); 3662306a36Sopenharmony_ci ff->spec->protocol->switch_fetching_mode(ff, false); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int init_stream(struct snd_ff *ff, struct amdtp_stream *s) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct fw_iso_resources *resources; 4262306a36Sopenharmony_ci enum amdtp_stream_direction dir; 4362306a36Sopenharmony_ci int err; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (s == &ff->tx_stream) { 4662306a36Sopenharmony_ci resources = &ff->tx_resources; 4762306a36Sopenharmony_ci dir = AMDTP_IN_STREAM; 4862306a36Sopenharmony_ci } else { 4962306a36Sopenharmony_ci resources = &ff->rx_resources; 5062306a36Sopenharmony_ci dir = AMDTP_OUT_STREAM; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci err = fw_iso_resources_init(resources, ff->unit); 5462306a36Sopenharmony_ci if (err < 0) 5562306a36Sopenharmony_ci return err; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci err = amdtp_ff_init(s, ff->unit, dir); 5862306a36Sopenharmony_ci if (err < 0) 5962306a36Sopenharmony_ci fw_iso_resources_destroy(resources); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return err; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci amdtp_stream_destroy(s); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (s == &ff->tx_stream) 6962306a36Sopenharmony_ci fw_iso_resources_destroy(&ff->tx_resources); 7062306a36Sopenharmony_ci else 7162306a36Sopenharmony_ci fw_iso_resources_destroy(&ff->rx_resources); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciint snd_ff_stream_init_duplex(struct snd_ff *ff) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int err; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci err = init_stream(ff, &ff->rx_stream); 7962306a36Sopenharmony_ci if (err < 0) 8062306a36Sopenharmony_ci return err; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci err = init_stream(ff, &ff->tx_stream); 8362306a36Sopenharmony_ci if (err < 0) { 8462306a36Sopenharmony_ci destroy_stream(ff, &ff->rx_stream); 8562306a36Sopenharmony_ci return err; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci err = amdtp_domain_init(&ff->domain); 8962306a36Sopenharmony_ci if (err < 0) { 9062306a36Sopenharmony_ci destroy_stream(ff, &ff->rx_stream); 9162306a36Sopenharmony_ci destroy_stream(ff, &ff->tx_stream); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return err; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * This function should be called before starting streams or after stopping 9962306a36Sopenharmony_ci * streams. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_civoid snd_ff_stream_destroy_duplex(struct snd_ff *ff) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci amdtp_domain_destroy(&ff->domain); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci destroy_stream(ff, &ff->rx_stream); 10662306a36Sopenharmony_ci destroy_stream(ff, &ff->tx_stream); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciint snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate, 11062306a36Sopenharmony_ci unsigned int frames_per_period, 11162306a36Sopenharmony_ci unsigned int frames_per_buffer) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci unsigned int curr_rate; 11462306a36Sopenharmony_ci enum snd_ff_clock_src src; 11562306a36Sopenharmony_ci int err; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci err = ff->spec->protocol->get_clock(ff, &curr_rate, &src); 11862306a36Sopenharmony_ci if (err < 0) 11962306a36Sopenharmony_ci return err; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (ff->substreams_counter == 0 || curr_rate != rate) { 12262306a36Sopenharmony_ci enum snd_ff_stream_mode mode; 12362306a36Sopenharmony_ci int i; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci amdtp_domain_stop(&ff->domain); 12662306a36Sopenharmony_ci finish_session(ff); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci fw_iso_resources_free(&ff->tx_resources); 12962306a36Sopenharmony_ci fw_iso_resources_free(&ff->rx_resources); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (i = 0; i < CIP_SFC_COUNT; ++i) { 13262306a36Sopenharmony_ci if (amdtp_rate_table[i] == rate) 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci if (i >= CIP_SFC_COUNT) 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci err = snd_ff_stream_get_multiplier_mode(i, &mode); 13962306a36Sopenharmony_ci if (err < 0) 14062306a36Sopenharmony_ci return err; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci err = amdtp_ff_set_parameters(&ff->tx_stream, rate, 14362306a36Sopenharmony_ci ff->spec->pcm_capture_channels[mode]); 14462306a36Sopenharmony_ci if (err < 0) 14562306a36Sopenharmony_ci return err; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci err = amdtp_ff_set_parameters(&ff->rx_stream, rate, 14862306a36Sopenharmony_ci ff->spec->pcm_playback_channels[mode]); 14962306a36Sopenharmony_ci if (err < 0) 15062306a36Sopenharmony_ci return err; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci err = ff->spec->protocol->allocate_resources(ff, rate); 15362306a36Sopenharmony_ci if (err < 0) 15462306a36Sopenharmony_ci return err; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci err = amdtp_domain_set_events_per_period(&ff->domain, 15762306a36Sopenharmony_ci frames_per_period, frames_per_buffer); 15862306a36Sopenharmony_ci if (err < 0) { 15962306a36Sopenharmony_ci fw_iso_resources_free(&ff->tx_resources); 16062306a36Sopenharmony_ci fw_iso_resources_free(&ff->rx_resources); 16162306a36Sopenharmony_ci return err; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciint snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci int err; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (ff->substreams_counter == 0) 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (amdtp_streaming_error(&ff->tx_stream) || 17662306a36Sopenharmony_ci amdtp_streaming_error(&ff->rx_stream)) { 17762306a36Sopenharmony_ci amdtp_domain_stop(&ff->domain); 17862306a36Sopenharmony_ci finish_session(ff); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * Regardless of current source of clock signal, drivers transfer some 18362306a36Sopenharmony_ci * packets. Then, the device transfers packets. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci if (!amdtp_stream_running(&ff->rx_stream)) { 18662306a36Sopenharmony_ci int spd = fw_parent_device(ff->unit)->max_speed; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci err = ff->spec->protocol->begin_session(ff, rate); 18962306a36Sopenharmony_ci if (err < 0) 19062306a36Sopenharmony_ci goto error; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream, 19362306a36Sopenharmony_ci ff->rx_resources.channel, spd); 19462306a36Sopenharmony_ci if (err < 0) 19562306a36Sopenharmony_ci goto error; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream, 19862306a36Sopenharmony_ci ff->tx_resources.channel, spd); 19962306a36Sopenharmony_ci if (err < 0) 20062306a36Sopenharmony_ci goto error; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci // NOTE: The device doesn't transfer packets unless receiving any packet. The 20362306a36Sopenharmony_ci // sequence of tx packets includes cycle skip corresponding to empty packet or 20462306a36Sopenharmony_ci // NODATA packet in IEC 61883-1/6. The sequence of the number of data blocks per 20562306a36Sopenharmony_ci // packet is important for media clock recovery. 20662306a36Sopenharmony_ci err = amdtp_domain_start(&ff->domain, 0, true, true); 20762306a36Sopenharmony_ci if (err < 0) 20862306a36Sopenharmony_ci goto error; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (!amdtp_domain_wait_ready(&ff->domain, READY_TIMEOUT_MS)) { 21162306a36Sopenharmony_ci err = -ETIMEDOUT; 21262306a36Sopenharmony_ci goto error; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci err = ff->spec->protocol->switch_fetching_mode(ff, true); 21662306a36Sopenharmony_ci if (err < 0) 21762306a36Sopenharmony_ci goto error; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_cierror: 22262306a36Sopenharmony_ci amdtp_domain_stop(&ff->domain); 22362306a36Sopenharmony_ci finish_session(ff); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return err; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_civoid snd_ff_stream_stop_duplex(struct snd_ff *ff) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci if (ff->substreams_counter == 0) { 23162306a36Sopenharmony_ci amdtp_domain_stop(&ff->domain); 23262306a36Sopenharmony_ci finish_session(ff); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci fw_iso_resources_free(&ff->tx_resources); 23562306a36Sopenharmony_ci fw_iso_resources_free(&ff->rx_resources); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_civoid snd_ff_stream_update_duplex(struct snd_ff *ff) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci amdtp_domain_stop(&ff->domain); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci // The device discontinue to transfer packets. 24462306a36Sopenharmony_ci amdtp_stream_pcm_abort(&ff->tx_stream); 24562306a36Sopenharmony_ci amdtp_stream_pcm_abort(&ff->rx_stream); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_civoid snd_ff_stream_lock_changed(struct snd_ff *ff) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci ff->dev_lock_changed = true; 25162306a36Sopenharmony_ci wake_up(&ff->hwdep_wait); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ciint snd_ff_stream_lock_try(struct snd_ff *ff) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int err; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci spin_lock_irq(&ff->lock); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* user land lock this */ 26162306a36Sopenharmony_ci if (ff->dev_lock_count < 0) { 26262306a36Sopenharmony_ci err = -EBUSY; 26362306a36Sopenharmony_ci goto end; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* this is the first time */ 26762306a36Sopenharmony_ci if (ff->dev_lock_count++ == 0) 26862306a36Sopenharmony_ci snd_ff_stream_lock_changed(ff); 26962306a36Sopenharmony_ci err = 0; 27062306a36Sopenharmony_ciend: 27162306a36Sopenharmony_ci spin_unlock_irq(&ff->lock); 27262306a36Sopenharmony_ci return err; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_civoid snd_ff_stream_lock_release(struct snd_ff *ff) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci spin_lock_irq(&ff->lock); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (WARN_ON(ff->dev_lock_count <= 0)) 28062306a36Sopenharmony_ci goto end; 28162306a36Sopenharmony_ci if (--ff->dev_lock_count == 0) 28262306a36Sopenharmony_ci snd_ff_stream_lock_changed(ff); 28362306a36Sopenharmony_ciend: 28462306a36Sopenharmony_ci spin_unlock_irq(&ff->lock); 28562306a36Sopenharmony_ci} 286