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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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