162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014-2015 Takashi Sakamoto 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "digi00x.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define READY_TIMEOUT_MS 200 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ciconst unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = { 1362306a36Sopenharmony_ci [SND_DG00X_RATE_44100] = 44100, 1462306a36Sopenharmony_ci [SND_DG00X_RATE_48000] = 48000, 1562306a36Sopenharmony_ci [SND_DG00X_RATE_88200] = 88200, 1662306a36Sopenharmony_ci [SND_DG00X_RATE_96000] = 96000, 1762306a36Sopenharmony_ci}; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */ 2062306a36Sopenharmony_ciconst unsigned int 2162306a36Sopenharmony_cisnd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = { 2262306a36Sopenharmony_ci /* Analog/ADAT/SPDIF */ 2362306a36Sopenharmony_ci [SND_DG00X_RATE_44100] = (8 + 8 + 2), 2462306a36Sopenharmony_ci [SND_DG00X_RATE_48000] = (8 + 8 + 2), 2562306a36Sopenharmony_ci /* Analog/SPDIF */ 2662306a36Sopenharmony_ci [SND_DG00X_RATE_88200] = (8 + 2), 2762306a36Sopenharmony_ci [SND_DG00X_RATE_96000] = (8 + 2), 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciint snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci u32 data; 3362306a36Sopenharmony_ci __be32 reg; 3462306a36Sopenharmony_ci int err; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, 3762306a36Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE, 3862306a36Sopenharmony_ci ®, sizeof(reg), 0); 3962306a36Sopenharmony_ci if (err < 0) 4062306a36Sopenharmony_ci return err; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci data = be32_to_cpu(reg) & 0x0f; 4362306a36Sopenharmony_ci if (data < ARRAY_SIZE(snd_dg00x_stream_rates)) 4462306a36Sopenharmony_ci *rate = snd_dg00x_stream_rates[data]; 4562306a36Sopenharmony_ci else 4662306a36Sopenharmony_ci err = -EIO; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return err; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciint snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci __be32 reg; 5462306a36Sopenharmony_ci unsigned int i; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) { 5762306a36Sopenharmony_ci if (rate == snd_dg00x_stream_rates[i]) 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci if (i == ARRAY_SIZE(snd_dg00x_stream_rates)) 6162306a36Sopenharmony_ci return -EINVAL; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci reg = cpu_to_be32(i); 6462306a36Sopenharmony_ci return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, 6562306a36Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE, 6662306a36Sopenharmony_ci ®, sizeof(reg), 0); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ciint snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x, 7062306a36Sopenharmony_ci enum snd_dg00x_clock *clock) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci __be32 reg; 7362306a36Sopenharmony_ci int err; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, 7662306a36Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE, 7762306a36Sopenharmony_ci ®, sizeof(reg), 0); 7862306a36Sopenharmony_ci if (err < 0) 7962306a36Sopenharmony_ci return err; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci *clock = be32_to_cpu(reg) & 0x0f; 8262306a36Sopenharmony_ci if (*clock >= SND_DG00X_CLOCK_COUNT) 8362306a36Sopenharmony_ci err = -EIO; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return err; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciint snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci __be32 reg; 9162306a36Sopenharmony_ci int err; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, 9462306a36Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL, 9562306a36Sopenharmony_ci ®, sizeof(reg), 0); 9662306a36Sopenharmony_ci if (err >= 0) 9762306a36Sopenharmony_ci *detect = be32_to_cpu(reg) > 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return err; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ciint snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x, 10362306a36Sopenharmony_ci unsigned int *rate) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci u32 data; 10662306a36Sopenharmony_ci __be32 reg; 10762306a36Sopenharmony_ci int err; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, 11062306a36Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE, 11162306a36Sopenharmony_ci ®, sizeof(reg), 0); 11262306a36Sopenharmony_ci if (err < 0) 11362306a36Sopenharmony_ci return err; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci data = be32_to_cpu(reg) & 0x0f; 11662306a36Sopenharmony_ci if (data < ARRAY_SIZE(snd_dg00x_stream_rates)) 11762306a36Sopenharmony_ci *rate = snd_dg00x_stream_rates[data]; 11862306a36Sopenharmony_ci /* This means desync. */ 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci err = -EBUSY; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return err; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void finish_session(struct snd_dg00x *dg00x) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci __be32 data; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci data = cpu_to_be32(0x00000003); 13062306a36Sopenharmony_ci snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, 13162306a36Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET, 13262306a36Sopenharmony_ci &data, sizeof(data), 0); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci // Unregister isochronous channels for both direction. 13562306a36Sopenharmony_ci data = 0; 13662306a36Sopenharmony_ci snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, 13762306a36Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, 13862306a36Sopenharmony_ci &data, sizeof(data), 0); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci // Just after finishing the session, the device may lost transmitting 14162306a36Sopenharmony_ci // functionality for a short time. 14262306a36Sopenharmony_ci msleep(50); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int begin_session(struct snd_dg00x *dg00x) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci __be32 data; 14862306a36Sopenharmony_ci u32 curr; 14962306a36Sopenharmony_ci int err; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci // Register isochronous channels for both direction. 15262306a36Sopenharmony_ci data = cpu_to_be32((dg00x->tx_resources.channel << 16) | 15362306a36Sopenharmony_ci dg00x->rx_resources.channel); 15462306a36Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, 15562306a36Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, 15662306a36Sopenharmony_ci &data, sizeof(data), 0); 15762306a36Sopenharmony_ci if (err < 0) 15862306a36Sopenharmony_ci return err; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, 16162306a36Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE, 16262306a36Sopenharmony_ci &data, sizeof(data), 0); 16362306a36Sopenharmony_ci if (err < 0) 16462306a36Sopenharmony_ci return err; 16562306a36Sopenharmony_ci curr = be32_to_cpu(data); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (curr == 0) 16862306a36Sopenharmony_ci curr = 2; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci curr--; 17162306a36Sopenharmony_ci while (curr > 0) { 17262306a36Sopenharmony_ci data = cpu_to_be32(curr); 17362306a36Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, 17462306a36Sopenharmony_ci TCODE_WRITE_QUADLET_REQUEST, 17562306a36Sopenharmony_ci DG00X_ADDR_BASE + 17662306a36Sopenharmony_ci DG00X_OFFSET_STREAMING_SET, 17762306a36Sopenharmony_ci &data, sizeof(data), 0); 17862306a36Sopenharmony_ci if (err < 0) 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci msleep(20); 18262306a36Sopenharmony_ci curr--; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return err; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream, 18962306a36Sopenharmony_ci unsigned int rate) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct fw_iso_resources *resources; 19262306a36Sopenharmony_ci int i; 19362306a36Sopenharmony_ci int err; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci // Check sampling rate. 19662306a36Sopenharmony_ci for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { 19762306a36Sopenharmony_ci if (snd_dg00x_stream_rates[i] == rate) 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci if (i == SND_DG00X_RATE_COUNT) 20162306a36Sopenharmony_ci return -EINVAL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (stream == &dg00x->tx_stream) 20462306a36Sopenharmony_ci resources = &dg00x->tx_resources; 20562306a36Sopenharmony_ci else 20662306a36Sopenharmony_ci resources = &dg00x->rx_resources; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci err = amdtp_dot_set_parameters(stream, rate, 20962306a36Sopenharmony_ci snd_dg00x_stream_pcm_channels[i]); 21062306a36Sopenharmony_ci if (err < 0) 21162306a36Sopenharmony_ci return err; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return fw_iso_resources_allocate(resources, 21462306a36Sopenharmony_ci amdtp_stream_get_max_payload(stream), 21562306a36Sopenharmony_ci fw_parent_device(dg00x->unit)->max_speed); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct fw_iso_resources *resources; 22162306a36Sopenharmony_ci enum amdtp_stream_direction dir; 22262306a36Sopenharmony_ci int err; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (s == &dg00x->tx_stream) { 22562306a36Sopenharmony_ci resources = &dg00x->tx_resources; 22662306a36Sopenharmony_ci dir = AMDTP_IN_STREAM; 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci resources = &dg00x->rx_resources; 22962306a36Sopenharmony_ci dir = AMDTP_OUT_STREAM; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci err = fw_iso_resources_init(resources, dg00x->unit); 23362306a36Sopenharmony_ci if (err < 0) 23462306a36Sopenharmony_ci return err; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci err = amdtp_dot_init(s, dg00x->unit, dir); 23762306a36Sopenharmony_ci if (err < 0) 23862306a36Sopenharmony_ci fw_iso_resources_destroy(resources); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return err; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci amdtp_stream_destroy(s); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (s == &dg00x->tx_stream) 24862306a36Sopenharmony_ci fw_iso_resources_destroy(&dg00x->tx_resources); 24962306a36Sopenharmony_ci else 25062306a36Sopenharmony_ci fw_iso_resources_destroy(&dg00x->rx_resources); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ciint snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci int err; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci err = init_stream(dg00x, &dg00x->rx_stream); 25862306a36Sopenharmony_ci if (err < 0) 25962306a36Sopenharmony_ci return err; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci err = init_stream(dg00x, &dg00x->tx_stream); 26262306a36Sopenharmony_ci if (err < 0) { 26362306a36Sopenharmony_ci destroy_stream(dg00x, &dg00x->rx_stream); 26462306a36Sopenharmony_ci return err; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci err = amdtp_domain_init(&dg00x->domain); 26862306a36Sopenharmony_ci if (err < 0) { 26962306a36Sopenharmony_ci destroy_stream(dg00x, &dg00x->rx_stream); 27062306a36Sopenharmony_ci destroy_stream(dg00x, &dg00x->tx_stream); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return err; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/* 27762306a36Sopenharmony_ci * This function should be called before starting streams or after stopping 27862306a36Sopenharmony_ci * streams. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_civoid snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci amdtp_domain_destroy(&dg00x->domain); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci destroy_stream(dg00x, &dg00x->rx_stream); 28562306a36Sopenharmony_ci destroy_stream(dg00x, &dg00x->tx_stream); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciint snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate, 28962306a36Sopenharmony_ci unsigned int frames_per_period, 29062306a36Sopenharmony_ci unsigned int frames_per_buffer) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci unsigned int curr_rate; 29362306a36Sopenharmony_ci int err; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate); 29662306a36Sopenharmony_ci if (err < 0) 29762306a36Sopenharmony_ci return err; 29862306a36Sopenharmony_ci if (rate == 0) 29962306a36Sopenharmony_ci rate = curr_rate; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (dg00x->substreams_counter == 0 || curr_rate != rate) { 30262306a36Sopenharmony_ci amdtp_domain_stop(&dg00x->domain); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci finish_session(dg00x); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci fw_iso_resources_free(&dg00x->tx_resources); 30762306a36Sopenharmony_ci fw_iso_resources_free(&dg00x->rx_resources); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci err = snd_dg00x_stream_set_local_rate(dg00x, rate); 31062306a36Sopenharmony_ci if (err < 0) 31162306a36Sopenharmony_ci return err; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci err = keep_resources(dg00x, &dg00x->rx_stream, rate); 31462306a36Sopenharmony_ci if (err < 0) 31562306a36Sopenharmony_ci return err; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci err = keep_resources(dg00x, &dg00x->tx_stream, rate); 31862306a36Sopenharmony_ci if (err < 0) { 31962306a36Sopenharmony_ci fw_iso_resources_free(&dg00x->rx_resources); 32062306a36Sopenharmony_ci return err; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci err = amdtp_domain_set_events_per_period(&dg00x->domain, 32462306a36Sopenharmony_ci frames_per_period, frames_per_buffer); 32562306a36Sopenharmony_ci if (err < 0) { 32662306a36Sopenharmony_ci fw_iso_resources_free(&dg00x->rx_resources); 32762306a36Sopenharmony_ci fw_iso_resources_free(&dg00x->tx_resources); 32862306a36Sopenharmony_ci return err; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ciint snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci unsigned int generation = dg00x->rx_resources.generation; 33862306a36Sopenharmony_ci int err = 0; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (dg00x->substreams_counter == 0) 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (amdtp_streaming_error(&dg00x->tx_stream) || 34462306a36Sopenharmony_ci amdtp_streaming_error(&dg00x->rx_stream)) { 34562306a36Sopenharmony_ci amdtp_domain_stop(&dg00x->domain); 34662306a36Sopenharmony_ci finish_session(dg00x); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (generation != fw_parent_device(dg00x->unit)->card->generation) { 35062306a36Sopenharmony_ci err = fw_iso_resources_update(&dg00x->tx_resources); 35162306a36Sopenharmony_ci if (err < 0) 35262306a36Sopenharmony_ci goto error; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci err = fw_iso_resources_update(&dg00x->rx_resources); 35562306a36Sopenharmony_ci if (err < 0) 35662306a36Sopenharmony_ci goto error; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * No packets are transmitted without receiving packets, reagardless of 36162306a36Sopenharmony_ci * which source of clock is used. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ci if (!amdtp_stream_running(&dg00x->rx_stream)) { 36462306a36Sopenharmony_ci int spd = fw_parent_device(dg00x->unit)->max_speed; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci err = begin_session(dg00x); 36762306a36Sopenharmony_ci if (err < 0) 36862306a36Sopenharmony_ci goto error; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream, 37162306a36Sopenharmony_ci dg00x->rx_resources.channel, spd); 37262306a36Sopenharmony_ci if (err < 0) 37362306a36Sopenharmony_ci goto error; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream, 37662306a36Sopenharmony_ci dg00x->tx_resources.channel, spd); 37762306a36Sopenharmony_ci if (err < 0) 37862306a36Sopenharmony_ci goto error; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci // NOTE: The device doesn't start packet transmission till receiving any packet. 38162306a36Sopenharmony_ci // It ignores presentation time expressed by the value of syt field of CIP header 38262306a36Sopenharmony_ci // in received packets. The sequence of the number of data blocks per packet is 38362306a36Sopenharmony_ci // important for media clock recovery. 38462306a36Sopenharmony_ci err = amdtp_domain_start(&dg00x->domain, 0, true, true); 38562306a36Sopenharmony_ci if (err < 0) 38662306a36Sopenharmony_ci goto error; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) { 38962306a36Sopenharmony_ci err = -ETIMEDOUT; 39062306a36Sopenharmony_ci goto error; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_cierror: 39662306a36Sopenharmony_ci amdtp_domain_stop(&dg00x->domain); 39762306a36Sopenharmony_ci finish_session(dg00x); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return err; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_civoid snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci if (dg00x->substreams_counter == 0) { 40562306a36Sopenharmony_ci amdtp_domain_stop(&dg00x->domain); 40662306a36Sopenharmony_ci finish_session(dg00x); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci fw_iso_resources_free(&dg00x->tx_resources); 40962306a36Sopenharmony_ci fw_iso_resources_free(&dg00x->rx_resources); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_civoid snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci fw_iso_resources_update(&dg00x->tx_resources); 41662306a36Sopenharmony_ci fw_iso_resources_update(&dg00x->rx_resources); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci amdtp_stream_update(&dg00x->tx_stream); 41962306a36Sopenharmony_ci amdtp_stream_update(&dg00x->rx_stream); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_civoid snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci dg00x->dev_lock_changed = true; 42562306a36Sopenharmony_ci wake_up(&dg00x->hwdep_wait); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ciint snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci int err; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci spin_lock_irq(&dg00x->lock); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* user land lock this */ 43562306a36Sopenharmony_ci if (dg00x->dev_lock_count < 0) { 43662306a36Sopenharmony_ci err = -EBUSY; 43762306a36Sopenharmony_ci goto end; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* this is the first time */ 44162306a36Sopenharmony_ci if (dg00x->dev_lock_count++ == 0) 44262306a36Sopenharmony_ci snd_dg00x_stream_lock_changed(dg00x); 44362306a36Sopenharmony_ci err = 0; 44462306a36Sopenharmony_ciend: 44562306a36Sopenharmony_ci spin_unlock_irq(&dg00x->lock); 44662306a36Sopenharmony_ci return err; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_civoid snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci spin_lock_irq(&dg00x->lock); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (WARN_ON(dg00x->dev_lock_count <= 0)) 45462306a36Sopenharmony_ci goto end; 45562306a36Sopenharmony_ci if (--dg00x->dev_lock_count == 0) 45662306a36Sopenharmony_ci snd_dg00x_stream_lock_changed(dg00x); 45762306a36Sopenharmony_ciend: 45862306a36Sopenharmony_ci spin_unlock_irq(&dg00x->lock); 45962306a36Sopenharmony_ci} 460