18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tascam-stream.c - a part of driver for TASCAM FireWire series 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Takashi Sakamoto 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include "tascam.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define CLOCK_STATUS_MASK 0xffff0000 128c2ecf20Sopenharmony_ci#define CLOCK_CONFIG_MASK 0x0000ffff 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define CALLBACK_TIMEOUT 500 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int get_clock(struct snd_tscm *tscm, u32 *data) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci int trial = 0; 198c2ecf20Sopenharmony_ci __be32 reg; 208c2ecf20Sopenharmony_ci int err; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci while (trial++ < 5) { 238c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST, 248c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS, 258c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 268c2ecf20Sopenharmony_ci if (err < 0) 278c2ecf20Sopenharmony_ci return err; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci *data = be32_to_cpu(reg); 308c2ecf20Sopenharmony_ci if (*data & CLOCK_STATUS_MASK) 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci // In intermediate state after changing clock status. 348c2ecf20Sopenharmony_ci msleep(50); 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci // Still in the intermediate state. 388c2ecf20Sopenharmony_ci if (trial >= 5) 398c2ecf20Sopenharmony_ci return -EAGAIN; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int set_clock(struct snd_tscm *tscm, unsigned int rate, 458c2ecf20Sopenharmony_ci enum snd_tscm_clock clock) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci u32 data; 488c2ecf20Sopenharmony_ci __be32 reg; 498c2ecf20Sopenharmony_ci int err; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci err = get_clock(tscm, &data); 528c2ecf20Sopenharmony_ci if (err < 0) 538c2ecf20Sopenharmony_ci return err; 548c2ecf20Sopenharmony_ci data &= CLOCK_CONFIG_MASK; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (rate > 0) { 578c2ecf20Sopenharmony_ci data &= 0x000000ff; 588c2ecf20Sopenharmony_ci /* Base rate. */ 598c2ecf20Sopenharmony_ci if ((rate % 44100) == 0) { 608c2ecf20Sopenharmony_ci data |= 0x00000100; 618c2ecf20Sopenharmony_ci /* Multiplier. */ 628c2ecf20Sopenharmony_ci if (rate / 44100 == 2) 638c2ecf20Sopenharmony_ci data |= 0x00008000; 648c2ecf20Sopenharmony_ci } else if ((rate % 48000) == 0) { 658c2ecf20Sopenharmony_ci data |= 0x00000200; 668c2ecf20Sopenharmony_ci /* Multiplier. */ 678c2ecf20Sopenharmony_ci if (rate / 48000 == 2) 688c2ecf20Sopenharmony_ci data |= 0x00008000; 698c2ecf20Sopenharmony_ci } else { 708c2ecf20Sopenharmony_ci return -EAGAIN; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (clock != INT_MAX) { 758c2ecf20Sopenharmony_ci data &= 0x0000ff00; 768c2ecf20Sopenharmony_ci data |= clock + 1; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci reg = cpu_to_be32(data); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 828c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS, 838c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 848c2ecf20Sopenharmony_ci if (err < 0) 858c2ecf20Sopenharmony_ci return err; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (data & 0x00008000) 888c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x0000001a); 898c2ecf20Sopenharmony_ci else 908c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x0000000d); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 938c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE, 948c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ciint snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci u32 data; 1008c2ecf20Sopenharmony_ci int err; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci err = get_clock(tscm, &data); 1038c2ecf20Sopenharmony_ci if (err < 0) 1048c2ecf20Sopenharmony_ci return err; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci data = (data & 0xff000000) >> 24; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Check base rate. */ 1098c2ecf20Sopenharmony_ci if ((data & 0x0f) == 0x01) 1108c2ecf20Sopenharmony_ci *rate = 44100; 1118c2ecf20Sopenharmony_ci else if ((data & 0x0f) == 0x02) 1128c2ecf20Sopenharmony_ci *rate = 48000; 1138c2ecf20Sopenharmony_ci else 1148c2ecf20Sopenharmony_ci return -EAGAIN; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* Check multiplier. */ 1178c2ecf20Sopenharmony_ci if ((data & 0xf0) == 0x80) 1188c2ecf20Sopenharmony_ci *rate *= 2; 1198c2ecf20Sopenharmony_ci else if ((data & 0xf0) != 0x00) 1208c2ecf20Sopenharmony_ci return -EAGAIN; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return err; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciint snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci u32 data; 1288c2ecf20Sopenharmony_ci int err; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci err = get_clock(tscm, &data); 1318c2ecf20Sopenharmony_ci if (err < 0) 1328c2ecf20Sopenharmony_ci return err; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci *clock = ((data & 0x00ff0000) >> 16) - 1; 1358c2ecf20Sopenharmony_ci if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT) 1368c2ecf20Sopenharmony_ci return -EIO; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int enable_data_channels(struct snd_tscm *tscm) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci __be32 reg; 1448c2ecf20Sopenharmony_ci u32 data; 1458c2ecf20Sopenharmony_ci unsigned int i; 1468c2ecf20Sopenharmony_ci int err; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci data = 0; 1498c2ecf20Sopenharmony_ci for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i) 1508c2ecf20Sopenharmony_ci data |= BIT(i); 1518c2ecf20Sopenharmony_ci if (tscm->spec->has_adat) 1528c2ecf20Sopenharmony_ci data |= 0x0000ff00; 1538c2ecf20Sopenharmony_ci if (tscm->spec->has_spdif) 1548c2ecf20Sopenharmony_ci data |= 0x00030000; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci reg = cpu_to_be32(data); 1578c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 1588c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS, 1598c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 1608c2ecf20Sopenharmony_ci if (err < 0) 1618c2ecf20Sopenharmony_ci return err; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci data = 0; 1648c2ecf20Sopenharmony_ci for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i) 1658c2ecf20Sopenharmony_ci data |= BIT(i); 1668c2ecf20Sopenharmony_ci if (tscm->spec->has_adat) 1678c2ecf20Sopenharmony_ci data |= 0x0000ff00; 1688c2ecf20Sopenharmony_ci if (tscm->spec->has_spdif) 1698c2ecf20Sopenharmony_ci data |= 0x00030000; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci reg = cpu_to_be32(data); 1728c2ecf20Sopenharmony_ci return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 1738c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS, 1748c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int set_stream_formats(struct snd_tscm *tscm, unsigned int rate) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci __be32 reg; 1808c2ecf20Sopenharmony_ci int err; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci // Set an option for unknown purpose. 1838c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00200000); 1848c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 1858c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION, 1868c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 1878c2ecf20Sopenharmony_ci if (err < 0) 1888c2ecf20Sopenharmony_ci return err; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return enable_data_channels(tscm); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic void finish_session(struct snd_tscm *tscm) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci __be32 reg; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci reg = 0; 1988c2ecf20Sopenharmony_ci snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 1998c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING, 2008c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci reg = 0; 2038c2ecf20Sopenharmony_ci snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 2048c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON, 2058c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci // Unregister channels. 2088c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00000000); 2098c2ecf20Sopenharmony_ci snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 2108c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH, 2118c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2128c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00000000); 2138c2ecf20Sopenharmony_ci snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 2148c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN, 2158c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2168c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00000000); 2178c2ecf20Sopenharmony_ci snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 2188c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH, 2198c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int begin_session(struct snd_tscm *tscm) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci __be32 reg; 2258c2ecf20Sopenharmony_ci int err; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci // Register the isochronous channel for transmitting stream. 2288c2ecf20Sopenharmony_ci reg = cpu_to_be32(tscm->tx_resources.channel); 2298c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 2308c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH, 2318c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2328c2ecf20Sopenharmony_ci if (err < 0) 2338c2ecf20Sopenharmony_ci return err; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci // Unknown. 2368c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00000002); 2378c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 2388c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN, 2398c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2408c2ecf20Sopenharmony_ci if (err < 0) 2418c2ecf20Sopenharmony_ci return err; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci // Register the isochronous channel for receiving stream. 2448c2ecf20Sopenharmony_ci reg = cpu_to_be32(tscm->rx_resources.channel); 2458c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 2468c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH, 2478c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2488c2ecf20Sopenharmony_ci if (err < 0) 2498c2ecf20Sopenharmony_ci return err; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00000001); 2528c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 2538c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING, 2548c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2558c2ecf20Sopenharmony_ci if (err < 0) 2568c2ecf20Sopenharmony_ci return err; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00000001); 2598c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 2608c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON, 2618c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2628c2ecf20Sopenharmony_ci if (err < 0) 2638c2ecf20Sopenharmony_ci return err; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci // Set an option for unknown purpose. 2668c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00002000); 2678c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 2688c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION, 2698c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2708c2ecf20Sopenharmony_ci if (err < 0) 2718c2ecf20Sopenharmony_ci return err; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci // Start multiplexing PCM samples on packets. 2748c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00000001); 2758c2ecf20Sopenharmony_ci return snd_fw_transaction(tscm->unit, 2768c2ecf20Sopenharmony_ci TCODE_WRITE_QUADLET_REQUEST, 2778c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON, 2788c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int keep_resources(struct snd_tscm *tscm, unsigned int rate, 2828c2ecf20Sopenharmony_ci struct amdtp_stream *stream) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct fw_iso_resources *resources; 2858c2ecf20Sopenharmony_ci int err; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (stream == &tscm->tx_stream) 2888c2ecf20Sopenharmony_ci resources = &tscm->tx_resources; 2898c2ecf20Sopenharmony_ci else 2908c2ecf20Sopenharmony_ci resources = &tscm->rx_resources; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci err = amdtp_tscm_set_parameters(stream, rate); 2938c2ecf20Sopenharmony_ci if (err < 0) 2948c2ecf20Sopenharmony_ci return err; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return fw_iso_resources_allocate(resources, 2978c2ecf20Sopenharmony_ci amdtp_stream_get_max_payload(stream), 2988c2ecf20Sopenharmony_ci fw_parent_device(tscm->unit)->max_speed); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct fw_iso_resources *resources; 3048c2ecf20Sopenharmony_ci enum amdtp_stream_direction dir; 3058c2ecf20Sopenharmony_ci unsigned int pcm_channels; 3068c2ecf20Sopenharmony_ci int err; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (s == &tscm->tx_stream) { 3098c2ecf20Sopenharmony_ci resources = &tscm->tx_resources; 3108c2ecf20Sopenharmony_ci dir = AMDTP_IN_STREAM; 3118c2ecf20Sopenharmony_ci pcm_channels = tscm->spec->pcm_capture_analog_channels; 3128c2ecf20Sopenharmony_ci } else { 3138c2ecf20Sopenharmony_ci resources = &tscm->rx_resources; 3148c2ecf20Sopenharmony_ci dir = AMDTP_OUT_STREAM; 3158c2ecf20Sopenharmony_ci pcm_channels = tscm->spec->pcm_playback_analog_channels; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (tscm->spec->has_adat) 3198c2ecf20Sopenharmony_ci pcm_channels += 8; 3208c2ecf20Sopenharmony_ci if (tscm->spec->has_spdif) 3218c2ecf20Sopenharmony_ci pcm_channels += 2; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci err = fw_iso_resources_init(resources, tscm->unit); 3248c2ecf20Sopenharmony_ci if (err < 0) 3258c2ecf20Sopenharmony_ci return err; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels); 3288c2ecf20Sopenharmony_ci if (err < 0) 3298c2ecf20Sopenharmony_ci fw_iso_resources_free(resources); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return err; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci amdtp_stream_destroy(s); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (s == &tscm->tx_stream) 3398c2ecf20Sopenharmony_ci fw_iso_resources_destroy(&tscm->tx_resources); 3408c2ecf20Sopenharmony_ci else 3418c2ecf20Sopenharmony_ci fw_iso_resources_destroy(&tscm->rx_resources); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ciint snd_tscm_stream_init_duplex(struct snd_tscm *tscm) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci int err; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci err = init_stream(tscm, &tscm->tx_stream); 3498c2ecf20Sopenharmony_ci if (err < 0) 3508c2ecf20Sopenharmony_ci return err; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci err = init_stream(tscm, &tscm->rx_stream); 3538c2ecf20Sopenharmony_ci if (err < 0) { 3548c2ecf20Sopenharmony_ci destroy_stream(tscm, &tscm->tx_stream); 3558c2ecf20Sopenharmony_ci return err; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci err = amdtp_domain_init(&tscm->domain); 3598c2ecf20Sopenharmony_ci if (err < 0) { 3608c2ecf20Sopenharmony_ci destroy_stream(tscm, &tscm->tx_stream); 3618c2ecf20Sopenharmony_ci destroy_stream(tscm, &tscm->rx_stream); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return err; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci// At bus reset, streaming is stopped and some registers are clear. 3688c2ecf20Sopenharmony_civoid snd_tscm_stream_update_duplex(struct snd_tscm *tscm) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci amdtp_domain_stop(&tscm->domain); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci amdtp_stream_pcm_abort(&tscm->tx_stream); 3738c2ecf20Sopenharmony_ci amdtp_stream_pcm_abort(&tscm->rx_stream); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci// This function should be called before starting streams or after stopping 3778c2ecf20Sopenharmony_ci// streams. 3788c2ecf20Sopenharmony_civoid snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci amdtp_domain_destroy(&tscm->domain); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci destroy_stream(tscm, &tscm->rx_stream); 3838c2ecf20Sopenharmony_ci destroy_stream(tscm, &tscm->tx_stream); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciint snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate, 3878c2ecf20Sopenharmony_ci unsigned int frames_per_period, 3888c2ecf20Sopenharmony_ci unsigned int frames_per_buffer) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci unsigned int curr_rate; 3918c2ecf20Sopenharmony_ci int err; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci err = snd_tscm_stream_get_rate(tscm, &curr_rate); 3948c2ecf20Sopenharmony_ci if (err < 0) 3958c2ecf20Sopenharmony_ci return err; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (tscm->substreams_counter == 0 || rate != curr_rate) { 3988c2ecf20Sopenharmony_ci amdtp_domain_stop(&tscm->domain); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci finish_session(tscm); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci fw_iso_resources_free(&tscm->tx_resources); 4038c2ecf20Sopenharmony_ci fw_iso_resources_free(&tscm->rx_resources); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci err = set_clock(tscm, rate, INT_MAX); 4068c2ecf20Sopenharmony_ci if (err < 0) 4078c2ecf20Sopenharmony_ci return err; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci err = keep_resources(tscm, rate, &tscm->tx_stream); 4108c2ecf20Sopenharmony_ci if (err < 0) 4118c2ecf20Sopenharmony_ci return err; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci err = keep_resources(tscm, rate, &tscm->rx_stream); 4148c2ecf20Sopenharmony_ci if (err < 0) { 4158c2ecf20Sopenharmony_ci fw_iso_resources_free(&tscm->tx_resources); 4168c2ecf20Sopenharmony_ci return err; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci err = amdtp_domain_set_events_per_period(&tscm->domain, 4208c2ecf20Sopenharmony_ci frames_per_period, frames_per_buffer); 4218c2ecf20Sopenharmony_ci if (err < 0) { 4228c2ecf20Sopenharmony_ci fw_iso_resources_free(&tscm->tx_resources); 4238c2ecf20Sopenharmony_ci fw_iso_resources_free(&tscm->rx_resources); 4248c2ecf20Sopenharmony_ci return err; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ciint snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci unsigned int generation = tscm->rx_resources.generation; 4348c2ecf20Sopenharmony_ci int err; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (tscm->substreams_counter == 0) 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (amdtp_streaming_error(&tscm->rx_stream) || 4408c2ecf20Sopenharmony_ci amdtp_streaming_error(&tscm->tx_stream)) { 4418c2ecf20Sopenharmony_ci amdtp_domain_stop(&tscm->domain); 4428c2ecf20Sopenharmony_ci finish_session(tscm); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (generation != fw_parent_device(tscm->unit)->card->generation) { 4468c2ecf20Sopenharmony_ci err = fw_iso_resources_update(&tscm->tx_resources); 4478c2ecf20Sopenharmony_ci if (err < 0) 4488c2ecf20Sopenharmony_ci goto error; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci err = fw_iso_resources_update(&tscm->rx_resources); 4518c2ecf20Sopenharmony_ci if (err < 0) 4528c2ecf20Sopenharmony_ci goto error; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (!amdtp_stream_running(&tscm->rx_stream)) { 4568c2ecf20Sopenharmony_ci int spd = fw_parent_device(tscm->unit)->max_speed; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci err = set_stream_formats(tscm, rate); 4598c2ecf20Sopenharmony_ci if (err < 0) 4608c2ecf20Sopenharmony_ci goto error; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci err = begin_session(tscm); 4638c2ecf20Sopenharmony_ci if (err < 0) 4648c2ecf20Sopenharmony_ci goto error; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream, 4678c2ecf20Sopenharmony_ci tscm->rx_resources.channel, spd); 4688c2ecf20Sopenharmony_ci if (err < 0) 4698c2ecf20Sopenharmony_ci goto error; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream, 4728c2ecf20Sopenharmony_ci tscm->tx_resources.channel, spd); 4738c2ecf20Sopenharmony_ci if (err < 0) 4748c2ecf20Sopenharmony_ci goto error; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci err = amdtp_domain_start(&tscm->domain, 0); 4778c2ecf20Sopenharmony_ci if (err < 0) 4788c2ecf20Sopenharmony_ci goto error; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (!amdtp_stream_wait_callback(&tscm->rx_stream, 4818c2ecf20Sopenharmony_ci CALLBACK_TIMEOUT) || 4828c2ecf20Sopenharmony_ci !amdtp_stream_wait_callback(&tscm->tx_stream, 4838c2ecf20Sopenharmony_ci CALLBACK_TIMEOUT)) { 4848c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 4858c2ecf20Sopenharmony_ci goto error; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_cierror: 4918c2ecf20Sopenharmony_ci amdtp_domain_stop(&tscm->domain); 4928c2ecf20Sopenharmony_ci finish_session(tscm); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return err; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_civoid snd_tscm_stream_stop_duplex(struct snd_tscm *tscm) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci if (tscm->substreams_counter == 0) { 5008c2ecf20Sopenharmony_ci amdtp_domain_stop(&tscm->domain); 5018c2ecf20Sopenharmony_ci finish_session(tscm); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci fw_iso_resources_free(&tscm->tx_resources); 5048c2ecf20Sopenharmony_ci fw_iso_resources_free(&tscm->rx_resources); 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_civoid snd_tscm_stream_lock_changed(struct snd_tscm *tscm) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci tscm->dev_lock_changed = true; 5118c2ecf20Sopenharmony_ci wake_up(&tscm->hwdep_wait); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ciint snd_tscm_stream_lock_try(struct snd_tscm *tscm) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci int err; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci spin_lock_irq(&tscm->lock); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* user land lock this */ 5218c2ecf20Sopenharmony_ci if (tscm->dev_lock_count < 0) { 5228c2ecf20Sopenharmony_ci err = -EBUSY; 5238c2ecf20Sopenharmony_ci goto end; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* this is the first time */ 5278c2ecf20Sopenharmony_ci if (tscm->dev_lock_count++ == 0) 5288c2ecf20Sopenharmony_ci snd_tscm_stream_lock_changed(tscm); 5298c2ecf20Sopenharmony_ci err = 0; 5308c2ecf20Sopenharmony_ciend: 5318c2ecf20Sopenharmony_ci spin_unlock_irq(&tscm->lock); 5328c2ecf20Sopenharmony_ci return err; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_civoid snd_tscm_stream_lock_release(struct snd_tscm *tscm) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci spin_lock_irq(&tscm->lock); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (WARN_ON(tscm->dev_lock_count <= 0)) 5408c2ecf20Sopenharmony_ci goto end; 5418c2ecf20Sopenharmony_ci if (--tscm->dev_lock_count == 0) 5428c2ecf20Sopenharmony_ci snd_tscm_stream_lock_changed(tscm); 5438c2ecf20Sopenharmony_ciend: 5448c2ecf20Sopenharmony_ci spin_unlock_irq(&tscm->lock); 5458c2ecf20Sopenharmony_ci} 546