162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * dice_stream.c - a part of driver for DICE based devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 662306a36Sopenharmony_ci * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "dice.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define READY_TIMEOUT_MS 200 1262306a36Sopenharmony_ci#define NOTIFICATION_TIMEOUT_MS 100 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistruct reg_params { 1562306a36Sopenharmony_ci unsigned int count; 1662306a36Sopenharmony_ci unsigned int size; 1762306a36Sopenharmony_ci}; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciconst unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { 2062306a36Sopenharmony_ci /* mode 0 */ 2162306a36Sopenharmony_ci [0] = 32000, 2262306a36Sopenharmony_ci [1] = 44100, 2362306a36Sopenharmony_ci [2] = 48000, 2462306a36Sopenharmony_ci /* mode 1 */ 2562306a36Sopenharmony_ci [3] = 88200, 2662306a36Sopenharmony_ci [4] = 96000, 2762306a36Sopenharmony_ci /* mode 2 */ 2862306a36Sopenharmony_ci [5] = 176400, 2962306a36Sopenharmony_ci [6] = 192000, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciint snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, 3362306a36Sopenharmony_ci enum snd_dice_rate_mode *mode) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci /* Corresponding to each entry in snd_dice_rates. */ 3662306a36Sopenharmony_ci static const enum snd_dice_rate_mode modes[] = { 3762306a36Sopenharmony_ci [0] = SND_DICE_RATE_MODE_LOW, 3862306a36Sopenharmony_ci [1] = SND_DICE_RATE_MODE_LOW, 3962306a36Sopenharmony_ci [2] = SND_DICE_RATE_MODE_LOW, 4062306a36Sopenharmony_ci [3] = SND_DICE_RATE_MODE_MIDDLE, 4162306a36Sopenharmony_ci [4] = SND_DICE_RATE_MODE_MIDDLE, 4262306a36Sopenharmony_ci [5] = SND_DICE_RATE_MODE_HIGH, 4362306a36Sopenharmony_ci [6] = SND_DICE_RATE_MODE_HIGH, 4462306a36Sopenharmony_ci }; 4562306a36Sopenharmony_ci int i; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { 4862306a36Sopenharmony_ci if (!(dice->clock_caps & BIT(i))) 4962306a36Sopenharmony_ci continue; 5062306a36Sopenharmony_ci if (snd_dice_rates[i] != rate) 5162306a36Sopenharmony_ci continue; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci *mode = modes[i]; 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return -EINVAL; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int select_clock(struct snd_dice *dice, unsigned int rate) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci __be32 reg, new; 6362306a36Sopenharmony_ci u32 data; 6462306a36Sopenharmony_ci int i; 6562306a36Sopenharmony_ci int err; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, 6862306a36Sopenharmony_ci ®, sizeof(reg)); 6962306a36Sopenharmony_ci if (err < 0) 7062306a36Sopenharmony_ci return err; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci data = be32_to_cpu(reg); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci data &= ~CLOCK_RATE_MASK; 7562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { 7662306a36Sopenharmony_ci if (snd_dice_rates[i] == rate) 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci if (i == ARRAY_SIZE(snd_dice_rates)) 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci data |= i << CLOCK_RATE_SHIFT; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (completion_done(&dice->clock_accepted)) 8462306a36Sopenharmony_ci reinit_completion(&dice->clock_accepted); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci new = cpu_to_be32(data); 8762306a36Sopenharmony_ci err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, 8862306a36Sopenharmony_ci &new, sizeof(new)); 8962306a36Sopenharmony_ci if (err < 0) 9062306a36Sopenharmony_ci return err; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (wait_for_completion_timeout(&dice->clock_accepted, 9362306a36Sopenharmony_ci msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) { 9462306a36Sopenharmony_ci if (reg != new) 9562306a36Sopenharmony_ci return -ETIMEDOUT; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int get_register_params(struct snd_dice *dice, 10262306a36Sopenharmony_ci struct reg_params *tx_params, 10362306a36Sopenharmony_ci struct reg_params *rx_params) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci __be32 reg[2]; 10662306a36Sopenharmony_ci int err; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg)); 10962306a36Sopenharmony_ci if (err < 0) 11062306a36Sopenharmony_ci return err; 11162306a36Sopenharmony_ci tx_params->count = 11262306a36Sopenharmony_ci min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); 11362306a36Sopenharmony_ci tx_params->size = be32_to_cpu(reg[1]) * 4; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg)); 11662306a36Sopenharmony_ci if (err < 0) 11762306a36Sopenharmony_ci return err; 11862306a36Sopenharmony_ci rx_params->count = 11962306a36Sopenharmony_ci min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); 12062306a36Sopenharmony_ci rx_params->size = be32_to_cpu(reg[1]) * 4; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void release_resources(struct snd_dice *dice) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci int i; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci for (i = 0; i < MAX_STREAMS; ++i) { 13062306a36Sopenharmony_ci fw_iso_resources_free(&dice->tx_resources[i]); 13162306a36Sopenharmony_ci fw_iso_resources_free(&dice->rx_resources[i]); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, 13662306a36Sopenharmony_ci struct reg_params *params) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci __be32 reg; 13962306a36Sopenharmony_ci unsigned int i; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci for (i = 0; i < params->count; i++) { 14262306a36Sopenharmony_ci reg = cpu_to_be32((u32)-1); 14362306a36Sopenharmony_ci if (dir == AMDTP_IN_STREAM) { 14462306a36Sopenharmony_ci snd_dice_transaction_write_tx(dice, 14562306a36Sopenharmony_ci params->size * i + TX_ISOCHRONOUS, 14662306a36Sopenharmony_ci ®, sizeof(reg)); 14762306a36Sopenharmony_ci } else { 14862306a36Sopenharmony_ci snd_dice_transaction_write_rx(dice, 14962306a36Sopenharmony_ci params->size * i + RX_ISOCHRONOUS, 15062306a36Sopenharmony_ci ®, sizeof(reg)); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream, 15662306a36Sopenharmony_ci struct fw_iso_resources *resources, unsigned int rate, 15762306a36Sopenharmony_ci unsigned int pcm_chs, unsigned int midi_ports) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci bool double_pcm_frames; 16062306a36Sopenharmony_ci unsigned int i; 16162306a36Sopenharmony_ci int err; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci // At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in 16462306a36Sopenharmony_ci // one data block of AMDTP packet. Thus sampling transfer frequency is 16562306a36Sopenharmony_ci // a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are 16662306a36Sopenharmony_ci // transferred on AMDTP packets at 96 kHz. Two successive samples of a 16762306a36Sopenharmony_ci // channel are stored consecutively in the packet. This quirk is called 16862306a36Sopenharmony_ci // as 'Dual Wire'. 16962306a36Sopenharmony_ci // For this quirk, blocking mode is required and PCM buffer size should 17062306a36Sopenharmony_ci // be aligned to SYT_INTERVAL. 17162306a36Sopenharmony_ci double_pcm_frames = (rate > 96000 && !dice->disable_double_pcm_frames); 17262306a36Sopenharmony_ci if (double_pcm_frames) { 17362306a36Sopenharmony_ci rate /= 2; 17462306a36Sopenharmony_ci pcm_chs *= 2; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports, 17862306a36Sopenharmony_ci double_pcm_frames); 17962306a36Sopenharmony_ci if (err < 0) 18062306a36Sopenharmony_ci return err; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (double_pcm_frames) { 18362306a36Sopenharmony_ci pcm_chs /= 2; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci for (i = 0; i < pcm_chs; i++) { 18662306a36Sopenharmony_ci amdtp_am824_set_pcm_position(stream, i, i * 2); 18762306a36Sopenharmony_ci amdtp_am824_set_pcm_position(stream, i + pcm_chs, 18862306a36Sopenharmony_ci i * 2 + 1); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return fw_iso_resources_allocate(resources, 19362306a36Sopenharmony_ci amdtp_stream_get_max_payload(stream), 19462306a36Sopenharmony_ci fw_parent_device(dice->unit)->max_speed); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int keep_dual_resources(struct snd_dice *dice, unsigned int rate, 19862306a36Sopenharmony_ci enum amdtp_stream_direction dir, 19962306a36Sopenharmony_ci struct reg_params *params) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci enum snd_dice_rate_mode mode; 20262306a36Sopenharmony_ci int i; 20362306a36Sopenharmony_ci int err; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci err = snd_dice_stream_get_rate_mode(dice, rate, &mode); 20662306a36Sopenharmony_ci if (err < 0) 20762306a36Sopenharmony_ci return err; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for (i = 0; i < params->count; ++i) { 21062306a36Sopenharmony_ci __be32 reg[2]; 21162306a36Sopenharmony_ci struct amdtp_stream *stream; 21262306a36Sopenharmony_ci struct fw_iso_resources *resources; 21362306a36Sopenharmony_ci unsigned int pcm_cache; 21462306a36Sopenharmony_ci unsigned int pcm_chs; 21562306a36Sopenharmony_ci unsigned int midi_ports; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (dir == AMDTP_IN_STREAM) { 21862306a36Sopenharmony_ci stream = &dice->tx_stream[i]; 21962306a36Sopenharmony_ci resources = &dice->tx_resources[i]; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci pcm_cache = dice->tx_pcm_chs[i][mode]; 22262306a36Sopenharmony_ci err = snd_dice_transaction_read_tx(dice, 22362306a36Sopenharmony_ci params->size * i + TX_NUMBER_AUDIO, 22462306a36Sopenharmony_ci reg, sizeof(reg)); 22562306a36Sopenharmony_ci } else { 22662306a36Sopenharmony_ci stream = &dice->rx_stream[i]; 22762306a36Sopenharmony_ci resources = &dice->rx_resources[i]; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci pcm_cache = dice->rx_pcm_chs[i][mode]; 23062306a36Sopenharmony_ci err = snd_dice_transaction_read_rx(dice, 23162306a36Sopenharmony_ci params->size * i + RX_NUMBER_AUDIO, 23262306a36Sopenharmony_ci reg, sizeof(reg)); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci if (err < 0) 23562306a36Sopenharmony_ci return err; 23662306a36Sopenharmony_ci pcm_chs = be32_to_cpu(reg[0]); 23762306a36Sopenharmony_ci midi_ports = be32_to_cpu(reg[1]); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci // These are important for developer of this driver. 24062306a36Sopenharmony_ci if (pcm_chs != pcm_cache) { 24162306a36Sopenharmony_ci dev_info(&dice->unit->device, 24262306a36Sopenharmony_ci "cache mismatch: pcm: %u:%u, midi: %u\n", 24362306a36Sopenharmony_ci pcm_chs, pcm_cache, midi_ports); 24462306a36Sopenharmony_ci return -EPROTO; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci err = keep_resources(dice, stream, resources, rate, pcm_chs, 24862306a36Sopenharmony_ci midi_ports); 24962306a36Sopenharmony_ci if (err < 0) 25062306a36Sopenharmony_ci return err; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void finish_session(struct snd_dice *dice, struct reg_params *tx_params, 25762306a36Sopenharmony_ci struct reg_params *rx_params) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci stop_streams(dice, AMDTP_IN_STREAM, tx_params); 26062306a36Sopenharmony_ci stop_streams(dice, AMDTP_OUT_STREAM, rx_params); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci snd_dice_transaction_clear_enable(dice); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciint snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate, 26662306a36Sopenharmony_ci unsigned int events_per_period, 26762306a36Sopenharmony_ci unsigned int events_per_buffer) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci unsigned int curr_rate; 27062306a36Sopenharmony_ci int err; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci // Check sampling transmission frequency. 27362306a36Sopenharmony_ci err = snd_dice_transaction_get_rate(dice, &curr_rate); 27462306a36Sopenharmony_ci if (err < 0) 27562306a36Sopenharmony_ci return err; 27662306a36Sopenharmony_ci if (rate == 0) 27762306a36Sopenharmony_ci rate = curr_rate; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (dice->substreams_counter == 0 || curr_rate != rate) { 28062306a36Sopenharmony_ci struct reg_params tx_params, rx_params; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci amdtp_domain_stop(&dice->domain); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci err = get_register_params(dice, &tx_params, &rx_params); 28562306a36Sopenharmony_ci if (err < 0) 28662306a36Sopenharmony_ci return err; 28762306a36Sopenharmony_ci finish_session(dice, &tx_params, &rx_params); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci release_resources(dice); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci // Just after owning the unit (GLOBAL_OWNER), the unit can 29262306a36Sopenharmony_ci // return invalid stream formats. Selecting clock parameters 29362306a36Sopenharmony_ci // have an effect for the unit to refine it. 29462306a36Sopenharmony_ci err = select_clock(dice, rate); 29562306a36Sopenharmony_ci if (err < 0) 29662306a36Sopenharmony_ci return err; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci // After changing sampling transfer frequency, the value of 29962306a36Sopenharmony_ci // register can be changed. 30062306a36Sopenharmony_ci err = get_register_params(dice, &tx_params, &rx_params); 30162306a36Sopenharmony_ci if (err < 0) 30262306a36Sopenharmony_ci return err; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM, 30562306a36Sopenharmony_ci &tx_params); 30662306a36Sopenharmony_ci if (err < 0) 30762306a36Sopenharmony_ci goto error; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM, 31062306a36Sopenharmony_ci &rx_params); 31162306a36Sopenharmony_ci if (err < 0) 31262306a36Sopenharmony_ci goto error; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci err = amdtp_domain_set_events_per_period(&dice->domain, 31562306a36Sopenharmony_ci events_per_period, events_per_buffer); 31662306a36Sopenharmony_ci if (err < 0) 31762306a36Sopenharmony_ci goto error; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_cierror: 32262306a36Sopenharmony_ci release_resources(dice); 32362306a36Sopenharmony_ci return err; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, 32762306a36Sopenharmony_ci unsigned int rate, struct reg_params *params) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci unsigned int max_speed = fw_parent_device(dice->unit)->max_speed; 33062306a36Sopenharmony_ci int i; 33162306a36Sopenharmony_ci int err; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci for (i = 0; i < params->count; i++) { 33462306a36Sopenharmony_ci struct amdtp_stream *stream; 33562306a36Sopenharmony_ci struct fw_iso_resources *resources; 33662306a36Sopenharmony_ci __be32 reg; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (dir == AMDTP_IN_STREAM) { 33962306a36Sopenharmony_ci stream = dice->tx_stream + i; 34062306a36Sopenharmony_ci resources = dice->tx_resources + i; 34162306a36Sopenharmony_ci } else { 34262306a36Sopenharmony_ci stream = dice->rx_stream + i; 34362306a36Sopenharmony_ci resources = dice->rx_resources + i; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci reg = cpu_to_be32(resources->channel); 34762306a36Sopenharmony_ci if (dir == AMDTP_IN_STREAM) { 34862306a36Sopenharmony_ci err = snd_dice_transaction_write_tx(dice, 34962306a36Sopenharmony_ci params->size * i + TX_ISOCHRONOUS, 35062306a36Sopenharmony_ci ®, sizeof(reg)); 35162306a36Sopenharmony_ci } else { 35262306a36Sopenharmony_ci err = snd_dice_transaction_write_rx(dice, 35362306a36Sopenharmony_ci params->size * i + RX_ISOCHRONOUS, 35462306a36Sopenharmony_ci ®, sizeof(reg)); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci if (err < 0) 35762306a36Sopenharmony_ci return err; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (dir == AMDTP_IN_STREAM) { 36062306a36Sopenharmony_ci reg = cpu_to_be32(max_speed); 36162306a36Sopenharmony_ci err = snd_dice_transaction_write_tx(dice, 36262306a36Sopenharmony_ci params->size * i + TX_SPEED, 36362306a36Sopenharmony_ci ®, sizeof(reg)); 36462306a36Sopenharmony_ci if (err < 0) 36562306a36Sopenharmony_ci return err; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci err = amdtp_domain_add_stream(&dice->domain, stream, 36962306a36Sopenharmony_ci resources->channel, max_speed); 37062306a36Sopenharmony_ci if (err < 0) 37162306a36Sopenharmony_ci return err; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* 37862306a36Sopenharmony_ci * MEMO: After this function, there're two states of streams: 37962306a36Sopenharmony_ci * - None streams are running. 38062306a36Sopenharmony_ci * - All streams are running. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ciint snd_dice_stream_start_duplex(struct snd_dice *dice) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci unsigned int generation = dice->rx_resources[0].generation; 38562306a36Sopenharmony_ci struct reg_params tx_params, rx_params; 38662306a36Sopenharmony_ci unsigned int i; 38762306a36Sopenharmony_ci unsigned int rate; 38862306a36Sopenharmony_ci enum snd_dice_rate_mode mode; 38962306a36Sopenharmony_ci int err; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (dice->substreams_counter == 0) 39262306a36Sopenharmony_ci return -EIO; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci err = get_register_params(dice, &tx_params, &rx_params); 39562306a36Sopenharmony_ci if (err < 0) 39662306a36Sopenharmony_ci return err; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci // Check error of packet streaming. 39962306a36Sopenharmony_ci for (i = 0; i < MAX_STREAMS; ++i) { 40062306a36Sopenharmony_ci if (amdtp_streaming_error(&dice->tx_stream[i]) || 40162306a36Sopenharmony_ci amdtp_streaming_error(&dice->rx_stream[i])) { 40262306a36Sopenharmony_ci amdtp_domain_stop(&dice->domain); 40362306a36Sopenharmony_ci finish_session(dice, &tx_params, &rx_params); 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (generation != fw_parent_device(dice->unit)->card->generation) { 40962306a36Sopenharmony_ci for (i = 0; i < MAX_STREAMS; ++i) { 41062306a36Sopenharmony_ci if (i < tx_params.count) 41162306a36Sopenharmony_ci fw_iso_resources_update(dice->tx_resources + i); 41262306a36Sopenharmony_ci if (i < rx_params.count) 41362306a36Sopenharmony_ci fw_iso_resources_update(dice->rx_resources + i); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci // Check required streams are running or not. 41862306a36Sopenharmony_ci err = snd_dice_transaction_get_rate(dice, &rate); 41962306a36Sopenharmony_ci if (err < 0) 42062306a36Sopenharmony_ci return err; 42162306a36Sopenharmony_ci err = snd_dice_stream_get_rate_mode(dice, rate, &mode); 42262306a36Sopenharmony_ci if (err < 0) 42362306a36Sopenharmony_ci return err; 42462306a36Sopenharmony_ci for (i = 0; i < MAX_STREAMS; ++i) { 42562306a36Sopenharmony_ci if (dice->tx_pcm_chs[i][mode] > 0 && 42662306a36Sopenharmony_ci !amdtp_stream_running(&dice->tx_stream[i])) 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci if (dice->rx_pcm_chs[i][mode] > 0 && 42962306a36Sopenharmony_ci !amdtp_stream_running(&dice->rx_stream[i])) 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci if (i < MAX_STREAMS) { 43362306a36Sopenharmony_ci // Start both streams. 43462306a36Sopenharmony_ci err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); 43562306a36Sopenharmony_ci if (err < 0) 43662306a36Sopenharmony_ci goto error; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); 43962306a36Sopenharmony_ci if (err < 0) 44062306a36Sopenharmony_ci goto error; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci err = snd_dice_transaction_set_enable(dice); 44362306a36Sopenharmony_ci if (err < 0) { 44462306a36Sopenharmony_ci dev_err(&dice->unit->device, 44562306a36Sopenharmony_ci "fail to enable interface\n"); 44662306a36Sopenharmony_ci goto error; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci // MEMO: The device immediately starts packet transmission when enabled. Some 45062306a36Sopenharmony_ci // devices are strictly to generate any discontinuity in the sequence of tx packet 45162306a36Sopenharmony_ci // when they receives invalid sequence of presentation time in CIP header. The 45262306a36Sopenharmony_ci // sequence replay for media clock recovery can suppress the behaviour. 45362306a36Sopenharmony_ci err = amdtp_domain_start(&dice->domain, 0, true, false); 45462306a36Sopenharmony_ci if (err < 0) 45562306a36Sopenharmony_ci goto error; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (!amdtp_domain_wait_ready(&dice->domain, READY_TIMEOUT_MS)) { 45862306a36Sopenharmony_ci err = -ETIMEDOUT; 45962306a36Sopenharmony_ci goto error; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_cierror: 46562306a36Sopenharmony_ci amdtp_domain_stop(&dice->domain); 46662306a36Sopenharmony_ci finish_session(dice, &tx_params, &rx_params); 46762306a36Sopenharmony_ci return err; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/* 47162306a36Sopenharmony_ci * MEMO: After this function, there're two states of streams: 47262306a36Sopenharmony_ci * - None streams are running. 47362306a36Sopenharmony_ci * - All streams are running. 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_civoid snd_dice_stream_stop_duplex(struct snd_dice *dice) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct reg_params tx_params, rx_params; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (dice->substreams_counter == 0) { 48062306a36Sopenharmony_ci if (get_register_params(dice, &tx_params, &rx_params) >= 0) 48162306a36Sopenharmony_ci finish_session(dice, &tx_params, &rx_params); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci amdtp_domain_stop(&dice->domain); 48462306a36Sopenharmony_ci release_resources(dice); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir, 48962306a36Sopenharmony_ci unsigned int index) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct amdtp_stream *stream; 49262306a36Sopenharmony_ci struct fw_iso_resources *resources; 49362306a36Sopenharmony_ci int err; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (dir == AMDTP_IN_STREAM) { 49662306a36Sopenharmony_ci stream = &dice->tx_stream[index]; 49762306a36Sopenharmony_ci resources = &dice->tx_resources[index]; 49862306a36Sopenharmony_ci } else { 49962306a36Sopenharmony_ci stream = &dice->rx_stream[index]; 50062306a36Sopenharmony_ci resources = &dice->rx_resources[index]; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci err = fw_iso_resources_init(resources, dice->unit); 50462306a36Sopenharmony_ci if (err < 0) 50562306a36Sopenharmony_ci goto end; 50662306a36Sopenharmony_ci resources->channels_mask = 0x00000000ffffffffuLL; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING); 50962306a36Sopenharmony_ci if (err < 0) { 51062306a36Sopenharmony_ci amdtp_stream_destroy(stream); 51162306a36Sopenharmony_ci fw_iso_resources_destroy(resources); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ciend: 51462306a36Sopenharmony_ci return err; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/* 51862306a36Sopenharmony_ci * This function should be called before starting streams or after stopping 51962306a36Sopenharmony_ci * streams. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_cistatic void destroy_stream(struct snd_dice *dice, 52262306a36Sopenharmony_ci enum amdtp_stream_direction dir, 52362306a36Sopenharmony_ci unsigned int index) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct amdtp_stream *stream; 52662306a36Sopenharmony_ci struct fw_iso_resources *resources; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (dir == AMDTP_IN_STREAM) { 52962306a36Sopenharmony_ci stream = &dice->tx_stream[index]; 53062306a36Sopenharmony_ci resources = &dice->tx_resources[index]; 53162306a36Sopenharmony_ci } else { 53262306a36Sopenharmony_ci stream = &dice->rx_stream[index]; 53362306a36Sopenharmony_ci resources = &dice->rx_resources[index]; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci amdtp_stream_destroy(stream); 53762306a36Sopenharmony_ci fw_iso_resources_destroy(resources); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ciint snd_dice_stream_init_duplex(struct snd_dice *dice) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci int i, err; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci for (i = 0; i < MAX_STREAMS; i++) { 54562306a36Sopenharmony_ci err = init_stream(dice, AMDTP_IN_STREAM, i); 54662306a36Sopenharmony_ci if (err < 0) { 54762306a36Sopenharmony_ci for (; i >= 0; i--) 54862306a36Sopenharmony_ci destroy_stream(dice, AMDTP_IN_STREAM, i); 54962306a36Sopenharmony_ci goto end; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci for (i = 0; i < MAX_STREAMS; i++) { 55462306a36Sopenharmony_ci err = init_stream(dice, AMDTP_OUT_STREAM, i); 55562306a36Sopenharmony_ci if (err < 0) { 55662306a36Sopenharmony_ci for (; i >= 0; i--) 55762306a36Sopenharmony_ci destroy_stream(dice, AMDTP_OUT_STREAM, i); 55862306a36Sopenharmony_ci for (i = 0; i < MAX_STREAMS; i++) 55962306a36Sopenharmony_ci destroy_stream(dice, AMDTP_IN_STREAM, i); 56062306a36Sopenharmony_ci goto end; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci err = amdtp_domain_init(&dice->domain); 56562306a36Sopenharmony_ci if (err < 0) { 56662306a36Sopenharmony_ci for (i = 0; i < MAX_STREAMS; ++i) { 56762306a36Sopenharmony_ci destroy_stream(dice, AMDTP_OUT_STREAM, i); 56862306a36Sopenharmony_ci destroy_stream(dice, AMDTP_IN_STREAM, i); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ciend: 57262306a36Sopenharmony_ci return err; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_civoid snd_dice_stream_destroy_duplex(struct snd_dice *dice) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci unsigned int i; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci for (i = 0; i < MAX_STREAMS; i++) { 58062306a36Sopenharmony_ci destroy_stream(dice, AMDTP_IN_STREAM, i); 58162306a36Sopenharmony_ci destroy_stream(dice, AMDTP_OUT_STREAM, i); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci amdtp_domain_destroy(&dice->domain); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_civoid snd_dice_stream_update_duplex(struct snd_dice *dice) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct reg_params tx_params, rx_params; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* 59262306a36Sopenharmony_ci * On a bus reset, the DICE firmware disables streaming and then goes 59362306a36Sopenharmony_ci * off contemplating its own navel for hundreds of milliseconds before 59462306a36Sopenharmony_ci * it can react to any of our attempts to reenable streaming. This 59562306a36Sopenharmony_ci * means that we lose synchronization anyway, so we force our streams 59662306a36Sopenharmony_ci * to stop so that the application can restart them in an orderly 59762306a36Sopenharmony_ci * manner. 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_ci dice->global_enabled = false; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (get_register_params(dice, &tx_params, &rx_params) == 0) { 60262306a36Sopenharmony_ci amdtp_domain_stop(&dice->domain); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci stop_streams(dice, AMDTP_IN_STREAM, &tx_params); 60562306a36Sopenharmony_ci stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ciint snd_dice_stream_detect_current_formats(struct snd_dice *dice) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci unsigned int rate; 61262306a36Sopenharmony_ci enum snd_dice_rate_mode mode; 61362306a36Sopenharmony_ci __be32 reg[2]; 61462306a36Sopenharmony_ci struct reg_params tx_params, rx_params; 61562306a36Sopenharmony_ci int i; 61662306a36Sopenharmony_ci int err; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* If extended protocol is available, detect detail spec. */ 61962306a36Sopenharmony_ci err = snd_dice_detect_extension_formats(dice); 62062306a36Sopenharmony_ci if (err >= 0) 62162306a36Sopenharmony_ci return err; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* 62462306a36Sopenharmony_ci * Available stream format is restricted at current mode of sampling 62562306a36Sopenharmony_ci * clock. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci err = snd_dice_transaction_get_rate(dice, &rate); 62862306a36Sopenharmony_ci if (err < 0) 62962306a36Sopenharmony_ci return err; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci err = snd_dice_stream_get_rate_mode(dice, rate, &mode); 63262306a36Sopenharmony_ci if (err < 0) 63362306a36Sopenharmony_ci return err; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* 63662306a36Sopenharmony_ci * Just after owning the unit (GLOBAL_OWNER), the unit can return 63762306a36Sopenharmony_ci * invalid stream formats. Selecting clock parameters have an effect 63862306a36Sopenharmony_ci * for the unit to refine it. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci err = select_clock(dice, rate); 64162306a36Sopenharmony_ci if (err < 0) 64262306a36Sopenharmony_ci return err; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci err = get_register_params(dice, &tx_params, &rx_params); 64562306a36Sopenharmony_ci if (err < 0) 64662306a36Sopenharmony_ci return err; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci for (i = 0; i < tx_params.count; ++i) { 64962306a36Sopenharmony_ci err = snd_dice_transaction_read_tx(dice, 65062306a36Sopenharmony_ci tx_params.size * i + TX_NUMBER_AUDIO, 65162306a36Sopenharmony_ci reg, sizeof(reg)); 65262306a36Sopenharmony_ci if (err < 0) 65362306a36Sopenharmony_ci return err; 65462306a36Sopenharmony_ci dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]); 65562306a36Sopenharmony_ci dice->tx_midi_ports[i] = max_t(unsigned int, 65662306a36Sopenharmony_ci be32_to_cpu(reg[1]), dice->tx_midi_ports[i]); 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci for (i = 0; i < rx_params.count; ++i) { 65962306a36Sopenharmony_ci err = snd_dice_transaction_read_rx(dice, 66062306a36Sopenharmony_ci rx_params.size * i + RX_NUMBER_AUDIO, 66162306a36Sopenharmony_ci reg, sizeof(reg)); 66262306a36Sopenharmony_ci if (err < 0) 66362306a36Sopenharmony_ci return err; 66462306a36Sopenharmony_ci dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]); 66562306a36Sopenharmony_ci dice->rx_midi_ports[i] = max_t(unsigned int, 66662306a36Sopenharmony_ci be32_to_cpu(reg[1]), dice->rx_midi_ports[i]); 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic void dice_lock_changed(struct snd_dice *dice) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci dice->dev_lock_changed = true; 67562306a36Sopenharmony_ci wake_up(&dice->hwdep_wait); 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ciint snd_dice_stream_lock_try(struct snd_dice *dice) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci int err; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci spin_lock_irq(&dice->lock); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (dice->dev_lock_count < 0) { 68562306a36Sopenharmony_ci err = -EBUSY; 68662306a36Sopenharmony_ci goto out; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (dice->dev_lock_count++ == 0) 69062306a36Sopenharmony_ci dice_lock_changed(dice); 69162306a36Sopenharmony_ci err = 0; 69262306a36Sopenharmony_ciout: 69362306a36Sopenharmony_ci spin_unlock_irq(&dice->lock); 69462306a36Sopenharmony_ci return err; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_civoid snd_dice_stream_lock_release(struct snd_dice *dice) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci spin_lock_irq(&dice->lock); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (WARN_ON(dice->dev_lock_count <= 0)) 70262306a36Sopenharmony_ci goto out; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (--dice->dev_lock_count == 0) 70562306a36Sopenharmony_ci dice_lock_changed(dice); 70662306a36Sopenharmony_ciout: 70762306a36Sopenharmony_ci spin_unlock_irq(&dice->lock); 70862306a36Sopenharmony_ci} 709