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 	amdtp_stream_stop(&ff->tx_stream);
37 	amdtp_stream_stop(&ff->rx_stream);
38 
39 	ff->spec->protocol->finish_session(ff);
40 	ff->spec->protocol->switch_fetching_mode(ff, false);
41 }
42 
43 static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
44 {
45 	int err;
46 	struct fw_iso_resources *resources;
47 	struct amdtp_stream *stream;
48 
49 	if (dir == AMDTP_IN_STREAM) {
50 		resources = &ff->tx_resources;
51 		stream = &ff->tx_stream;
52 	} else {
53 		resources = &ff->rx_resources;
54 		stream = &ff->rx_stream;
55 	}
56 
57 	err = fw_iso_resources_init(resources, ff->unit);
58 	if (err < 0)
59 		return err;
60 
61 	err = amdtp_ff_init(stream, ff->unit, dir);
62 	if (err < 0)
63 		fw_iso_resources_destroy(resources);
64 
65 	return err;
66 }
67 
68 static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
69 {
70 	if (dir == AMDTP_IN_STREAM) {
71 		amdtp_stream_destroy(&ff->tx_stream);
72 		fw_iso_resources_destroy(&ff->tx_resources);
73 	} else {
74 		amdtp_stream_destroy(&ff->rx_stream);
75 		fw_iso_resources_destroy(&ff->rx_resources);
76 	}
77 }
78 
79 int snd_ff_stream_init_duplex(struct snd_ff *ff)
80 {
81 	int err;
82 
83 	err = init_stream(ff, AMDTP_OUT_STREAM);
84 	if (err < 0)
85 		goto end;
86 
87 	err = init_stream(ff, AMDTP_IN_STREAM);
88 	if (err < 0)
89 		destroy_stream(ff, AMDTP_OUT_STREAM);
90 end:
91 	return err;
92 }
93 
94 /*
95  * This function should be called before starting streams or after stopping
96  * streams.
97  */
98 void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
99 {
100 	destroy_stream(ff, AMDTP_IN_STREAM);
101 	destroy_stream(ff, AMDTP_OUT_STREAM);
102 }
103 
104 int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
105 {
106 	unsigned int curr_rate;
107 	enum snd_ff_clock_src src;
108 	int err;
109 
110 	err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
111 	if (err < 0)
112 		return err;
113 
114 	if (ff->substreams_counter == 0 || curr_rate != rate) {
115 		enum snd_ff_stream_mode mode;
116 		int i;
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 int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
153 {
154 	int err;
155 
156 	if (ff->substreams_counter == 0)
157 		return 0;
158 
159 	if (amdtp_streaming_error(&ff->tx_stream) ||
160 	    amdtp_streaming_error(&ff->rx_stream))
161 		finish_session(ff);
162 
163 	/*
164 	 * Regardless of current source of clock signal, drivers transfer some
165 	 * packets. Then, the device transfers packets.
166 	 */
167 	if (!amdtp_stream_running(&ff->rx_stream)) {
168 		err = ff->spec->protocol->begin_session(ff, rate);
169 		if (err < 0)
170 			goto error;
171 
172 		err = amdtp_stream_start(&ff->rx_stream,
173 					 ff->rx_resources.channel,
174 					 fw_parent_device(ff->unit)->max_speed);
175 		if (err < 0)
176 			goto error;
177 
178 		if (!amdtp_stream_wait_callback(&ff->rx_stream,
179 						CALLBACK_TIMEOUT_MS)) {
180 			err = -ETIMEDOUT;
181 			goto error;
182 		}
183 
184 		err = ff->spec->protocol->switch_fetching_mode(ff, true);
185 		if (err < 0)
186 			goto error;
187 	}
188 
189 	if (!amdtp_stream_running(&ff->tx_stream)) {
190 		err = amdtp_stream_start(&ff->tx_stream,
191 					 ff->tx_resources.channel,
192 					 fw_parent_device(ff->unit)->max_speed);
193 		if (err < 0)
194 			goto error;
195 
196 		if (!amdtp_stream_wait_callback(&ff->tx_stream,
197 						CALLBACK_TIMEOUT_MS)) {
198 			err = -ETIMEDOUT;
199 			goto error;
200 		}
201 	}
202 
203 	return 0;
204 error:
205 	finish_session(ff);
206 
207 	return err;
208 }
209 
210 void snd_ff_stream_stop_duplex(struct snd_ff *ff)
211 {
212 	if (ff->substreams_counter == 0) {
213 		finish_session(ff);
214 
215 		fw_iso_resources_free(&ff->tx_resources);
216 		fw_iso_resources_free(&ff->rx_resources);
217 	}
218 }
219 
220 void snd_ff_stream_update_duplex(struct snd_ff *ff)
221 {
222 	// The device discontinue to transfer packets.
223 	amdtp_stream_pcm_abort(&ff->tx_stream);
224 	amdtp_stream_stop(&ff->tx_stream);
225 
226 	amdtp_stream_pcm_abort(&ff->rx_stream);
227 	amdtp_stream_stop(&ff->rx_stream);
228 }
229 
230 void snd_ff_stream_lock_changed(struct snd_ff *ff)
231 {
232 	ff->dev_lock_changed = true;
233 	wake_up(&ff->hwdep_wait);
234 }
235 
236 int snd_ff_stream_lock_try(struct snd_ff *ff)
237 {
238 	int err;
239 
240 	spin_lock_irq(&ff->lock);
241 
242 	/* user land lock this */
243 	if (ff->dev_lock_count < 0) {
244 		err = -EBUSY;
245 		goto end;
246 	}
247 
248 	/* this is the first time */
249 	if (ff->dev_lock_count++ == 0)
250 		snd_ff_stream_lock_changed(ff);
251 	err = 0;
252 end:
253 	spin_unlock_irq(&ff->lock);
254 	return err;
255 }
256 
257 void snd_ff_stream_lock_release(struct snd_ff *ff)
258 {
259 	spin_lock_irq(&ff->lock);
260 
261 	if (WARN_ON(ff->dev_lock_count <= 0))
262 		goto end;
263 	if (--ff->dev_lock_count == 0)
264 		snd_ff_stream_lock_changed(ff);
265 end:
266 	spin_unlock_irq(&ff->lock);
267 }
268