xref: /OK3568_Linux_fs/kernel/sound/firewire/tascam/tascam-stream.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * tascam-stream.c - a part of driver for TASCAM FireWire series
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2015 Takashi Sakamoto
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/delay.h>
9*4882a593Smuzhiyun #include "tascam.h"
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #define CLOCK_STATUS_MASK      0xffff0000
12*4882a593Smuzhiyun #define CLOCK_CONFIG_MASK      0x0000ffff
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #define CALLBACK_TIMEOUT 500
15*4882a593Smuzhiyun 
get_clock(struct snd_tscm * tscm,u32 * data)16*4882a593Smuzhiyun static int get_clock(struct snd_tscm *tscm, u32 *data)
17*4882a593Smuzhiyun {
18*4882a593Smuzhiyun 	int trial = 0;
19*4882a593Smuzhiyun 	__be32 reg;
20*4882a593Smuzhiyun 	int err;
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun 	while (trial++ < 5) {
23*4882a593Smuzhiyun 		err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
24*4882a593Smuzhiyun 				TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
25*4882a593Smuzhiyun 				&reg, sizeof(reg), 0);
26*4882a593Smuzhiyun 		if (err < 0)
27*4882a593Smuzhiyun 			return err;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 		*data = be32_to_cpu(reg);
30*4882a593Smuzhiyun 		if (*data & CLOCK_STATUS_MASK)
31*4882a593Smuzhiyun 			break;
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 		// In intermediate state after changing clock status.
34*4882a593Smuzhiyun 		msleep(50);
35*4882a593Smuzhiyun 	}
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	// Still in the intermediate state.
38*4882a593Smuzhiyun 	if (trial >= 5)
39*4882a593Smuzhiyun 		return -EAGAIN;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	return 0;
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun 
set_clock(struct snd_tscm * tscm,unsigned int rate,enum snd_tscm_clock clock)44*4882a593Smuzhiyun static int set_clock(struct snd_tscm *tscm, unsigned int rate,
45*4882a593Smuzhiyun 		     enum snd_tscm_clock clock)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun 	u32 data;
48*4882a593Smuzhiyun 	__be32 reg;
49*4882a593Smuzhiyun 	int err;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	err = get_clock(tscm, &data);
52*4882a593Smuzhiyun 	if (err < 0)
53*4882a593Smuzhiyun 		return err;
54*4882a593Smuzhiyun 	data &= CLOCK_CONFIG_MASK;
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	if (rate > 0) {
57*4882a593Smuzhiyun 		data &= 0x000000ff;
58*4882a593Smuzhiyun 		/* Base rate. */
59*4882a593Smuzhiyun 		if ((rate % 44100) == 0) {
60*4882a593Smuzhiyun 			data |= 0x00000100;
61*4882a593Smuzhiyun 			/* Multiplier. */
62*4882a593Smuzhiyun 			if (rate / 44100 == 2)
63*4882a593Smuzhiyun 				data |= 0x00008000;
64*4882a593Smuzhiyun 		} else if ((rate % 48000) == 0) {
65*4882a593Smuzhiyun 			data |= 0x00000200;
66*4882a593Smuzhiyun 			/* Multiplier. */
67*4882a593Smuzhiyun 			if (rate / 48000 == 2)
68*4882a593Smuzhiyun 				data |= 0x00008000;
69*4882a593Smuzhiyun 		} else {
70*4882a593Smuzhiyun 			return -EAGAIN;
71*4882a593Smuzhiyun 		}
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	if (clock != INT_MAX) {
75*4882a593Smuzhiyun 		data &= 0x0000ff00;
76*4882a593Smuzhiyun 		data |= clock + 1;
77*4882a593Smuzhiyun 	}
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	reg = cpu_to_be32(data);
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
82*4882a593Smuzhiyun 				 TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
83*4882a593Smuzhiyun 				 &reg, sizeof(reg), 0);
84*4882a593Smuzhiyun 	if (err < 0)
85*4882a593Smuzhiyun 		return err;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	if (data & 0x00008000)
88*4882a593Smuzhiyun 		reg = cpu_to_be32(0x0000001a);
89*4882a593Smuzhiyun 	else
90*4882a593Smuzhiyun 		reg = cpu_to_be32(0x0000000d);
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
93*4882a593Smuzhiyun 				  TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
94*4882a593Smuzhiyun 				  &reg, sizeof(reg), 0);
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun 
snd_tscm_stream_get_rate(struct snd_tscm * tscm,unsigned int * rate)97*4882a593Smuzhiyun int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	u32 data;
100*4882a593Smuzhiyun 	int err;
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	err = get_clock(tscm, &data);
103*4882a593Smuzhiyun 	if (err < 0)
104*4882a593Smuzhiyun 		return err;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	data = (data & 0xff000000) >> 24;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	/* Check base rate. */
109*4882a593Smuzhiyun 	if ((data & 0x0f) == 0x01)
110*4882a593Smuzhiyun 		*rate = 44100;
111*4882a593Smuzhiyun 	else if ((data & 0x0f) == 0x02)
112*4882a593Smuzhiyun 		*rate = 48000;
113*4882a593Smuzhiyun 	else
114*4882a593Smuzhiyun 		return -EAGAIN;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	/* Check multiplier. */
117*4882a593Smuzhiyun 	if ((data & 0xf0) == 0x80)
118*4882a593Smuzhiyun 		*rate *= 2;
119*4882a593Smuzhiyun 	else if ((data & 0xf0) != 0x00)
120*4882a593Smuzhiyun 		return -EAGAIN;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	return err;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
snd_tscm_stream_get_clock(struct snd_tscm * tscm,enum snd_tscm_clock * clock)125*4882a593Smuzhiyun int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	u32 data;
128*4882a593Smuzhiyun 	int err;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	err = get_clock(tscm, &data);
131*4882a593Smuzhiyun 	if (err < 0)
132*4882a593Smuzhiyun 		return err;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	*clock = ((data & 0x00ff0000) >> 16) - 1;
135*4882a593Smuzhiyun 	if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
136*4882a593Smuzhiyun 		return -EIO;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun 
enable_data_channels(struct snd_tscm * tscm)141*4882a593Smuzhiyun static int enable_data_channels(struct snd_tscm *tscm)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun 	__be32 reg;
144*4882a593Smuzhiyun 	u32 data;
145*4882a593Smuzhiyun 	unsigned int i;
146*4882a593Smuzhiyun 	int err;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	data = 0;
149*4882a593Smuzhiyun 	for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
150*4882a593Smuzhiyun 		data |= BIT(i);
151*4882a593Smuzhiyun 	if (tscm->spec->has_adat)
152*4882a593Smuzhiyun 		data |= 0x0000ff00;
153*4882a593Smuzhiyun 	if (tscm->spec->has_spdif)
154*4882a593Smuzhiyun 		data |= 0x00030000;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	reg = cpu_to_be32(data);
157*4882a593Smuzhiyun 	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
158*4882a593Smuzhiyun 				 TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
159*4882a593Smuzhiyun 				 &reg, sizeof(reg), 0);
160*4882a593Smuzhiyun 	if (err < 0)
161*4882a593Smuzhiyun 		return err;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	data = 0;
164*4882a593Smuzhiyun 	for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
165*4882a593Smuzhiyun 		data |= BIT(i);
166*4882a593Smuzhiyun 	if (tscm->spec->has_adat)
167*4882a593Smuzhiyun 		data |= 0x0000ff00;
168*4882a593Smuzhiyun 	if (tscm->spec->has_spdif)
169*4882a593Smuzhiyun 		data |= 0x00030000;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	reg = cpu_to_be32(data);
172*4882a593Smuzhiyun 	return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
173*4882a593Smuzhiyun 				  TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
174*4882a593Smuzhiyun 				  &reg, sizeof(reg), 0);
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun 
set_stream_formats(struct snd_tscm * tscm,unsigned int rate)177*4882a593Smuzhiyun static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun 	__be32 reg;
180*4882a593Smuzhiyun 	int err;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	// Set an option for unknown purpose.
183*4882a593Smuzhiyun 	reg = cpu_to_be32(0x00200000);
184*4882a593Smuzhiyun 	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
185*4882a593Smuzhiyun 				 TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
186*4882a593Smuzhiyun 				 &reg, sizeof(reg), 0);
187*4882a593Smuzhiyun 	if (err < 0)
188*4882a593Smuzhiyun 		return err;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	return enable_data_channels(tscm);
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun 
finish_session(struct snd_tscm * tscm)193*4882a593Smuzhiyun static void finish_session(struct snd_tscm *tscm)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun 	__be32 reg;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	reg = 0;
198*4882a593Smuzhiyun 	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
199*4882a593Smuzhiyun 			   TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
200*4882a593Smuzhiyun 			   &reg, sizeof(reg), 0);
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	reg = 0;
203*4882a593Smuzhiyun 	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
204*4882a593Smuzhiyun 			   TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
205*4882a593Smuzhiyun 			   &reg, sizeof(reg), 0);
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	// Unregister channels.
208*4882a593Smuzhiyun 	reg = cpu_to_be32(0x00000000);
209*4882a593Smuzhiyun 	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
210*4882a593Smuzhiyun 			   TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
211*4882a593Smuzhiyun 			   &reg, sizeof(reg), 0);
212*4882a593Smuzhiyun 	reg = cpu_to_be32(0x00000000);
213*4882a593Smuzhiyun 	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
214*4882a593Smuzhiyun 			   TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
215*4882a593Smuzhiyun 			   &reg, sizeof(reg), 0);
216*4882a593Smuzhiyun 	reg = cpu_to_be32(0x00000000);
217*4882a593Smuzhiyun 	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
218*4882a593Smuzhiyun 			   TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
219*4882a593Smuzhiyun 			   &reg, sizeof(reg), 0);
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun 
begin_session(struct snd_tscm * tscm)222*4882a593Smuzhiyun static int begin_session(struct snd_tscm *tscm)
223*4882a593Smuzhiyun {
224*4882a593Smuzhiyun 	__be32 reg;
225*4882a593Smuzhiyun 	int err;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	// Register the isochronous channel for transmitting stream.
228*4882a593Smuzhiyun 	reg = cpu_to_be32(tscm->tx_resources.channel);
229*4882a593Smuzhiyun 	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
230*4882a593Smuzhiyun 				 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
231*4882a593Smuzhiyun 				 &reg, sizeof(reg), 0);
232*4882a593Smuzhiyun 	if (err < 0)
233*4882a593Smuzhiyun 		return err;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	// Unknown.
236*4882a593Smuzhiyun 	reg = cpu_to_be32(0x00000002);
237*4882a593Smuzhiyun 	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
238*4882a593Smuzhiyun 				 TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
239*4882a593Smuzhiyun 				 &reg, sizeof(reg), 0);
240*4882a593Smuzhiyun 	if (err < 0)
241*4882a593Smuzhiyun 		return err;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	// Register the isochronous channel for receiving stream.
244*4882a593Smuzhiyun 	reg = cpu_to_be32(tscm->rx_resources.channel);
245*4882a593Smuzhiyun 	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
246*4882a593Smuzhiyun 				 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
247*4882a593Smuzhiyun 				 &reg, sizeof(reg), 0);
248*4882a593Smuzhiyun 	if (err < 0)
249*4882a593Smuzhiyun 		return err;
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	reg = cpu_to_be32(0x00000001);
252*4882a593Smuzhiyun 	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
253*4882a593Smuzhiyun 				 TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
254*4882a593Smuzhiyun 				 &reg, sizeof(reg), 0);
255*4882a593Smuzhiyun 	if (err < 0)
256*4882a593Smuzhiyun 		return err;
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	reg = cpu_to_be32(0x00000001);
259*4882a593Smuzhiyun 	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
260*4882a593Smuzhiyun 				 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
261*4882a593Smuzhiyun 				 &reg, sizeof(reg), 0);
262*4882a593Smuzhiyun 	if (err < 0)
263*4882a593Smuzhiyun 		return err;
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	// Set an option for unknown purpose.
266*4882a593Smuzhiyun 	reg = cpu_to_be32(0x00002000);
267*4882a593Smuzhiyun 	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
268*4882a593Smuzhiyun 				 TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
269*4882a593Smuzhiyun 				 &reg, sizeof(reg), 0);
270*4882a593Smuzhiyun 	if (err < 0)
271*4882a593Smuzhiyun 		return err;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	// Start multiplexing PCM samples on packets.
274*4882a593Smuzhiyun 	reg = cpu_to_be32(0x00000001);
275*4882a593Smuzhiyun 	return snd_fw_transaction(tscm->unit,
276*4882a593Smuzhiyun 				  TCODE_WRITE_QUADLET_REQUEST,
277*4882a593Smuzhiyun 				  TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
278*4882a593Smuzhiyun 				  &reg, sizeof(reg), 0);
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
keep_resources(struct snd_tscm * tscm,unsigned int rate,struct amdtp_stream * stream)281*4882a593Smuzhiyun static int keep_resources(struct snd_tscm *tscm, unsigned int rate,
282*4882a593Smuzhiyun 			  struct amdtp_stream *stream)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun 	struct fw_iso_resources *resources;
285*4882a593Smuzhiyun 	int err;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	if (stream == &tscm->tx_stream)
288*4882a593Smuzhiyun 		resources = &tscm->tx_resources;
289*4882a593Smuzhiyun 	else
290*4882a593Smuzhiyun 		resources = &tscm->rx_resources;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	err = amdtp_tscm_set_parameters(stream, rate);
293*4882a593Smuzhiyun 	if (err < 0)
294*4882a593Smuzhiyun 		return err;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	return fw_iso_resources_allocate(resources,
297*4882a593Smuzhiyun 				amdtp_stream_get_max_payload(stream),
298*4882a593Smuzhiyun 				fw_parent_device(tscm->unit)->max_speed);
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun 
init_stream(struct snd_tscm * tscm,struct amdtp_stream * s)301*4882a593Smuzhiyun static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun 	struct fw_iso_resources *resources;
304*4882a593Smuzhiyun 	enum amdtp_stream_direction dir;
305*4882a593Smuzhiyun 	unsigned int pcm_channels;
306*4882a593Smuzhiyun 	int err;
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	if (s == &tscm->tx_stream) {
309*4882a593Smuzhiyun 		resources = &tscm->tx_resources;
310*4882a593Smuzhiyun 		dir = AMDTP_IN_STREAM;
311*4882a593Smuzhiyun 		pcm_channels = tscm->spec->pcm_capture_analog_channels;
312*4882a593Smuzhiyun 	} else {
313*4882a593Smuzhiyun 		resources = &tscm->rx_resources;
314*4882a593Smuzhiyun 		dir = AMDTP_OUT_STREAM;
315*4882a593Smuzhiyun 		pcm_channels = tscm->spec->pcm_playback_analog_channels;
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	if (tscm->spec->has_adat)
319*4882a593Smuzhiyun 		pcm_channels += 8;
320*4882a593Smuzhiyun 	if (tscm->spec->has_spdif)
321*4882a593Smuzhiyun 		pcm_channels += 2;
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	err = fw_iso_resources_init(resources, tscm->unit);
324*4882a593Smuzhiyun 	if (err < 0)
325*4882a593Smuzhiyun 		return err;
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels);
328*4882a593Smuzhiyun 	if (err < 0)
329*4882a593Smuzhiyun 		fw_iso_resources_free(resources);
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	return err;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun 
destroy_stream(struct snd_tscm * tscm,struct amdtp_stream * s)334*4882a593Smuzhiyun static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
335*4882a593Smuzhiyun {
336*4882a593Smuzhiyun 	amdtp_stream_destroy(s);
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	if (s == &tscm->tx_stream)
339*4882a593Smuzhiyun 		fw_iso_resources_destroy(&tscm->tx_resources);
340*4882a593Smuzhiyun 	else
341*4882a593Smuzhiyun 		fw_iso_resources_destroy(&tscm->rx_resources);
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun 
snd_tscm_stream_init_duplex(struct snd_tscm * tscm)344*4882a593Smuzhiyun int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
345*4882a593Smuzhiyun {
346*4882a593Smuzhiyun 	int err;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	err = init_stream(tscm, &tscm->tx_stream);
349*4882a593Smuzhiyun 	if (err < 0)
350*4882a593Smuzhiyun 		return err;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	err = init_stream(tscm, &tscm->rx_stream);
353*4882a593Smuzhiyun 	if (err < 0) {
354*4882a593Smuzhiyun 		destroy_stream(tscm, &tscm->tx_stream);
355*4882a593Smuzhiyun 		return err;
356*4882a593Smuzhiyun 	}
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	err = amdtp_domain_init(&tscm->domain);
359*4882a593Smuzhiyun 	if (err < 0) {
360*4882a593Smuzhiyun 		destroy_stream(tscm, &tscm->tx_stream);
361*4882a593Smuzhiyun 		destroy_stream(tscm, &tscm->rx_stream);
362*4882a593Smuzhiyun 	}
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	return err;
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun // At bus reset, streaming is stopped and some registers are clear.
snd_tscm_stream_update_duplex(struct snd_tscm * tscm)368*4882a593Smuzhiyun void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
369*4882a593Smuzhiyun {
370*4882a593Smuzhiyun 	amdtp_domain_stop(&tscm->domain);
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	amdtp_stream_pcm_abort(&tscm->tx_stream);
373*4882a593Smuzhiyun 	amdtp_stream_pcm_abort(&tscm->rx_stream);
374*4882a593Smuzhiyun }
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun // This function should be called before starting streams or after stopping
377*4882a593Smuzhiyun // streams.
snd_tscm_stream_destroy_duplex(struct snd_tscm * tscm)378*4882a593Smuzhiyun void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
379*4882a593Smuzhiyun {
380*4882a593Smuzhiyun 	amdtp_domain_destroy(&tscm->domain);
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	destroy_stream(tscm, &tscm->rx_stream);
383*4882a593Smuzhiyun 	destroy_stream(tscm, &tscm->tx_stream);
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun 
snd_tscm_stream_reserve_duplex(struct snd_tscm * tscm,unsigned int rate,unsigned int frames_per_period,unsigned int frames_per_buffer)386*4882a593Smuzhiyun int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate,
387*4882a593Smuzhiyun 				   unsigned int frames_per_period,
388*4882a593Smuzhiyun 				   unsigned int frames_per_buffer)
389*4882a593Smuzhiyun {
390*4882a593Smuzhiyun 	unsigned int curr_rate;
391*4882a593Smuzhiyun 	int err;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	err = snd_tscm_stream_get_rate(tscm, &curr_rate);
394*4882a593Smuzhiyun 	if (err < 0)
395*4882a593Smuzhiyun 		return err;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	if (tscm->substreams_counter == 0 || rate != curr_rate) {
398*4882a593Smuzhiyun 		amdtp_domain_stop(&tscm->domain);
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 		finish_session(tscm);
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 		fw_iso_resources_free(&tscm->tx_resources);
403*4882a593Smuzhiyun 		fw_iso_resources_free(&tscm->rx_resources);
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 		err = set_clock(tscm, rate, INT_MAX);
406*4882a593Smuzhiyun 		if (err < 0)
407*4882a593Smuzhiyun 			return err;
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 		err = keep_resources(tscm, rate, &tscm->tx_stream);
410*4882a593Smuzhiyun 		if (err < 0)
411*4882a593Smuzhiyun 			return err;
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 		err = keep_resources(tscm, rate, &tscm->rx_stream);
414*4882a593Smuzhiyun 		if (err < 0) {
415*4882a593Smuzhiyun 			fw_iso_resources_free(&tscm->tx_resources);
416*4882a593Smuzhiyun 			return err;
417*4882a593Smuzhiyun 		}
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 		err = amdtp_domain_set_events_per_period(&tscm->domain,
420*4882a593Smuzhiyun 					frames_per_period, frames_per_buffer);
421*4882a593Smuzhiyun 		if (err < 0) {
422*4882a593Smuzhiyun 			fw_iso_resources_free(&tscm->tx_resources);
423*4882a593Smuzhiyun 			fw_iso_resources_free(&tscm->rx_resources);
424*4882a593Smuzhiyun 			return err;
425*4882a593Smuzhiyun 		}
426*4882a593Smuzhiyun 	}
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	return 0;
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun 
snd_tscm_stream_start_duplex(struct snd_tscm * tscm,unsigned int rate)431*4882a593Smuzhiyun int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
432*4882a593Smuzhiyun {
433*4882a593Smuzhiyun 	unsigned int generation = tscm->rx_resources.generation;
434*4882a593Smuzhiyun 	int err;
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 	if (tscm->substreams_counter == 0)
437*4882a593Smuzhiyun 		return 0;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	if (amdtp_streaming_error(&tscm->rx_stream) ||
440*4882a593Smuzhiyun 	    amdtp_streaming_error(&tscm->tx_stream)) {
441*4882a593Smuzhiyun 		amdtp_domain_stop(&tscm->domain);
442*4882a593Smuzhiyun 		finish_session(tscm);
443*4882a593Smuzhiyun 	}
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	if (generation != fw_parent_device(tscm->unit)->card->generation) {
446*4882a593Smuzhiyun 		err = fw_iso_resources_update(&tscm->tx_resources);
447*4882a593Smuzhiyun 		if (err < 0)
448*4882a593Smuzhiyun 			goto error;
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 		err = fw_iso_resources_update(&tscm->rx_resources);
451*4882a593Smuzhiyun 		if (err < 0)
452*4882a593Smuzhiyun 			goto error;
453*4882a593Smuzhiyun 	}
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun 	if (!amdtp_stream_running(&tscm->rx_stream)) {
456*4882a593Smuzhiyun 		int spd = fw_parent_device(tscm->unit)->max_speed;
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 		err = set_stream_formats(tscm, rate);
459*4882a593Smuzhiyun 		if (err < 0)
460*4882a593Smuzhiyun 			goto error;
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 		err = begin_session(tscm);
463*4882a593Smuzhiyun 		if (err < 0)
464*4882a593Smuzhiyun 			goto error;
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 		err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream,
467*4882a593Smuzhiyun 					      tscm->rx_resources.channel, spd);
468*4882a593Smuzhiyun 		if (err < 0)
469*4882a593Smuzhiyun 			goto error;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 		err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream,
472*4882a593Smuzhiyun 					      tscm->tx_resources.channel, spd);
473*4882a593Smuzhiyun 		if (err < 0)
474*4882a593Smuzhiyun 			goto error;
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 		err = amdtp_domain_start(&tscm->domain, 0);
477*4882a593Smuzhiyun 		if (err < 0)
478*4882a593Smuzhiyun 			return err;
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 		if (!amdtp_stream_wait_callback(&tscm->rx_stream,
481*4882a593Smuzhiyun 						CALLBACK_TIMEOUT) ||
482*4882a593Smuzhiyun 		    !amdtp_stream_wait_callback(&tscm->tx_stream,
483*4882a593Smuzhiyun 						CALLBACK_TIMEOUT)) {
484*4882a593Smuzhiyun 			err = -ETIMEDOUT;
485*4882a593Smuzhiyun 			goto error;
486*4882a593Smuzhiyun 		}
487*4882a593Smuzhiyun 	}
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	return 0;
490*4882a593Smuzhiyun error:
491*4882a593Smuzhiyun 	amdtp_domain_stop(&tscm->domain);
492*4882a593Smuzhiyun 	finish_session(tscm);
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	return err;
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun 
snd_tscm_stream_stop_duplex(struct snd_tscm * tscm)497*4882a593Smuzhiyun void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
498*4882a593Smuzhiyun {
499*4882a593Smuzhiyun 	if (tscm->substreams_counter == 0) {
500*4882a593Smuzhiyun 		amdtp_domain_stop(&tscm->domain);
501*4882a593Smuzhiyun 		finish_session(tscm);
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 		fw_iso_resources_free(&tscm->tx_resources);
504*4882a593Smuzhiyun 		fw_iso_resources_free(&tscm->rx_resources);
505*4882a593Smuzhiyun 	}
506*4882a593Smuzhiyun }
507*4882a593Smuzhiyun 
snd_tscm_stream_lock_changed(struct snd_tscm * tscm)508*4882a593Smuzhiyun void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
509*4882a593Smuzhiyun {
510*4882a593Smuzhiyun 	tscm->dev_lock_changed = true;
511*4882a593Smuzhiyun 	wake_up(&tscm->hwdep_wait);
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun 
snd_tscm_stream_lock_try(struct snd_tscm * tscm)514*4882a593Smuzhiyun int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
515*4882a593Smuzhiyun {
516*4882a593Smuzhiyun 	int err;
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 	spin_lock_irq(&tscm->lock);
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	/* user land lock this */
521*4882a593Smuzhiyun 	if (tscm->dev_lock_count < 0) {
522*4882a593Smuzhiyun 		err = -EBUSY;
523*4882a593Smuzhiyun 		goto end;
524*4882a593Smuzhiyun 	}
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	/* this is the first time */
527*4882a593Smuzhiyun 	if (tscm->dev_lock_count++ == 0)
528*4882a593Smuzhiyun 		snd_tscm_stream_lock_changed(tscm);
529*4882a593Smuzhiyun 	err = 0;
530*4882a593Smuzhiyun end:
531*4882a593Smuzhiyun 	spin_unlock_irq(&tscm->lock);
532*4882a593Smuzhiyun 	return err;
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun 
snd_tscm_stream_lock_release(struct snd_tscm * tscm)535*4882a593Smuzhiyun void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
536*4882a593Smuzhiyun {
537*4882a593Smuzhiyun 	spin_lock_irq(&tscm->lock);
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	if (WARN_ON(tscm->dev_lock_count <= 0))
540*4882a593Smuzhiyun 		goto end;
541*4882a593Smuzhiyun 	if (--tscm->dev_lock_count == 0)
542*4882a593Smuzhiyun 		snd_tscm_stream_lock_changed(tscm);
543*4882a593Smuzhiyun end:
544*4882a593Smuzhiyun 	spin_unlock_irq(&tscm->lock);
545*4882a593Smuzhiyun }
546