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				&reg, 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				 &reg, 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				  &reg, 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				 &reg, 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				  &reg, 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				 &reg, 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			   &reg, 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			   &reg, 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			   &reg, 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			   &reg, 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			   &reg, 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				 &reg, 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				 &reg, 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				 &reg, 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				 &reg, 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				 &reg, 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				 &reg, 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				  &reg, 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