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