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