1 /*
2  * ff-stream.c - a part of driver for RME Fireface series
3  *
4  * Copyright (c) 2015-2017 Takashi Sakamoto
5  *
6  * Licensed under the terms of the GNU General Public License, version 2.
7  */
8 
9 #include "ff.h"
10 
11 #define CALLBACK_TIMEOUT_MS	200
12 
13 static int get_rate_mode(unsigned int rate, unsigned int *mode)
14 {
15 	int i;
16 
17 	for (i = 0; i < CIP_SFC_COUNT; i++) {
18 		if (amdtp_rate_table[i] == rate)
19 			break;
20 	}
21 
22 	if (i == CIP_SFC_COUNT)
23 		return -EINVAL;
24 
25 	*mode = ((int)i - 1) / 2;
26 
27 	return 0;
28 }
29 
30 /*
31  * Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
32  * we can allocate between 0 and 7 channel.
33  */
34 static int keep_resources(struct snd_ff *ff, unsigned int rate)
35 {
36 	int mode;
37 	int err;
38 
39 	err = get_rate_mode(rate, &mode);
40 	if (err < 0)
41 		return err;
42 
43 	/* Keep resources for in-stream. */
44 	err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
45 				      ff->spec->pcm_capture_channels[mode]);
46 	if (err < 0)
47 		return err;
48 	ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
49 	err = fw_iso_resources_allocate(&ff->tx_resources,
50 			amdtp_stream_get_max_payload(&ff->tx_stream),
51 			fw_parent_device(ff->unit)->max_speed);
52 	if (err < 0)
53 		return err;
54 
55 	/* Keep resources for out-stream. */
56 	err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
57 				      ff->spec->pcm_playback_channels[mode]);
58 	if (err < 0)
59 		return err;
60 	ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
61 	err = fw_iso_resources_allocate(&ff->rx_resources,
62 			amdtp_stream_get_max_payload(&ff->rx_stream),
63 			fw_parent_device(ff->unit)->max_speed);
64 	if (err < 0)
65 		fw_iso_resources_free(&ff->tx_resources);
66 
67 	return err;
68 }
69 
70 static void release_resources(struct snd_ff *ff)
71 {
72 	fw_iso_resources_free(&ff->tx_resources);
73 	fw_iso_resources_free(&ff->rx_resources);
74 }
75 
76 static inline void finish_session(struct snd_ff *ff)
77 {
78 	ff->spec->protocol->finish_session(ff);
79 	ff->spec->protocol->switch_fetching_mode(ff, false);
80 }
81 
82 static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
83 {
84 	int err;
85 	struct fw_iso_resources *resources;
86 	struct amdtp_stream *stream;
87 
88 	if (dir == AMDTP_IN_STREAM) {
89 		resources = &ff->tx_resources;
90 		stream = &ff->tx_stream;
91 	} else {
92 		resources = &ff->rx_resources;
93 		stream = &ff->rx_stream;
94 	}
95 
96 	err = fw_iso_resources_init(resources, ff->unit);
97 	if (err < 0)
98 		return err;
99 
100 	err = amdtp_ff_init(stream, ff->unit, dir);
101 	if (err < 0)
102 		fw_iso_resources_destroy(resources);
103 
104 	return err;
105 }
106 
107 static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
108 {
109 	if (dir == AMDTP_IN_STREAM) {
110 		amdtp_stream_destroy(&ff->tx_stream);
111 		fw_iso_resources_destroy(&ff->tx_resources);
112 	} else {
113 		amdtp_stream_destroy(&ff->rx_stream);
114 		fw_iso_resources_destroy(&ff->rx_resources);
115 	}
116 }
117 
118 int snd_ff_stream_init_duplex(struct snd_ff *ff)
119 {
120 	int err;
121 
122 	err = init_stream(ff, AMDTP_OUT_STREAM);
123 	if (err < 0)
124 		goto end;
125 
126 	err = init_stream(ff, AMDTP_IN_STREAM);
127 	if (err < 0)
128 		destroy_stream(ff, AMDTP_OUT_STREAM);
129 end:
130 	return err;
131 }
132 
133 /*
134  * This function should be called before starting streams or after stopping
135  * streams.
136  */
137 void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
138 {
139 	destroy_stream(ff, AMDTP_IN_STREAM);
140 	destroy_stream(ff, AMDTP_OUT_STREAM);
141 }
142 
143 int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
144 {
145 	unsigned int curr_rate;
146 	enum snd_ff_clock_src src;
147 	int err;
148 
149 	if (ff->substreams_counter == 0)
150 		return 0;
151 
152 	err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
153 	if (err < 0)
154 		return err;
155 	if (curr_rate != rate ||
156 	    amdtp_streaming_error(&ff->tx_stream) ||
157 	    amdtp_streaming_error(&ff->rx_stream)) {
158 		finish_session(ff);
159 
160 		amdtp_stream_stop(&ff->tx_stream);
161 		amdtp_stream_stop(&ff->rx_stream);
162 
163 		release_resources(ff);
164 	}
165 
166 	/*
167 	 * Regardless of current source of clock signal, drivers transfer some
168 	 * packets. Then, the device transfers packets.
169 	 */
170 	if (!amdtp_stream_running(&ff->rx_stream)) {
171 		err = keep_resources(ff, rate);
172 		if (err < 0)
173 			goto error;
174 
175 		err = ff->spec->protocol->begin_session(ff, rate);
176 		if (err < 0)
177 			goto error;
178 
179 		err = amdtp_stream_start(&ff->rx_stream,
180 					 ff->rx_resources.channel,
181 					 fw_parent_device(ff->unit)->max_speed);
182 		if (err < 0)
183 			goto error;
184 
185 		if (!amdtp_stream_wait_callback(&ff->rx_stream,
186 						CALLBACK_TIMEOUT_MS)) {
187 			err = -ETIMEDOUT;
188 			goto error;
189 		}
190 
191 		err = ff->spec->protocol->switch_fetching_mode(ff, true);
192 		if (err < 0)
193 			goto error;
194 	}
195 
196 	if (!amdtp_stream_running(&ff->tx_stream)) {
197 		err = amdtp_stream_start(&ff->tx_stream,
198 					 ff->tx_resources.channel,
199 					 fw_parent_device(ff->unit)->max_speed);
200 		if (err < 0)
201 			goto error;
202 
203 		if (!amdtp_stream_wait_callback(&ff->tx_stream,
204 						CALLBACK_TIMEOUT_MS)) {
205 			err = -ETIMEDOUT;
206 			goto error;
207 		}
208 	}
209 
210 	return 0;
211 error:
212 	amdtp_stream_stop(&ff->tx_stream);
213 	amdtp_stream_stop(&ff->rx_stream);
214 
215 	finish_session(ff);
216 	release_resources(ff);
217 
218 	return err;
219 }
220 
221 void snd_ff_stream_stop_duplex(struct snd_ff *ff)
222 {
223 	if (ff->substreams_counter > 0)
224 		return;
225 
226 	amdtp_stream_stop(&ff->tx_stream);
227 	amdtp_stream_stop(&ff->rx_stream);
228 	finish_session(ff);
229 	release_resources(ff);
230 }
231 
232 void snd_ff_stream_update_duplex(struct snd_ff *ff)
233 {
234 	/* The device discontinue to transfer packets.  */
235 	amdtp_stream_pcm_abort(&ff->tx_stream);
236 	amdtp_stream_stop(&ff->tx_stream);
237 
238 	amdtp_stream_pcm_abort(&ff->rx_stream);
239 	amdtp_stream_stop(&ff->rx_stream);
240 
241 	fw_iso_resources_update(&ff->tx_resources);
242 	fw_iso_resources_update(&ff->rx_resources);
243 }
244 
245 void snd_ff_stream_lock_changed(struct snd_ff *ff)
246 {
247 	ff->dev_lock_changed = true;
248 	wake_up(&ff->hwdep_wait);
249 }
250 
251 int snd_ff_stream_lock_try(struct snd_ff *ff)
252 {
253 	int err;
254 
255 	spin_lock_irq(&ff->lock);
256 
257 	/* user land lock this */
258 	if (ff->dev_lock_count < 0) {
259 		err = -EBUSY;
260 		goto end;
261 	}
262 
263 	/* this is the first time */
264 	if (ff->dev_lock_count++ == 0)
265 		snd_ff_stream_lock_changed(ff);
266 	err = 0;
267 end:
268 	spin_unlock_irq(&ff->lock);
269 	return err;
270 }
271 
272 void snd_ff_stream_lock_release(struct snd_ff *ff)
273 {
274 	spin_lock_irq(&ff->lock);
275 
276 	if (WARN_ON(ff->dev_lock_count <= 0))
277 		goto end;
278 	if (--ff->dev_lock_count == 0)
279 		snd_ff_stream_lock_changed(ff);
280 end:
281 	spin_unlock_irq(&ff->lock);
282 }
283