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