18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015 Takashi Sakamoto 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "digi00x.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define CALLBACK_TIMEOUT 500 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ciconst unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = { 138c2ecf20Sopenharmony_ci [SND_DG00X_RATE_44100] = 44100, 148c2ecf20Sopenharmony_ci [SND_DG00X_RATE_48000] = 48000, 158c2ecf20Sopenharmony_ci [SND_DG00X_RATE_88200] = 88200, 168c2ecf20Sopenharmony_ci [SND_DG00X_RATE_96000] = 96000, 178c2ecf20Sopenharmony_ci}; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */ 208c2ecf20Sopenharmony_ciconst unsigned int 218c2ecf20Sopenharmony_cisnd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = { 228c2ecf20Sopenharmony_ci /* Analog/ADAT/SPDIF */ 238c2ecf20Sopenharmony_ci [SND_DG00X_RATE_44100] = (8 + 8 + 2), 248c2ecf20Sopenharmony_ci [SND_DG00X_RATE_48000] = (8 + 8 + 2), 258c2ecf20Sopenharmony_ci /* Analog/SPDIF */ 268c2ecf20Sopenharmony_ci [SND_DG00X_RATE_88200] = (8 + 2), 278c2ecf20Sopenharmony_ci [SND_DG00X_RATE_96000] = (8 + 2), 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciint snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci u32 data; 338c2ecf20Sopenharmony_ci __be32 reg; 348c2ecf20Sopenharmony_ci int err; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, 378c2ecf20Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE, 388c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 398c2ecf20Sopenharmony_ci if (err < 0) 408c2ecf20Sopenharmony_ci return err; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci data = be32_to_cpu(reg) & 0x0f; 438c2ecf20Sopenharmony_ci if (data < ARRAY_SIZE(snd_dg00x_stream_rates)) 448c2ecf20Sopenharmony_ci *rate = snd_dg00x_stream_rates[data]; 458c2ecf20Sopenharmony_ci else 468c2ecf20Sopenharmony_ci err = -EIO; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return err; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciint snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci __be32 reg; 548c2ecf20Sopenharmony_ci unsigned int i; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) { 578c2ecf20Sopenharmony_ci if (rate == snd_dg00x_stream_rates[i]) 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(snd_dg00x_stream_rates)) 618c2ecf20Sopenharmony_ci return -EINVAL; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci reg = cpu_to_be32(i); 648c2ecf20Sopenharmony_ci return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, 658c2ecf20Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE, 668c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ciint snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x, 708c2ecf20Sopenharmony_ci enum snd_dg00x_clock *clock) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci __be32 reg; 738c2ecf20Sopenharmony_ci int err; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, 768c2ecf20Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE, 778c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 788c2ecf20Sopenharmony_ci if (err < 0) 798c2ecf20Sopenharmony_ci return err; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci *clock = be32_to_cpu(reg) & 0x0f; 828c2ecf20Sopenharmony_ci if (*clock >= SND_DG00X_CLOCK_COUNT) 838c2ecf20Sopenharmony_ci err = -EIO; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return err; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciint snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci __be32 reg; 918c2ecf20Sopenharmony_ci int err; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, 948c2ecf20Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL, 958c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 968c2ecf20Sopenharmony_ci if (err >= 0) 978c2ecf20Sopenharmony_ci *detect = be32_to_cpu(reg) > 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return err; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ciint snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x, 1038c2ecf20Sopenharmony_ci unsigned int *rate) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci u32 data; 1068c2ecf20Sopenharmony_ci __be32 reg; 1078c2ecf20Sopenharmony_ci int err; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, 1108c2ecf20Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE, 1118c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 1128c2ecf20Sopenharmony_ci if (err < 0) 1138c2ecf20Sopenharmony_ci return err; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci data = be32_to_cpu(reg) & 0x0f; 1168c2ecf20Sopenharmony_ci if (data < ARRAY_SIZE(snd_dg00x_stream_rates)) 1178c2ecf20Sopenharmony_ci *rate = snd_dg00x_stream_rates[data]; 1188c2ecf20Sopenharmony_ci /* This means desync. */ 1198c2ecf20Sopenharmony_ci else 1208c2ecf20Sopenharmony_ci err = -EBUSY; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return err; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void finish_session(struct snd_dg00x *dg00x) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci __be32 data; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci data = cpu_to_be32(0x00000003); 1308c2ecf20Sopenharmony_ci snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, 1318c2ecf20Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET, 1328c2ecf20Sopenharmony_ci &data, sizeof(data), 0); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci // Unregister isochronous channels for both direction. 1358c2ecf20Sopenharmony_ci data = 0; 1368c2ecf20Sopenharmony_ci snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, 1378c2ecf20Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, 1388c2ecf20Sopenharmony_ci &data, sizeof(data), 0); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci // Just after finishing the session, the device may lost transmitting 1418c2ecf20Sopenharmony_ci // functionality for a short time. 1428c2ecf20Sopenharmony_ci msleep(50); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int begin_session(struct snd_dg00x *dg00x) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci __be32 data; 1488c2ecf20Sopenharmony_ci u32 curr; 1498c2ecf20Sopenharmony_ci int err; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci // Register isochronous channels for both direction. 1528c2ecf20Sopenharmony_ci data = cpu_to_be32((dg00x->tx_resources.channel << 16) | 1538c2ecf20Sopenharmony_ci dg00x->rx_resources.channel); 1548c2ecf20Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, 1558c2ecf20Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, 1568c2ecf20Sopenharmony_ci &data, sizeof(data), 0); 1578c2ecf20Sopenharmony_ci if (err < 0) 1588c2ecf20Sopenharmony_ci return err; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, 1618c2ecf20Sopenharmony_ci DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE, 1628c2ecf20Sopenharmony_ci &data, sizeof(data), 0); 1638c2ecf20Sopenharmony_ci if (err < 0) 1648c2ecf20Sopenharmony_ci return err; 1658c2ecf20Sopenharmony_ci curr = be32_to_cpu(data); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (curr == 0) 1688c2ecf20Sopenharmony_ci curr = 2; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci curr--; 1718c2ecf20Sopenharmony_ci while (curr > 0) { 1728c2ecf20Sopenharmony_ci data = cpu_to_be32(curr); 1738c2ecf20Sopenharmony_ci err = snd_fw_transaction(dg00x->unit, 1748c2ecf20Sopenharmony_ci TCODE_WRITE_QUADLET_REQUEST, 1758c2ecf20Sopenharmony_ci DG00X_ADDR_BASE + 1768c2ecf20Sopenharmony_ci DG00X_OFFSET_STREAMING_SET, 1778c2ecf20Sopenharmony_ci &data, sizeof(data), 0); 1788c2ecf20Sopenharmony_ci if (err < 0) 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci msleep(20); 1828c2ecf20Sopenharmony_ci curr--; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return err; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream, 1898c2ecf20Sopenharmony_ci unsigned int rate) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct fw_iso_resources *resources; 1928c2ecf20Sopenharmony_ci int i; 1938c2ecf20Sopenharmony_ci int err; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci // Check sampling rate. 1968c2ecf20Sopenharmony_ci for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { 1978c2ecf20Sopenharmony_ci if (snd_dg00x_stream_rates[i] == rate) 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci if (i == SND_DG00X_RATE_COUNT) 2018c2ecf20Sopenharmony_ci return -EINVAL; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (stream == &dg00x->tx_stream) 2048c2ecf20Sopenharmony_ci resources = &dg00x->tx_resources; 2058c2ecf20Sopenharmony_ci else 2068c2ecf20Sopenharmony_ci resources = &dg00x->rx_resources; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci err = amdtp_dot_set_parameters(stream, rate, 2098c2ecf20Sopenharmony_ci snd_dg00x_stream_pcm_channels[i]); 2108c2ecf20Sopenharmony_ci if (err < 0) 2118c2ecf20Sopenharmony_ci return err; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return fw_iso_resources_allocate(resources, 2148c2ecf20Sopenharmony_ci amdtp_stream_get_max_payload(stream), 2158c2ecf20Sopenharmony_ci fw_parent_device(dg00x->unit)->max_speed); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct fw_iso_resources *resources; 2218c2ecf20Sopenharmony_ci enum amdtp_stream_direction dir; 2228c2ecf20Sopenharmony_ci int err; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (s == &dg00x->tx_stream) { 2258c2ecf20Sopenharmony_ci resources = &dg00x->tx_resources; 2268c2ecf20Sopenharmony_ci dir = AMDTP_IN_STREAM; 2278c2ecf20Sopenharmony_ci } else { 2288c2ecf20Sopenharmony_ci resources = &dg00x->rx_resources; 2298c2ecf20Sopenharmony_ci dir = AMDTP_OUT_STREAM; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci err = fw_iso_resources_init(resources, dg00x->unit); 2338c2ecf20Sopenharmony_ci if (err < 0) 2348c2ecf20Sopenharmony_ci return err; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci err = amdtp_dot_init(s, dg00x->unit, dir); 2378c2ecf20Sopenharmony_ci if (err < 0) 2388c2ecf20Sopenharmony_ci fw_iso_resources_destroy(resources); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return err; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci amdtp_stream_destroy(s); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (s == &dg00x->tx_stream) 2488c2ecf20Sopenharmony_ci fw_iso_resources_destroy(&dg00x->tx_resources); 2498c2ecf20Sopenharmony_ci else 2508c2ecf20Sopenharmony_ci fw_iso_resources_destroy(&dg00x->rx_resources); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ciint snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci int err; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci err = init_stream(dg00x, &dg00x->rx_stream); 2588c2ecf20Sopenharmony_ci if (err < 0) 2598c2ecf20Sopenharmony_ci return err; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci err = init_stream(dg00x, &dg00x->tx_stream); 2628c2ecf20Sopenharmony_ci if (err < 0) { 2638c2ecf20Sopenharmony_ci destroy_stream(dg00x, &dg00x->rx_stream); 2648c2ecf20Sopenharmony_ci return err; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci err = amdtp_domain_init(&dg00x->domain); 2688c2ecf20Sopenharmony_ci if (err < 0) { 2698c2ecf20Sopenharmony_ci destroy_stream(dg00x, &dg00x->rx_stream); 2708c2ecf20Sopenharmony_ci destroy_stream(dg00x, &dg00x->tx_stream); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return err; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/* 2778c2ecf20Sopenharmony_ci * This function should be called before starting streams or after stopping 2788c2ecf20Sopenharmony_ci * streams. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_civoid snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci amdtp_domain_destroy(&dg00x->domain); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci destroy_stream(dg00x, &dg00x->rx_stream); 2858c2ecf20Sopenharmony_ci destroy_stream(dg00x, &dg00x->tx_stream); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ciint snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate, 2898c2ecf20Sopenharmony_ci unsigned int frames_per_period, 2908c2ecf20Sopenharmony_ci unsigned int frames_per_buffer) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci unsigned int curr_rate; 2938c2ecf20Sopenharmony_ci int err; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate); 2968c2ecf20Sopenharmony_ci if (err < 0) 2978c2ecf20Sopenharmony_ci return err; 2988c2ecf20Sopenharmony_ci if (rate == 0) 2998c2ecf20Sopenharmony_ci rate = curr_rate; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (dg00x->substreams_counter == 0 || curr_rate != rate) { 3028c2ecf20Sopenharmony_ci amdtp_domain_stop(&dg00x->domain); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci finish_session(dg00x); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci fw_iso_resources_free(&dg00x->tx_resources); 3078c2ecf20Sopenharmony_ci fw_iso_resources_free(&dg00x->rx_resources); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci err = snd_dg00x_stream_set_local_rate(dg00x, rate); 3108c2ecf20Sopenharmony_ci if (err < 0) 3118c2ecf20Sopenharmony_ci return err; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci err = keep_resources(dg00x, &dg00x->rx_stream, rate); 3148c2ecf20Sopenharmony_ci if (err < 0) 3158c2ecf20Sopenharmony_ci return err; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci err = keep_resources(dg00x, &dg00x->tx_stream, rate); 3188c2ecf20Sopenharmony_ci if (err < 0) { 3198c2ecf20Sopenharmony_ci fw_iso_resources_free(&dg00x->rx_resources); 3208c2ecf20Sopenharmony_ci return err; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci err = amdtp_domain_set_events_per_period(&dg00x->domain, 3248c2ecf20Sopenharmony_ci frames_per_period, frames_per_buffer); 3258c2ecf20Sopenharmony_ci if (err < 0) { 3268c2ecf20Sopenharmony_ci fw_iso_resources_free(&dg00x->rx_resources); 3278c2ecf20Sopenharmony_ci fw_iso_resources_free(&dg00x->tx_resources); 3288c2ecf20Sopenharmony_ci return err; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ciint snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci unsigned int generation = dg00x->rx_resources.generation; 3388c2ecf20Sopenharmony_ci int err = 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (dg00x->substreams_counter == 0) 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (amdtp_streaming_error(&dg00x->tx_stream) || 3448c2ecf20Sopenharmony_ci amdtp_streaming_error(&dg00x->rx_stream)) { 3458c2ecf20Sopenharmony_ci amdtp_domain_stop(&dg00x->domain); 3468c2ecf20Sopenharmony_ci finish_session(dg00x); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (generation != fw_parent_device(dg00x->unit)->card->generation) { 3508c2ecf20Sopenharmony_ci err = fw_iso_resources_update(&dg00x->tx_resources); 3518c2ecf20Sopenharmony_ci if (err < 0) 3528c2ecf20Sopenharmony_ci goto error; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci err = fw_iso_resources_update(&dg00x->rx_resources); 3558c2ecf20Sopenharmony_ci if (err < 0) 3568c2ecf20Sopenharmony_ci goto error; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * No packets are transmitted without receiving packets, reagardless of 3618c2ecf20Sopenharmony_ci * which source of clock is used. 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ci if (!amdtp_stream_running(&dg00x->rx_stream)) { 3648c2ecf20Sopenharmony_ci int spd = fw_parent_device(dg00x->unit)->max_speed; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci err = begin_session(dg00x); 3678c2ecf20Sopenharmony_ci if (err < 0) 3688c2ecf20Sopenharmony_ci goto error; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream, 3718c2ecf20Sopenharmony_ci dg00x->rx_resources.channel, spd); 3728c2ecf20Sopenharmony_ci if (err < 0) 3738c2ecf20Sopenharmony_ci goto error; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream, 3768c2ecf20Sopenharmony_ci dg00x->tx_resources.channel, spd); 3778c2ecf20Sopenharmony_ci if (err < 0) 3788c2ecf20Sopenharmony_ci goto error; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci err = amdtp_domain_start(&dg00x->domain, 0); 3818c2ecf20Sopenharmony_ci if (err < 0) 3828c2ecf20Sopenharmony_ci goto error; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (!amdtp_stream_wait_callback(&dg00x->rx_stream, 3858c2ecf20Sopenharmony_ci CALLBACK_TIMEOUT) || 3868c2ecf20Sopenharmony_ci !amdtp_stream_wait_callback(&dg00x->tx_stream, 3878c2ecf20Sopenharmony_ci CALLBACK_TIMEOUT)) { 3888c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 3898c2ecf20Sopenharmony_ci goto error; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_cierror: 3958c2ecf20Sopenharmony_ci amdtp_domain_stop(&dg00x->domain); 3968c2ecf20Sopenharmony_ci finish_session(dg00x); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return err; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_civoid snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci if (dg00x->substreams_counter == 0) { 4048c2ecf20Sopenharmony_ci amdtp_domain_stop(&dg00x->domain); 4058c2ecf20Sopenharmony_ci finish_session(dg00x); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci fw_iso_resources_free(&dg00x->tx_resources); 4088c2ecf20Sopenharmony_ci fw_iso_resources_free(&dg00x->rx_resources); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_civoid snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci fw_iso_resources_update(&dg00x->tx_resources); 4158c2ecf20Sopenharmony_ci fw_iso_resources_update(&dg00x->rx_resources); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci amdtp_stream_update(&dg00x->tx_stream); 4188c2ecf20Sopenharmony_ci amdtp_stream_update(&dg00x->rx_stream); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_civoid snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci dg00x->dev_lock_changed = true; 4248c2ecf20Sopenharmony_ci wake_up(&dg00x->hwdep_wait); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ciint snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci int err; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci spin_lock_irq(&dg00x->lock); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* user land lock this */ 4348c2ecf20Sopenharmony_ci if (dg00x->dev_lock_count < 0) { 4358c2ecf20Sopenharmony_ci err = -EBUSY; 4368c2ecf20Sopenharmony_ci goto end; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* this is the first time */ 4408c2ecf20Sopenharmony_ci if (dg00x->dev_lock_count++ == 0) 4418c2ecf20Sopenharmony_ci snd_dg00x_stream_lock_changed(dg00x); 4428c2ecf20Sopenharmony_ci err = 0; 4438c2ecf20Sopenharmony_ciend: 4448c2ecf20Sopenharmony_ci spin_unlock_irq(&dg00x->lock); 4458c2ecf20Sopenharmony_ci return err; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_civoid snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci spin_lock_irq(&dg00x->lock); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (WARN_ON(dg00x->dev_lock_count <= 0)) 4538c2ecf20Sopenharmony_ci goto end; 4548c2ecf20Sopenharmony_ci if (--dg00x->dev_lock_count == 0) 4558c2ecf20Sopenharmony_ci snd_dg00x_stream_lock_changed(dg00x); 4568c2ecf20Sopenharmony_ciend: 4578c2ecf20Sopenharmony_ci spin_unlock_irq(&dg00x->lock); 4588c2ecf20Sopenharmony_ci} 459