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 int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
14 				      enum snd_ff_stream_mode *mode)
15 {
16 	static const enum snd_ff_stream_mode modes[] = {
17 		[CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW,
18 		[CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW,
19 		[CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW,
20 		[CIP_SFC_88200] = SND_FF_STREAM_MODE_MID,
21 		[CIP_SFC_96000] = SND_FF_STREAM_MODE_MID,
22 		[CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH,
23 		[CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH,
24 	};
25 
26 	if (sfc >= CIP_SFC_COUNT)
27 		return -EINVAL;
28 
29 	*mode = modes[sfc];
30 
31 	return 0;
32 }
33 
34 static inline void finish_session(struct snd_ff *ff)
35 {
36 	ff->spec->protocol->finish_session(ff);
37 	ff->spec->protocol->switch_fetching_mode(ff, false);
38 }
39 
40 static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
41 {
42 	int err;
43 	struct fw_iso_resources *resources;
44 	struct amdtp_stream *stream;
45 
46 	if (dir == AMDTP_IN_STREAM) {
47 		resources = &ff->tx_resources;
48 		stream = &ff->tx_stream;
49 	} else {
50 		resources = &ff->rx_resources;
51 		stream = &ff->rx_stream;
52 	}
53 
54 	err = fw_iso_resources_init(resources, ff->unit);
55 	if (err < 0)
56 		return err;
57 
58 	err = amdtp_ff_init(stream, ff->unit, dir);
59 	if (err < 0)
60 		fw_iso_resources_destroy(resources);
61 
62 	return err;
63 }
64 
65 static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
66 {
67 	if (dir == AMDTP_IN_STREAM) {
68 		amdtp_stream_destroy(&ff->tx_stream);
69 		fw_iso_resources_destroy(&ff->tx_resources);
70 	} else {
71 		amdtp_stream_destroy(&ff->rx_stream);
72 		fw_iso_resources_destroy(&ff->rx_resources);
73 	}
74 }
75 
76 int snd_ff_stream_init_duplex(struct snd_ff *ff)
77 {
78 	int err;
79 
80 	err = init_stream(ff, AMDTP_OUT_STREAM);
81 	if (err < 0)
82 		goto end;
83 
84 	err = init_stream(ff, AMDTP_IN_STREAM);
85 	if (err < 0)
86 		destroy_stream(ff, AMDTP_OUT_STREAM);
87 end:
88 	return err;
89 }
90 
91 /*
92  * This function should be called before starting streams or after stopping
93  * streams.
94  */
95 void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
96 {
97 	destroy_stream(ff, AMDTP_IN_STREAM);
98 	destroy_stream(ff, AMDTP_OUT_STREAM);
99 }
100 
101 int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
102 {
103 	unsigned int curr_rate;
104 	enum snd_ff_clock_src src;
105 	int err;
106 
107 	err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
108 	if (err < 0)
109 		return err;
110 
111 	if (ff->substreams_counter == 0 || curr_rate != rate) {
112 		enum snd_ff_stream_mode mode;
113 		int i;
114 
115 		amdtp_stream_stop(&ff->tx_stream);
116 		amdtp_stream_stop(&ff->rx_stream);
117 
118 		finish_session(ff);
119 
120 		fw_iso_resources_free(&ff->tx_resources);
121 		fw_iso_resources_free(&ff->rx_resources);
122 
123 		for (i = 0; i < CIP_SFC_COUNT; ++i) {
124 			if (amdtp_rate_table[i] == rate)
125 				break;
126 		}
127 		if (i >= CIP_SFC_COUNT)
128 			return -EINVAL;
129 
130 		err = snd_ff_stream_get_multiplier_mode(i, &mode);
131 		if (err < 0)
132 			return err;
133 
134 		err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
135 					ff->spec->pcm_capture_channels[mode]);
136 		if (err < 0)
137 			return err;
138 
139 		err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
140 					ff->spec->pcm_playback_channels[mode]);
141 		if (err < 0)
142 			return err;
143 
144 		err = ff->spec->protocol->allocate_resources(ff, rate);
145 		if (err < 0)
146 			return err;
147 	}
148 
149 	return 0;
150 }
151 
152 void snd_ff_stream_release_duplex(struct snd_ff *ff)
153 {
154 	if (ff->substreams_counter == 0) {
155 		fw_iso_resources_free(&ff->tx_resources);
156 		fw_iso_resources_free(&ff->rx_resources);
157 	}
158 }
159 
160 int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
161 {
162 	int err;
163 
164 	if (ff->substreams_counter == 0)
165 		return 0;
166 
167 	if (amdtp_streaming_error(&ff->tx_stream) ||
168 	    amdtp_streaming_error(&ff->rx_stream)) {
169 		amdtp_stream_stop(&ff->tx_stream);
170 		amdtp_stream_stop(&ff->rx_stream);
171 
172 		finish_session(ff);
173 	}
174 
175 	/*
176 	 * Regardless of current source of clock signal, drivers transfer some
177 	 * packets. Then, the device transfers packets.
178 	 */
179 	if (!amdtp_stream_running(&ff->rx_stream)) {
180 		err = ff->spec->protocol->begin_session(ff, rate);
181 		if (err < 0)
182 			goto error;
183 
184 		err = amdtp_stream_start(&ff->rx_stream,
185 					 ff->rx_resources.channel,
186 					 fw_parent_device(ff->unit)->max_speed);
187 		if (err < 0)
188 			goto error;
189 
190 		if (!amdtp_stream_wait_callback(&ff->rx_stream,
191 						CALLBACK_TIMEOUT_MS)) {
192 			err = -ETIMEDOUT;
193 			goto error;
194 		}
195 
196 		err = ff->spec->protocol->switch_fetching_mode(ff, true);
197 		if (err < 0)
198 			goto error;
199 	}
200 
201 	if (!amdtp_stream_running(&ff->tx_stream)) {
202 		err = amdtp_stream_start(&ff->tx_stream,
203 					 ff->tx_resources.channel,
204 					 fw_parent_device(ff->unit)->max_speed);
205 		if (err < 0)
206 			goto error;
207 
208 		if (!amdtp_stream_wait_callback(&ff->tx_stream,
209 						CALLBACK_TIMEOUT_MS)) {
210 			err = -ETIMEDOUT;
211 			goto error;
212 		}
213 	}
214 
215 	return 0;
216 error:
217 	amdtp_stream_stop(&ff->tx_stream);
218 	amdtp_stream_stop(&ff->rx_stream);
219 
220 	finish_session(ff);
221 
222 	return err;
223 }
224 
225 void snd_ff_stream_stop_duplex(struct snd_ff *ff)
226 {
227 	if (ff->substreams_counter > 0)
228 		return;
229 
230 	amdtp_stream_stop(&ff->tx_stream);
231 	amdtp_stream_stop(&ff->rx_stream);
232 	finish_session(ff);
233 }
234 
235 void snd_ff_stream_update_duplex(struct snd_ff *ff)
236 {
237 	// The device discontinue to transfer packets.
238 	amdtp_stream_pcm_abort(&ff->tx_stream);
239 	amdtp_stream_stop(&ff->tx_stream);
240 
241 	amdtp_stream_pcm_abort(&ff->rx_stream);
242 	amdtp_stream_stop(&ff->rx_stream);
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