1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26eb6c81eSTakashi Sakamoto /*
36eb6c81eSTakashi Sakamoto  * dice_stream.c - a part of driver for DICE based devices
46eb6c81eSTakashi Sakamoto  *
56eb6c81eSTakashi Sakamoto  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
66eb6c81eSTakashi Sakamoto  * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
76eb6c81eSTakashi Sakamoto  */
86eb6c81eSTakashi Sakamoto 
96eb6c81eSTakashi Sakamoto #include "dice.h"
106eb6c81eSTakashi Sakamoto 
11288a8d0cSTakashi Sakamoto #define	CALLBACK_TIMEOUT	200
12dfabc0eeSTakashi Sakamoto #define NOTIFICATION_TIMEOUT_MS	(2 * MSEC_PER_SEC)
13288a8d0cSTakashi Sakamoto 
148cc1a8abSTakashi Sakamoto struct reg_params {
158cc1a8abSTakashi Sakamoto 	unsigned int count;
168cc1a8abSTakashi Sakamoto 	unsigned int size;
178cc1a8abSTakashi Sakamoto };
188cc1a8abSTakashi Sakamoto 
196eb6c81eSTakashi Sakamoto const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
206eb6c81eSTakashi Sakamoto 	/* mode 0 */
216eb6c81eSTakashi Sakamoto 	[0] =  32000,
226eb6c81eSTakashi Sakamoto 	[1] =  44100,
236eb6c81eSTakashi Sakamoto 	[2] =  48000,
246eb6c81eSTakashi Sakamoto 	/* mode 1 */
256eb6c81eSTakashi Sakamoto 	[3] =  88200,
266eb6c81eSTakashi Sakamoto 	[4] =  96000,
276eb6c81eSTakashi Sakamoto 	/* mode 2 */
286eb6c81eSTakashi Sakamoto 	[5] = 176400,
296eb6c81eSTakashi Sakamoto 	[6] = 192000,
306eb6c81eSTakashi Sakamoto };
316eb6c81eSTakashi Sakamoto 
32b60152f7STakashi Sakamoto int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
33b60152f7STakashi Sakamoto 				  enum snd_dice_rate_mode *mode)
34b60152f7STakashi Sakamoto {
35b60152f7STakashi Sakamoto 	/* Corresponding to each entry in snd_dice_rates. */
36b60152f7STakashi Sakamoto 	static const enum snd_dice_rate_mode modes[] = {
37b60152f7STakashi Sakamoto 		[0] = SND_DICE_RATE_MODE_LOW,
38b60152f7STakashi Sakamoto 		[1] = SND_DICE_RATE_MODE_LOW,
39b60152f7STakashi Sakamoto 		[2] = SND_DICE_RATE_MODE_LOW,
40b60152f7STakashi Sakamoto 		[3] = SND_DICE_RATE_MODE_MIDDLE,
41b60152f7STakashi Sakamoto 		[4] = SND_DICE_RATE_MODE_MIDDLE,
42b60152f7STakashi Sakamoto 		[5] = SND_DICE_RATE_MODE_HIGH,
43b60152f7STakashi Sakamoto 		[6] = SND_DICE_RATE_MODE_HIGH,
44b60152f7STakashi Sakamoto 	};
45b60152f7STakashi Sakamoto 	int i;
46b60152f7STakashi Sakamoto 
47b60152f7STakashi Sakamoto 	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
48b60152f7STakashi Sakamoto 		if (!(dice->clock_caps & BIT(i)))
49b60152f7STakashi Sakamoto 			continue;
50b60152f7STakashi Sakamoto 		if (snd_dice_rates[i] != rate)
51b60152f7STakashi Sakamoto 			continue;
52b60152f7STakashi Sakamoto 
53b60152f7STakashi Sakamoto 		*mode = modes[i];
54b60152f7STakashi Sakamoto 		return 0;
55b60152f7STakashi Sakamoto 	}
56b60152f7STakashi Sakamoto 
57b60152f7STakashi Sakamoto 	return -EINVAL;
58b60152f7STakashi Sakamoto }
59b60152f7STakashi Sakamoto 
60dfabc0eeSTakashi Sakamoto /*
61dfabc0eeSTakashi Sakamoto  * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE
62dfabc0eeSTakashi Sakamoto  * to GLOBAL_STATUS. Especially, just after powering on, these are different.
63dfabc0eeSTakashi Sakamoto  */
64afa617f2STakashi Sakamoto static int ensure_phase_lock(struct snd_dice *dice, unsigned int rate)
65dfabc0eeSTakashi Sakamoto {
66fbeac84dSTakashi Sakamoto 	__be32 reg, nominal;
67afa617f2STakashi Sakamoto 	u32 data;
68afa617f2STakashi Sakamoto 	int i;
69dfabc0eeSTakashi Sakamoto 	int err;
70dfabc0eeSTakashi Sakamoto 
71dfabc0eeSTakashi Sakamoto 	err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
72dfabc0eeSTakashi Sakamoto 					       &reg, sizeof(reg));
73dfabc0eeSTakashi Sakamoto 	if (err < 0)
74dfabc0eeSTakashi Sakamoto 		return err;
75dfabc0eeSTakashi Sakamoto 
76afa617f2STakashi Sakamoto 	data = be32_to_cpu(reg);
77afa617f2STakashi Sakamoto 
78afa617f2STakashi Sakamoto 	data &= ~CLOCK_RATE_MASK;
79afa617f2STakashi Sakamoto 	for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
80afa617f2STakashi Sakamoto 		if (snd_dice_rates[i] == rate)
81afa617f2STakashi Sakamoto 			break;
82afa617f2STakashi Sakamoto 	}
83afa617f2STakashi Sakamoto 	if (i == ARRAY_SIZE(snd_dice_rates))
84afa617f2STakashi Sakamoto 		return -EINVAL;
85afa617f2STakashi Sakamoto 	data |= i << CLOCK_RATE_SHIFT;
86afa617f2STakashi Sakamoto 
87dfabc0eeSTakashi Sakamoto 	if (completion_done(&dice->clock_accepted))
88dfabc0eeSTakashi Sakamoto 		reinit_completion(&dice->clock_accepted);
89dfabc0eeSTakashi Sakamoto 
90afa617f2STakashi Sakamoto 	reg = cpu_to_be32(data);
91dfabc0eeSTakashi Sakamoto 	err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
92dfabc0eeSTakashi Sakamoto 						&reg, sizeof(reg));
93dfabc0eeSTakashi Sakamoto 	if (err < 0)
94dfabc0eeSTakashi Sakamoto 		return err;
95dfabc0eeSTakashi Sakamoto 
96dfabc0eeSTakashi Sakamoto 	if (wait_for_completion_timeout(&dice->clock_accepted,
97fbeac84dSTakashi Sakamoto 			msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
98fbeac84dSTakashi Sakamoto 		/*
99fbeac84dSTakashi Sakamoto 		 * Old versions of Dice firmware transfer no notification when
100fbeac84dSTakashi Sakamoto 		 * the same clock status as current one is set. In this case,
101fbeac84dSTakashi Sakamoto 		 * just check current clock status.
102fbeac84dSTakashi Sakamoto 		 */
103fbeac84dSTakashi Sakamoto 		err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS,
104fbeac84dSTakashi Sakamoto 						&nominal, sizeof(nominal));
105fbeac84dSTakashi Sakamoto 		if (err < 0)
106fbeac84dSTakashi Sakamoto 			return err;
107fbeac84dSTakashi Sakamoto 		if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED))
108dfabc0eeSTakashi Sakamoto 			return -ETIMEDOUT;
109fbeac84dSTakashi Sakamoto 	}
110dfabc0eeSTakashi Sakamoto 
111dfabc0eeSTakashi Sakamoto 	return 0;
112dfabc0eeSTakashi Sakamoto }
113dfabc0eeSTakashi Sakamoto 
1148cc1a8abSTakashi Sakamoto static int get_register_params(struct snd_dice *dice,
1158cc1a8abSTakashi Sakamoto 			       struct reg_params *tx_params,
1168cc1a8abSTakashi Sakamoto 			       struct reg_params *rx_params)
1176eb6c81eSTakashi Sakamoto {
118436b5abeSTakashi Sakamoto 	__be32 reg[2];
119436b5abeSTakashi Sakamoto 	int err;
1206eb6c81eSTakashi Sakamoto 
121436b5abeSTakashi Sakamoto 	err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
122436b5abeSTakashi Sakamoto 	if (err < 0)
123436b5abeSTakashi Sakamoto 		return err;
1248cc1a8abSTakashi Sakamoto 	tx_params->count =
1258cc1a8abSTakashi Sakamoto 			min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
1268cc1a8abSTakashi Sakamoto 	tx_params->size = be32_to_cpu(reg[1]) * 4;
127288a8d0cSTakashi Sakamoto 
128436b5abeSTakashi Sakamoto 	err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg));
129436b5abeSTakashi Sakamoto 	if (err < 0)
130436b5abeSTakashi Sakamoto 		return err;
1318cc1a8abSTakashi Sakamoto 	rx_params->count =
1328cc1a8abSTakashi Sakamoto 			min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
1338cc1a8abSTakashi Sakamoto 	rx_params->size = be32_to_cpu(reg[1]) * 4;
134436b5abeSTakashi Sakamoto 
135436b5abeSTakashi Sakamoto 	return 0;
136436b5abeSTakashi Sakamoto }
137436b5abeSTakashi Sakamoto 
138436b5abeSTakashi Sakamoto static void release_resources(struct snd_dice *dice)
139436b5abeSTakashi Sakamoto {
1403cd2c2d7STakashi Sakamoto 	int i;
141436b5abeSTakashi Sakamoto 
1423cd2c2d7STakashi Sakamoto 	for (i = 0; i < MAX_STREAMS; ++i) {
143436b5abeSTakashi Sakamoto 		fw_iso_resources_free(&dice->tx_resources[i]);
144436b5abeSTakashi Sakamoto 		fw_iso_resources_free(&dice->rx_resources[i]);
145436b5abeSTakashi Sakamoto 	}
146436b5abeSTakashi Sakamoto }
147436b5abeSTakashi Sakamoto 
148436b5abeSTakashi Sakamoto static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
1498cc1a8abSTakashi Sakamoto 			 struct reg_params *params)
150436b5abeSTakashi Sakamoto {
151436b5abeSTakashi Sakamoto 	__be32 reg;
152436b5abeSTakashi Sakamoto 	unsigned int i;
153436b5abeSTakashi Sakamoto 
1548cc1a8abSTakashi Sakamoto 	for (i = 0; i < params->count; i++) {
155436b5abeSTakashi Sakamoto 		reg = cpu_to_be32((u32)-1);
156436b5abeSTakashi Sakamoto 		if (dir == AMDTP_IN_STREAM) {
157436b5abeSTakashi Sakamoto 			snd_dice_transaction_write_tx(dice,
1588cc1a8abSTakashi Sakamoto 					params->size * i + TX_ISOCHRONOUS,
159436b5abeSTakashi Sakamoto 					&reg, sizeof(reg));
160436b5abeSTakashi Sakamoto 		} else {
161436b5abeSTakashi Sakamoto 			snd_dice_transaction_write_rx(dice,
1628cc1a8abSTakashi Sakamoto 					params->size * i + RX_ISOCHRONOUS,
163436b5abeSTakashi Sakamoto 					&reg, sizeof(reg));
164436b5abeSTakashi Sakamoto 		}
165436b5abeSTakashi Sakamoto 	}
166288a8d0cSTakashi Sakamoto }
167288a8d0cSTakashi Sakamoto 
168c738aed1STakashi Sakamoto static int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream,
169c738aed1STakashi Sakamoto 			  struct fw_iso_resources *resources, unsigned int rate,
170c738aed1STakashi Sakamoto 			  unsigned int pcm_chs, unsigned int midi_ports)
171288a8d0cSTakashi Sakamoto {
17227ec83b5STakashi Sakamoto 	bool double_pcm_frames;
173436b5abeSTakashi Sakamoto 	unsigned int i;
174288a8d0cSTakashi Sakamoto 	int err;
175288a8d0cSTakashi Sakamoto 
176c738aed1STakashi Sakamoto 	// At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
177c738aed1STakashi Sakamoto 	// one data block of AMDTP packet. Thus sampling transfer frequency is
178c738aed1STakashi Sakamoto 	// a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
179c738aed1STakashi Sakamoto 	// transferred on AMDTP packets at 96 kHz. Two successive samples of a
180c738aed1STakashi Sakamoto 	// channel are stored consecutively in the packet. This quirk is called
181c738aed1STakashi Sakamoto 	// as 'Dual Wire'.
182c738aed1STakashi Sakamoto 	// For this quirk, blocking mode is required and PCM buffer size should
183c738aed1STakashi Sakamoto 	// be aligned to SYT_INTERVAL.
1846f688268STakashi Sakamoto 	double_pcm_frames = rate > 96000;
18527ec83b5STakashi Sakamoto 	if (double_pcm_frames) {
186288a8d0cSTakashi Sakamoto 		rate /= 2;
187288a8d0cSTakashi Sakamoto 		pcm_chs *= 2;
188288a8d0cSTakashi Sakamoto 	}
189288a8d0cSTakashi Sakamoto 
19051c29fd2STakashi Sakamoto 	err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
19151c29fd2STakashi Sakamoto 					 double_pcm_frames);
192547e631cSTakashi Sakamoto 	if (err < 0)
193436b5abeSTakashi Sakamoto 		return err;
194547e631cSTakashi Sakamoto 
19527ec83b5STakashi Sakamoto 	if (double_pcm_frames) {
196288a8d0cSTakashi Sakamoto 		pcm_chs /= 2;
197288a8d0cSTakashi Sakamoto 
198288a8d0cSTakashi Sakamoto 		for (i = 0; i < pcm_chs; i++) {
199f65be911STakashi Sakamoto 			amdtp_am824_set_pcm_position(stream, i, i * 2);
200f65be911STakashi Sakamoto 			amdtp_am824_set_pcm_position(stream, i + pcm_chs,
201f65be911STakashi Sakamoto 						     i * 2 + 1);
202288a8d0cSTakashi Sakamoto 		}
203288a8d0cSTakashi Sakamoto 	}
204288a8d0cSTakashi Sakamoto 
205436b5abeSTakashi Sakamoto 	return fw_iso_resources_allocate(resources,
206436b5abeSTakashi Sakamoto 				amdtp_stream_get_max_payload(stream),
207436b5abeSTakashi Sakamoto 				fw_parent_device(dice->unit)->max_speed);
208288a8d0cSTakashi Sakamoto }
209288a8d0cSTakashi Sakamoto 
210c738aed1STakashi Sakamoto static int keep_dual_resources(struct snd_dice *dice, unsigned int rate,
211c738aed1STakashi Sakamoto 			       enum amdtp_stream_direction dir,
212c738aed1STakashi Sakamoto 			       struct reg_params *params)
213c738aed1STakashi Sakamoto {
214c738aed1STakashi Sakamoto 	enum snd_dice_rate_mode mode;
215c738aed1STakashi Sakamoto 	int i;
216c738aed1STakashi Sakamoto 	int err;
217c738aed1STakashi Sakamoto 
218c738aed1STakashi Sakamoto 	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
219c738aed1STakashi Sakamoto 	if (err < 0)
220c738aed1STakashi Sakamoto 		return err;
221c738aed1STakashi Sakamoto 
222c738aed1STakashi Sakamoto 	for (i = 0; i < params->count; ++i) {
223c738aed1STakashi Sakamoto 		__be32 reg[2];
224c738aed1STakashi Sakamoto 		struct amdtp_stream *stream;
225c738aed1STakashi Sakamoto 		struct fw_iso_resources *resources;
226c738aed1STakashi Sakamoto 		unsigned int pcm_cache;
227c738aed1STakashi Sakamoto 		unsigned int midi_cache;
228c738aed1STakashi Sakamoto 		unsigned int pcm_chs;
229c738aed1STakashi Sakamoto 		unsigned int midi_ports;
230c738aed1STakashi Sakamoto 
231c738aed1STakashi Sakamoto 		if (dir == AMDTP_IN_STREAM) {
232c738aed1STakashi Sakamoto 			stream = &dice->tx_stream[i];
233c738aed1STakashi Sakamoto 			resources = &dice->tx_resources[i];
234c738aed1STakashi Sakamoto 
235c738aed1STakashi Sakamoto 			pcm_cache = dice->tx_pcm_chs[i][mode];
236c738aed1STakashi Sakamoto 			midi_cache = dice->tx_midi_ports[i];
237c738aed1STakashi Sakamoto 			err = snd_dice_transaction_read_tx(dice,
238c738aed1STakashi Sakamoto 					params->size * i + TX_NUMBER_AUDIO,
239c738aed1STakashi Sakamoto 					reg, sizeof(reg));
240c738aed1STakashi Sakamoto 		} else {
241c738aed1STakashi Sakamoto 			stream = &dice->rx_stream[i];
242c738aed1STakashi Sakamoto 			resources = &dice->rx_resources[i];
243c738aed1STakashi Sakamoto 
244c738aed1STakashi Sakamoto 			pcm_cache = dice->rx_pcm_chs[i][mode];
245c738aed1STakashi Sakamoto 			midi_cache = dice->rx_midi_ports[i];
246c738aed1STakashi Sakamoto 			err = snd_dice_transaction_read_rx(dice,
247c738aed1STakashi Sakamoto 					params->size * i + RX_NUMBER_AUDIO,
248c738aed1STakashi Sakamoto 					reg, sizeof(reg));
249c738aed1STakashi Sakamoto 		}
250c738aed1STakashi Sakamoto 		if (err < 0)
251c738aed1STakashi Sakamoto 			return err;
252c738aed1STakashi Sakamoto 		pcm_chs = be32_to_cpu(reg[0]);
253c738aed1STakashi Sakamoto 		midi_ports = be32_to_cpu(reg[1]);
254c738aed1STakashi Sakamoto 
255c738aed1STakashi Sakamoto 		// These are important for developer of this driver.
256c738aed1STakashi Sakamoto 		if (pcm_chs != pcm_cache || midi_ports != midi_cache) {
257c738aed1STakashi Sakamoto 			dev_info(&dice->unit->device,
258c738aed1STakashi Sakamoto 				 "cache mismatch: pcm: %u:%u, midi: %u:%u\n",
259c738aed1STakashi Sakamoto 				 pcm_chs, pcm_cache, midi_ports, midi_cache);
260c738aed1STakashi Sakamoto 			return -EPROTO;
261c738aed1STakashi Sakamoto 		}
262c738aed1STakashi Sakamoto 
263c738aed1STakashi Sakamoto 		err = keep_resources(dice, stream, resources, rate, pcm_chs,
264c738aed1STakashi Sakamoto 				     midi_ports);
265c738aed1STakashi Sakamoto 		if (err < 0)
266c738aed1STakashi Sakamoto 			return err;
267c738aed1STakashi Sakamoto 	}
268c738aed1STakashi Sakamoto 
269c738aed1STakashi Sakamoto 	return 0;
270c738aed1STakashi Sakamoto }
271c738aed1STakashi Sakamoto 
272b3480638STakashi Sakamoto static void finish_session(struct snd_dice *dice, struct reg_params *tx_params,
273b3480638STakashi Sakamoto 			   struct reg_params *rx_params)
274b3480638STakashi Sakamoto {
275b3480638STakashi Sakamoto 	stop_streams(dice, AMDTP_IN_STREAM, tx_params);
276b3480638STakashi Sakamoto 	stop_streams(dice, AMDTP_OUT_STREAM, rx_params);
277b3480638STakashi Sakamoto 
278b3480638STakashi Sakamoto 	snd_dice_transaction_clear_enable(dice);
279b3480638STakashi Sakamoto }
280b3480638STakashi Sakamoto 
2813cd2c2d7STakashi Sakamoto int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate)
2823cd2c2d7STakashi Sakamoto {
2833cd2c2d7STakashi Sakamoto 	unsigned int curr_rate;
2843cd2c2d7STakashi Sakamoto 	int err;
2853cd2c2d7STakashi Sakamoto 
2863cd2c2d7STakashi Sakamoto 	// Check sampling transmission frequency.
2873cd2c2d7STakashi Sakamoto 	err = snd_dice_transaction_get_rate(dice, &curr_rate);
2883cd2c2d7STakashi Sakamoto 	if (err < 0)
2893cd2c2d7STakashi Sakamoto 		return err;
2903cd2c2d7STakashi Sakamoto 	if (rate == 0)
2913cd2c2d7STakashi Sakamoto 		rate = curr_rate;
2923cd2c2d7STakashi Sakamoto 
2933cd2c2d7STakashi Sakamoto 	if (dice->substreams_counter == 0 || curr_rate != rate) {
2943cd2c2d7STakashi Sakamoto 		struct reg_params tx_params, rx_params;
2953cd2c2d7STakashi Sakamoto 
296e9f21129STakashi Sakamoto 		amdtp_domain_stop(&dice->domain);
297e9f21129STakashi Sakamoto 
2983cd2c2d7STakashi Sakamoto 		err = get_register_params(dice, &tx_params, &rx_params);
2993cd2c2d7STakashi Sakamoto 		if (err < 0)
3003cd2c2d7STakashi Sakamoto 			return err;
3013cd2c2d7STakashi Sakamoto 		finish_session(dice, &tx_params, &rx_params);
3023cd2c2d7STakashi Sakamoto 
3033cd2c2d7STakashi Sakamoto 		release_resources(dice);
3043cd2c2d7STakashi Sakamoto 
3053cd2c2d7STakashi Sakamoto 		// Just after owning the unit (GLOBAL_OWNER), the unit can
3063cd2c2d7STakashi Sakamoto 		// return invalid stream formats. Selecting clock parameters
3073cd2c2d7STakashi Sakamoto 		// have an effect for the unit to refine it.
3083cd2c2d7STakashi Sakamoto 		err = ensure_phase_lock(dice, rate);
3093cd2c2d7STakashi Sakamoto 		if (err < 0)
3103cd2c2d7STakashi Sakamoto 			return err;
3113cd2c2d7STakashi Sakamoto 
3123cd2c2d7STakashi Sakamoto 		// After changing sampling transfer frequency, the value of
3133cd2c2d7STakashi Sakamoto 		// register can be changed.
3143cd2c2d7STakashi Sakamoto 		err = get_register_params(dice, &tx_params, &rx_params);
3153cd2c2d7STakashi Sakamoto 		if (err < 0)
3163cd2c2d7STakashi Sakamoto 			return err;
3173cd2c2d7STakashi Sakamoto 
3183cd2c2d7STakashi Sakamoto 		err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM,
3193cd2c2d7STakashi Sakamoto 					  &tx_params);
3203cd2c2d7STakashi Sakamoto 		if (err < 0)
3213cd2c2d7STakashi Sakamoto 			goto error;
3223cd2c2d7STakashi Sakamoto 
3233cd2c2d7STakashi Sakamoto 		err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM,
3243cd2c2d7STakashi Sakamoto 					  &rx_params);
3253cd2c2d7STakashi Sakamoto 		if (err < 0)
3263cd2c2d7STakashi Sakamoto 			goto error;
3273cd2c2d7STakashi Sakamoto 	}
3283cd2c2d7STakashi Sakamoto 
3293cd2c2d7STakashi Sakamoto 	return 0;
3303cd2c2d7STakashi Sakamoto error:
3313cd2c2d7STakashi Sakamoto 	release_resources(dice);
3323cd2c2d7STakashi Sakamoto 	return err;
3333cd2c2d7STakashi Sakamoto }
3343cd2c2d7STakashi Sakamoto 
335436b5abeSTakashi Sakamoto static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
3368cc1a8abSTakashi Sakamoto 			 unsigned int rate, struct reg_params *params)
337436b5abeSTakashi Sakamoto {
338c738aed1STakashi Sakamoto 	unsigned int max_speed = fw_parent_device(dice->unit)->max_speed;
339c738aed1STakashi Sakamoto 	int i;
340c738aed1STakashi Sakamoto 	int err;
341436b5abeSTakashi Sakamoto 
3428cc1a8abSTakashi Sakamoto 	for (i = 0; i < params->count; i++) {
343c738aed1STakashi Sakamoto 		struct amdtp_stream *stream;
344c738aed1STakashi Sakamoto 		struct fw_iso_resources *resources;
345c738aed1STakashi Sakamoto 		__be32 reg;
346afa617f2STakashi Sakamoto 
347436b5abeSTakashi Sakamoto 		if (dir == AMDTP_IN_STREAM) {
348c738aed1STakashi Sakamoto 			stream = dice->tx_stream + i;
349c738aed1STakashi Sakamoto 			resources = dice->tx_resources + i;
350436b5abeSTakashi Sakamoto 		} else {
351c738aed1STakashi Sakamoto 			stream = dice->rx_stream + i;
352c738aed1STakashi Sakamoto 			resources = dice->rx_resources + i;
353afa617f2STakashi Sakamoto 		}
354afa617f2STakashi Sakamoto 
355c738aed1STakashi Sakamoto 		reg = cpu_to_be32(resources->channel);
356436b5abeSTakashi Sakamoto 		if (dir == AMDTP_IN_STREAM) {
357436b5abeSTakashi Sakamoto 			err = snd_dice_transaction_write_tx(dice,
3588cc1a8abSTakashi Sakamoto 					params->size * i + TX_ISOCHRONOUS,
359c738aed1STakashi Sakamoto 					&reg, sizeof(reg));
360436b5abeSTakashi Sakamoto 		} else {
361436b5abeSTakashi Sakamoto 			err = snd_dice_transaction_write_rx(dice,
3628cc1a8abSTakashi Sakamoto 					params->size * i + RX_ISOCHRONOUS,
363c738aed1STakashi Sakamoto 					&reg, sizeof(reg));
364436b5abeSTakashi Sakamoto 		}
365436b5abeSTakashi Sakamoto 		if (err < 0)
366436b5abeSTakashi Sakamoto 			return err;
367436b5abeSTakashi Sakamoto 
368b0e159feSTakashi Sakamoto 		if (dir == AMDTP_IN_STREAM) {
369c738aed1STakashi Sakamoto 			reg = cpu_to_be32(max_speed);
370b0e159feSTakashi Sakamoto 			err = snd_dice_transaction_write_tx(dice,
371b0e159feSTakashi Sakamoto 					params->size * i + TX_SPEED,
372c738aed1STakashi Sakamoto 					&reg, sizeof(reg));
373b0e159feSTakashi Sakamoto 			if (err < 0)
374b0e159feSTakashi Sakamoto 				return err;
375b0e159feSTakashi Sakamoto 		}
376b0e159feSTakashi Sakamoto 
377e9f21129STakashi Sakamoto 		err = amdtp_domain_add_stream(&dice->domain, stream,
378e9f21129STakashi Sakamoto 					      resources->channel, max_speed);
379288a8d0cSTakashi Sakamoto 		if (err < 0)
380288a8d0cSTakashi Sakamoto 			return err;
381288a8d0cSTakashi Sakamoto 	}
382288a8d0cSTakashi Sakamoto 
383c72d3a0aSTakashi Sakamoto 	return 0;
384436b5abeSTakashi Sakamoto }
385436b5abeSTakashi Sakamoto 
3863cd2c2d7STakashi Sakamoto /*
3873cd2c2d7STakashi Sakamoto  * MEMO: After this function, there're two states of streams:
3883cd2c2d7STakashi Sakamoto  *  - None streams are running.
3893cd2c2d7STakashi Sakamoto  *  - All streams are running.
3903cd2c2d7STakashi Sakamoto  */
3913cd2c2d7STakashi Sakamoto int snd_dice_stream_start_duplex(struct snd_dice *dice)
39220b94544STakashi Sakamoto {
393d5553026STakashi Sakamoto 	unsigned int generation = dice->rx_resources[0].generation;
39420b94544STakashi Sakamoto 	struct reg_params tx_params, rx_params;
3953cd2c2d7STakashi Sakamoto 	unsigned int i;
3963cd2c2d7STakashi Sakamoto 	unsigned int rate;
3973cd2c2d7STakashi Sakamoto 	enum snd_dice_rate_mode mode;
39820b94544STakashi Sakamoto 	int err;
39920b94544STakashi Sakamoto 
4003cd2c2d7STakashi Sakamoto 	if (dice->substreams_counter == 0)
4013cd2c2d7STakashi Sakamoto 		return -EIO;
4023cd2c2d7STakashi Sakamoto 
40320b94544STakashi Sakamoto 	err = get_register_params(dice, &tx_params, &rx_params);
40420b94544STakashi Sakamoto 	if (err < 0)
40520b94544STakashi Sakamoto 		return err;
40620b94544STakashi Sakamoto 
4073cd2c2d7STakashi Sakamoto 	// Check error of packet streaming.
4083cd2c2d7STakashi Sakamoto 	for (i = 0; i < MAX_STREAMS; ++i) {
4093cd2c2d7STakashi Sakamoto 		if (amdtp_streaming_error(&dice->tx_stream[i]) ||
4103cd2c2d7STakashi Sakamoto 		    amdtp_streaming_error(&dice->rx_stream[i])) {
411e9f21129STakashi Sakamoto 			amdtp_domain_stop(&dice->domain);
412b3480638STakashi Sakamoto 			finish_session(dice, &tx_params, &rx_params);
4133cd2c2d7STakashi Sakamoto 			break;
4143cd2c2d7STakashi Sakamoto 		}
41520b94544STakashi Sakamoto 	}
41620b94544STakashi Sakamoto 
417d5553026STakashi Sakamoto 	if (generation != fw_parent_device(dice->unit)->card->generation) {
418d5553026STakashi Sakamoto 		for (i = 0; i < MAX_STREAMS; ++i) {
419d5553026STakashi Sakamoto 			if (i < tx_params.count)
420d5553026STakashi Sakamoto 				fw_iso_resources_update(dice->tx_resources + i);
421d5553026STakashi Sakamoto 			if (i < rx_params.count)
422d5553026STakashi Sakamoto 				fw_iso_resources_update(dice->rx_resources + i);
423d5553026STakashi Sakamoto 		}
424d5553026STakashi Sakamoto 	}
425d5553026STakashi Sakamoto 
4263cd2c2d7STakashi Sakamoto 	// Check required streams are running or not.
4273cd2c2d7STakashi Sakamoto 	err = snd_dice_transaction_get_rate(dice, &rate);
428afa617f2STakashi Sakamoto 	if (err < 0)
429afa617f2STakashi Sakamoto 		return err;
4303cd2c2d7STakashi Sakamoto 	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
4313cd2c2d7STakashi Sakamoto 	if (err < 0)
4323cd2c2d7STakashi Sakamoto 		return err;
4333cd2c2d7STakashi Sakamoto 	for (i = 0; i < MAX_STREAMS; ++i) {
4343cd2c2d7STakashi Sakamoto 		if (dice->tx_pcm_chs[i][mode] > 0 &&
4353cd2c2d7STakashi Sakamoto 		    !amdtp_stream_running(&dice->tx_stream[i]))
4363cd2c2d7STakashi Sakamoto 			break;
4373cd2c2d7STakashi Sakamoto 		if (dice->rx_pcm_chs[i][mode] > 0 &&
4383cd2c2d7STakashi Sakamoto 		    !amdtp_stream_running(&dice->rx_stream[i]))
4393cd2c2d7STakashi Sakamoto 			break;
4403cd2c2d7STakashi Sakamoto 	}
4413cd2c2d7STakashi Sakamoto 	if (i < MAX_STREAMS) {
4423cd2c2d7STakashi Sakamoto 		// Start both streams.
44320b94544STakashi Sakamoto 		err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
44420b94544STakashi Sakamoto 		if (err < 0)
44520b94544STakashi Sakamoto 			goto error;
4463cd2c2d7STakashi Sakamoto 
44720b94544STakashi Sakamoto 		err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
44820b94544STakashi Sakamoto 		if (err < 0)
44920b94544STakashi Sakamoto 			goto error;
45020b94544STakashi Sakamoto 
45120b94544STakashi Sakamoto 		err = snd_dice_transaction_set_enable(dice);
45220b94544STakashi Sakamoto 		if (err < 0) {
4533cd2c2d7STakashi Sakamoto 			dev_err(&dice->unit->device,
4543cd2c2d7STakashi Sakamoto 				"fail to enable interface\n");
45520b94544STakashi Sakamoto 			goto error;
45620b94544STakashi Sakamoto 		}
45720b94544STakashi Sakamoto 
458e9f21129STakashi Sakamoto 		err = amdtp_domain_start(&dice->domain);
459e9f21129STakashi Sakamoto 		if (err < 0)
460e9f21129STakashi Sakamoto 			goto error;
461e9f21129STakashi Sakamoto 
46220b94544STakashi Sakamoto 		for (i = 0; i < MAX_STREAMS; i++) {
46320b94544STakashi Sakamoto 			if ((i < tx_params.count &&
46420b94544STakashi Sakamoto 			    !amdtp_stream_wait_callback(&dice->tx_stream[i],
46520b94544STakashi Sakamoto 							CALLBACK_TIMEOUT)) ||
46620b94544STakashi Sakamoto 			    (i < rx_params.count &&
46720b94544STakashi Sakamoto 			     !amdtp_stream_wait_callback(&dice->rx_stream[i],
46820b94544STakashi Sakamoto 							 CALLBACK_TIMEOUT))) {
46920b94544STakashi Sakamoto 				err = -ETIMEDOUT;
47020b94544STakashi Sakamoto 				goto error;
47120b94544STakashi Sakamoto 			}
47220b94544STakashi Sakamoto 		}
4733cd2c2d7STakashi Sakamoto 	}
47420b94544STakashi Sakamoto 
47520b94544STakashi Sakamoto 	return 0;
47620b94544STakashi Sakamoto error:
477e9f21129STakashi Sakamoto 	amdtp_domain_stop(&dice->domain);
478b3480638STakashi Sakamoto 	finish_session(dice, &tx_params, &rx_params);
47920b94544STakashi Sakamoto 	return err;
48020b94544STakashi Sakamoto }
48120b94544STakashi Sakamoto 
482436b5abeSTakashi Sakamoto /*
483436b5abeSTakashi Sakamoto  * MEMO: After this function, there're two states of streams:
484436b5abeSTakashi Sakamoto  *  - None streams are running.
485436b5abeSTakashi Sakamoto  *  - All streams are running.
486436b5abeSTakashi Sakamoto  */
4879a02843cSTakashi Sakamoto void snd_dice_stream_stop_duplex(struct snd_dice *dice)
4886eb6c81eSTakashi Sakamoto {
4898cc1a8abSTakashi Sakamoto 	struct reg_params tx_params, rx_params;
490436b5abeSTakashi Sakamoto 
4913cd2c2d7STakashi Sakamoto 	if (dice->substreams_counter == 0) {
492e9f21129STakashi Sakamoto 		if (get_register_params(dice, &tx_params, &rx_params) >= 0) {
493e9f21129STakashi Sakamoto 			amdtp_domain_stop(&dice->domain);
494b3480638STakashi Sakamoto 			finish_session(dice, &tx_params, &rx_params);
495e9f21129STakashi Sakamoto 		}
496740680f2STakashi Sakamoto 
497740680f2STakashi Sakamoto 		release_resources(dice);
4983cd2c2d7STakashi Sakamoto 	}
499436b5abeSTakashi Sakamoto }
5009a02843cSTakashi Sakamoto 
501436b5abeSTakashi Sakamoto static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
502436b5abeSTakashi Sakamoto 		       unsigned int index)
503436b5abeSTakashi Sakamoto {
504436b5abeSTakashi Sakamoto 	struct amdtp_stream *stream;
505436b5abeSTakashi Sakamoto 	struct fw_iso_resources *resources;
506436b5abeSTakashi Sakamoto 	int err;
507436b5abeSTakashi Sakamoto 
508436b5abeSTakashi Sakamoto 	if (dir == AMDTP_IN_STREAM) {
509436b5abeSTakashi Sakamoto 		stream = &dice->tx_stream[index];
510436b5abeSTakashi Sakamoto 		resources = &dice->tx_resources[index];
5119a02843cSTakashi Sakamoto 	} else {
512436b5abeSTakashi Sakamoto 		stream = &dice->rx_stream[index];
513436b5abeSTakashi Sakamoto 		resources = &dice->rx_resources[index];
5149a02843cSTakashi Sakamoto 	}
5159a02843cSTakashi Sakamoto 
5169a02843cSTakashi Sakamoto 	err = fw_iso_resources_init(resources, dice->unit);
5179a02843cSTakashi Sakamoto 	if (err < 0)
5189a02843cSTakashi Sakamoto 		goto end;
5199a02843cSTakashi Sakamoto 	resources->channels_mask = 0x00000000ffffffffuLL;
5209a02843cSTakashi Sakamoto 
5215955815eSTakashi Sakamoto 	err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
5229a02843cSTakashi Sakamoto 	if (err < 0) {
5239a02843cSTakashi Sakamoto 		amdtp_stream_destroy(stream);
5249a02843cSTakashi Sakamoto 		fw_iso_resources_destroy(resources);
5259a02843cSTakashi Sakamoto 	}
5269a02843cSTakashi Sakamoto end:
5279a02843cSTakashi Sakamoto 	return err;
5289a02843cSTakashi Sakamoto }
5299a02843cSTakashi Sakamoto 
530d23c2cc4STakashi Sakamoto /*
531d23c2cc4STakashi Sakamoto  * This function should be called before starting streams or after stopping
532d23c2cc4STakashi Sakamoto  * streams.
533d23c2cc4STakashi Sakamoto  */
534436b5abeSTakashi Sakamoto static void destroy_stream(struct snd_dice *dice,
535436b5abeSTakashi Sakamoto 			   enum amdtp_stream_direction dir,
536436b5abeSTakashi Sakamoto 			   unsigned int index)
5379a02843cSTakashi Sakamoto {
538436b5abeSTakashi Sakamoto 	struct amdtp_stream *stream;
539d23c2cc4STakashi Sakamoto 	struct fw_iso_resources *resources;
5409a02843cSTakashi Sakamoto 
541436b5abeSTakashi Sakamoto 	if (dir == AMDTP_IN_STREAM) {
542436b5abeSTakashi Sakamoto 		stream = &dice->tx_stream[index];
543436b5abeSTakashi Sakamoto 		resources = &dice->tx_resources[index];
544436b5abeSTakashi Sakamoto 	} else {
545436b5abeSTakashi Sakamoto 		stream = &dice->rx_stream[index];
546436b5abeSTakashi Sakamoto 		resources = &dice->rx_resources[index];
547436b5abeSTakashi Sakamoto 	}
548d23c2cc4STakashi Sakamoto 
549d23c2cc4STakashi Sakamoto 	amdtp_stream_destroy(stream);
550d23c2cc4STakashi Sakamoto 	fw_iso_resources_destroy(resources);
5519a02843cSTakashi Sakamoto }
5529a02843cSTakashi Sakamoto 
5539a02843cSTakashi Sakamoto int snd_dice_stream_init_duplex(struct snd_dice *dice)
5546eb6c81eSTakashi Sakamoto {
555436b5abeSTakashi Sakamoto 	int i, err;
5566eb6c81eSTakashi Sakamoto 
557436b5abeSTakashi Sakamoto 	for (i = 0; i < MAX_STREAMS; i++) {
558436b5abeSTakashi Sakamoto 		err = init_stream(dice, AMDTP_IN_STREAM, i);
559436b5abeSTakashi Sakamoto 		if (err < 0) {
560436b5abeSTakashi Sakamoto 			for (; i >= 0; i--)
5610f925660STakashi Sakamoto 				destroy_stream(dice, AMDTP_IN_STREAM, i);
5626eb6c81eSTakashi Sakamoto 			goto end;
563436b5abeSTakashi Sakamoto 		}
564436b5abeSTakashi Sakamoto 	}
5656eb6c81eSTakashi Sakamoto 
566436b5abeSTakashi Sakamoto 	for (i = 0; i < MAX_STREAMS; i++) {
567436b5abeSTakashi Sakamoto 		err = init_stream(dice, AMDTP_OUT_STREAM, i);
568436b5abeSTakashi Sakamoto 		if (err < 0) {
569436b5abeSTakashi Sakamoto 			for (; i >= 0; i--)
570436b5abeSTakashi Sakamoto 				destroy_stream(dice, AMDTP_OUT_STREAM, i);
571436b5abeSTakashi Sakamoto 			for (i = 0; i < MAX_STREAMS; i++)
572436b5abeSTakashi Sakamoto 				destroy_stream(dice, AMDTP_IN_STREAM, i);
573436b5abeSTakashi Sakamoto 			break;
574436b5abeSTakashi Sakamoto 		}
575436b5abeSTakashi Sakamoto 	}
576e9f21129STakashi Sakamoto 
577e9f21129STakashi Sakamoto 	err = amdtp_domain_init(&dice->domain);
578e9f21129STakashi Sakamoto 	if (err < 0) {
579e9f21129STakashi Sakamoto 		for (i = 0; i < MAX_STREAMS; ++i) {
580e9f21129STakashi Sakamoto 			destroy_stream(dice, AMDTP_OUT_STREAM, i);
581e9f21129STakashi Sakamoto 			destroy_stream(dice, AMDTP_IN_STREAM, i);
582e9f21129STakashi Sakamoto 		}
583e9f21129STakashi Sakamoto 	}
5846eb6c81eSTakashi Sakamoto end:
5856eb6c81eSTakashi Sakamoto 	return err;
5866eb6c81eSTakashi Sakamoto }
5876eb6c81eSTakashi Sakamoto 
5889a02843cSTakashi Sakamoto void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
5896eb6c81eSTakashi Sakamoto {
5906b94fb14STakashi Sakamoto 	unsigned int i;
591436b5abeSTakashi Sakamoto 
5926b94fb14STakashi Sakamoto 	for (i = 0; i < MAX_STREAMS; i++) {
5936b94fb14STakashi Sakamoto 		destroy_stream(dice, AMDTP_IN_STREAM, i);
5946b94fb14STakashi Sakamoto 		destroy_stream(dice, AMDTP_OUT_STREAM, i);
595436b5abeSTakashi Sakamoto 	}
596e9f21129STakashi Sakamoto 
597e9f21129STakashi Sakamoto 	amdtp_domain_destroy(&dice->domain);
5986eb6c81eSTakashi Sakamoto }
5996eb6c81eSTakashi Sakamoto 
6009a02843cSTakashi Sakamoto void snd_dice_stream_update_duplex(struct snd_dice *dice)
6016eb6c81eSTakashi Sakamoto {
6028cc1a8abSTakashi Sakamoto 	struct reg_params tx_params, rx_params;
603436b5abeSTakashi Sakamoto 
6046eb6c81eSTakashi Sakamoto 	/*
6056eb6c81eSTakashi Sakamoto 	 * On a bus reset, the DICE firmware disables streaming and then goes
6066eb6c81eSTakashi Sakamoto 	 * off contemplating its own navel for hundreds of milliseconds before
6076eb6c81eSTakashi Sakamoto 	 * it can react to any of our attempts to reenable streaming.  This
6086eb6c81eSTakashi Sakamoto 	 * means that we lose synchronization anyway, so we force our streams
6096eb6c81eSTakashi Sakamoto 	 * to stop so that the application can restart them in an orderly
6106eb6c81eSTakashi Sakamoto 	 * manner.
6116eb6c81eSTakashi Sakamoto 	 */
6126eb6c81eSTakashi Sakamoto 	dice->global_enabled = false;
6136eb6c81eSTakashi Sakamoto 
6148cc1a8abSTakashi Sakamoto 	if (get_register_params(dice, &tx_params, &rx_params) == 0) {
615e9f21129STakashi Sakamoto 		amdtp_domain_stop(&dice->domain);
616e9f21129STakashi Sakamoto 
6178cc1a8abSTakashi Sakamoto 		stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
6188cc1a8abSTakashi Sakamoto 		stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
619436b5abeSTakashi Sakamoto 	}
6206eb6c81eSTakashi Sakamoto }
6216eb6c81eSTakashi Sakamoto 
622b60152f7STakashi Sakamoto int snd_dice_stream_detect_current_formats(struct snd_dice *dice)
623b60152f7STakashi Sakamoto {
624b60152f7STakashi Sakamoto 	unsigned int rate;
625b60152f7STakashi Sakamoto 	enum snd_dice_rate_mode mode;
626b60152f7STakashi Sakamoto 	__be32 reg[2];
627b60152f7STakashi Sakamoto 	struct reg_params tx_params, rx_params;
628b60152f7STakashi Sakamoto 	int i;
629b60152f7STakashi Sakamoto 	int err;
630b60152f7STakashi Sakamoto 
63158579c05STakashi Sakamoto 	/* If extended protocol is available, detect detail spec. */
63258579c05STakashi Sakamoto 	err = snd_dice_detect_extension_formats(dice);
63358579c05STakashi Sakamoto 	if (err >= 0)
63458579c05STakashi Sakamoto 		return err;
63558579c05STakashi Sakamoto 
636b60152f7STakashi Sakamoto 	/*
637b60152f7STakashi Sakamoto 	 * Available stream format is restricted at current mode of sampling
638b60152f7STakashi Sakamoto 	 * clock.
639b60152f7STakashi Sakamoto 	 */
640b60152f7STakashi Sakamoto 	err = snd_dice_transaction_get_rate(dice, &rate);
641b60152f7STakashi Sakamoto 	if (err < 0)
642b60152f7STakashi Sakamoto 		return err;
643b60152f7STakashi Sakamoto 
644b60152f7STakashi Sakamoto 	err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
645b60152f7STakashi Sakamoto 	if (err < 0)
646b60152f7STakashi Sakamoto 		return err;
647b60152f7STakashi Sakamoto 
648b60152f7STakashi Sakamoto 	/*
649b60152f7STakashi Sakamoto 	 * Just after owning the unit (GLOBAL_OWNER), the unit can return
650b60152f7STakashi Sakamoto 	 * invalid stream formats. Selecting clock parameters have an effect
651b60152f7STakashi Sakamoto 	 * for the unit to refine it.
652b60152f7STakashi Sakamoto 	 */
653afa617f2STakashi Sakamoto 	err = ensure_phase_lock(dice, rate);
654b60152f7STakashi Sakamoto 	if (err < 0)
655b60152f7STakashi Sakamoto 		return err;
656b60152f7STakashi Sakamoto 
657b60152f7STakashi Sakamoto 	err = get_register_params(dice, &tx_params, &rx_params);
658b60152f7STakashi Sakamoto 	if (err < 0)
659b60152f7STakashi Sakamoto 		return err;
660b60152f7STakashi Sakamoto 
661b60152f7STakashi Sakamoto 	for (i = 0; i < tx_params.count; ++i) {
662b60152f7STakashi Sakamoto 		err = snd_dice_transaction_read_tx(dice,
663b60152f7STakashi Sakamoto 				tx_params.size * i + TX_NUMBER_AUDIO,
664b60152f7STakashi Sakamoto 				reg, sizeof(reg));
665b60152f7STakashi Sakamoto 		if (err < 0)
666b60152f7STakashi Sakamoto 			return err;
667b60152f7STakashi Sakamoto 		dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
668b60152f7STakashi Sakamoto 		dice->tx_midi_ports[i] = max_t(unsigned int,
669b60152f7STakashi Sakamoto 				be32_to_cpu(reg[1]), dice->tx_midi_ports[i]);
670b60152f7STakashi Sakamoto 	}
671b60152f7STakashi Sakamoto 	for (i = 0; i < rx_params.count; ++i) {
672b60152f7STakashi Sakamoto 		err = snd_dice_transaction_read_rx(dice,
673b60152f7STakashi Sakamoto 				rx_params.size * i + RX_NUMBER_AUDIO,
674b60152f7STakashi Sakamoto 				reg, sizeof(reg));
675b60152f7STakashi Sakamoto 		if (err < 0)
676b60152f7STakashi Sakamoto 			return err;
677b60152f7STakashi Sakamoto 		dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
678b60152f7STakashi Sakamoto 		dice->rx_midi_ports[i] = max_t(unsigned int,
679b60152f7STakashi Sakamoto 				be32_to_cpu(reg[1]), dice->rx_midi_ports[i]);
680b60152f7STakashi Sakamoto 	}
681b60152f7STakashi Sakamoto 
682b60152f7STakashi Sakamoto 	return 0;
683b60152f7STakashi Sakamoto }
684b60152f7STakashi Sakamoto 
6856eb6c81eSTakashi Sakamoto static void dice_lock_changed(struct snd_dice *dice)
6866eb6c81eSTakashi Sakamoto {
6876eb6c81eSTakashi Sakamoto 	dice->dev_lock_changed = true;
6886eb6c81eSTakashi Sakamoto 	wake_up(&dice->hwdep_wait);
6896eb6c81eSTakashi Sakamoto }
6906eb6c81eSTakashi Sakamoto 
6916eb6c81eSTakashi Sakamoto int snd_dice_stream_lock_try(struct snd_dice *dice)
6926eb6c81eSTakashi Sakamoto {
6936eb6c81eSTakashi Sakamoto 	int err;
6946eb6c81eSTakashi Sakamoto 
6956eb6c81eSTakashi Sakamoto 	spin_lock_irq(&dice->lock);
6966eb6c81eSTakashi Sakamoto 
6976eb6c81eSTakashi Sakamoto 	if (dice->dev_lock_count < 0) {
6986eb6c81eSTakashi Sakamoto 		err = -EBUSY;
6996eb6c81eSTakashi Sakamoto 		goto out;
7006eb6c81eSTakashi Sakamoto 	}
7016eb6c81eSTakashi Sakamoto 
7026eb6c81eSTakashi Sakamoto 	if (dice->dev_lock_count++ == 0)
7036eb6c81eSTakashi Sakamoto 		dice_lock_changed(dice);
7046eb6c81eSTakashi Sakamoto 	err = 0;
7056eb6c81eSTakashi Sakamoto out:
7066eb6c81eSTakashi Sakamoto 	spin_unlock_irq(&dice->lock);
7076eb6c81eSTakashi Sakamoto 	return err;
7086eb6c81eSTakashi Sakamoto }
7096eb6c81eSTakashi Sakamoto 
7106eb6c81eSTakashi Sakamoto void snd_dice_stream_lock_release(struct snd_dice *dice)
7116eb6c81eSTakashi Sakamoto {
7126eb6c81eSTakashi Sakamoto 	spin_lock_irq(&dice->lock);
7136eb6c81eSTakashi Sakamoto 
7146eb6c81eSTakashi Sakamoto 	if (WARN_ON(dice->dev_lock_count <= 0))
7156eb6c81eSTakashi Sakamoto 		goto out;
7166eb6c81eSTakashi Sakamoto 
7176eb6c81eSTakashi Sakamoto 	if (--dice->dev_lock_count == 0)
7186eb6c81eSTakashi Sakamoto 		dice_lock_changed(dice);
7196eb6c81eSTakashi Sakamoto out:
7206eb6c81eSTakashi Sakamoto 	spin_unlock_irq(&dice->lock);
7216eb6c81eSTakashi Sakamoto }
722