.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * ff-stream.c - a part of driver for RME Fireface series |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2015-2017 Takashi Sakamoto |
---|
5 | | - * |
---|
6 | | - * Licensed under the terms of the GNU General Public License, version 2. |
---|
7 | 6 | */ |
---|
8 | 7 | |
---|
9 | 8 | #include "ff.h" |
---|
10 | 9 | |
---|
11 | 10 | #define CALLBACK_TIMEOUT_MS 200 |
---|
12 | 11 | |
---|
13 | | -static int get_rate_mode(unsigned int rate, unsigned int *mode) |
---|
| 12 | +int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, |
---|
| 13 | + enum snd_ff_stream_mode *mode) |
---|
14 | 14 | { |
---|
15 | | - int i; |
---|
| 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 | + }; |
---|
16 | 24 | |
---|
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) |
---|
| 25 | + if (sfc >= CIP_SFC_COUNT) |
---|
23 | 26 | return -EINVAL; |
---|
24 | 27 | |
---|
25 | | - *mode = ((int)i - 1) / 2; |
---|
| 28 | + *mode = modes[sfc]; |
---|
26 | 29 | |
---|
27 | 30 | 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 | 31 | } |
---|
75 | 32 | |
---|
76 | 33 | static inline void finish_session(struct snd_ff *ff) |
---|
.. | .. |
---|
79 | 36 | ff->spec->protocol->switch_fetching_mode(ff, false); |
---|
80 | 37 | } |
---|
81 | 38 | |
---|
82 | | -static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) |
---|
| 39 | +static int init_stream(struct snd_ff *ff, struct amdtp_stream *s) |
---|
83 | 40 | { |
---|
84 | | - int err; |
---|
85 | 41 | struct fw_iso_resources *resources; |
---|
86 | | - struct amdtp_stream *stream; |
---|
| 42 | + enum amdtp_stream_direction dir; |
---|
| 43 | + int err; |
---|
87 | 44 | |
---|
88 | | - if (dir == AMDTP_IN_STREAM) { |
---|
| 45 | + if (s == &ff->tx_stream) { |
---|
89 | 46 | resources = &ff->tx_resources; |
---|
90 | | - stream = &ff->tx_stream; |
---|
| 47 | + dir = AMDTP_IN_STREAM; |
---|
91 | 48 | } else { |
---|
92 | 49 | resources = &ff->rx_resources; |
---|
93 | | - stream = &ff->rx_stream; |
---|
| 50 | + dir = AMDTP_OUT_STREAM; |
---|
94 | 51 | } |
---|
95 | 52 | |
---|
96 | 53 | err = fw_iso_resources_init(resources, ff->unit); |
---|
97 | 54 | if (err < 0) |
---|
98 | 55 | return err; |
---|
99 | 56 | |
---|
100 | | - err = amdtp_ff_init(stream, ff->unit, dir); |
---|
| 57 | + err = amdtp_ff_init(s, ff->unit, dir); |
---|
101 | 58 | if (err < 0) |
---|
102 | 59 | fw_iso_resources_destroy(resources); |
---|
103 | 60 | |
---|
104 | 61 | return err; |
---|
105 | 62 | } |
---|
106 | 63 | |
---|
107 | | -static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) |
---|
| 64 | +static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s) |
---|
108 | 65 | { |
---|
109 | | - if (dir == AMDTP_IN_STREAM) { |
---|
110 | | - amdtp_stream_destroy(&ff->tx_stream); |
---|
| 66 | + amdtp_stream_destroy(s); |
---|
| 67 | + |
---|
| 68 | + if (s == &ff->tx_stream) |
---|
111 | 69 | fw_iso_resources_destroy(&ff->tx_resources); |
---|
112 | | - } else { |
---|
113 | | - amdtp_stream_destroy(&ff->rx_stream); |
---|
| 70 | + else |
---|
114 | 71 | fw_iso_resources_destroy(&ff->rx_resources); |
---|
115 | | - } |
---|
116 | 72 | } |
---|
117 | 73 | |
---|
118 | 74 | int snd_ff_stream_init_duplex(struct snd_ff *ff) |
---|
119 | 75 | { |
---|
120 | 76 | int err; |
---|
121 | 77 | |
---|
122 | | - err = init_stream(ff, AMDTP_OUT_STREAM); |
---|
| 78 | + err = init_stream(ff, &ff->rx_stream); |
---|
123 | 79 | if (err < 0) |
---|
124 | | - goto end; |
---|
| 80 | + return err; |
---|
125 | 81 | |
---|
126 | | - err = init_stream(ff, AMDTP_IN_STREAM); |
---|
127 | | - if (err < 0) |
---|
128 | | - destroy_stream(ff, AMDTP_OUT_STREAM); |
---|
129 | | -end: |
---|
| 82 | + err = init_stream(ff, &ff->tx_stream); |
---|
| 83 | + if (err < 0) { |
---|
| 84 | + destroy_stream(ff, &ff->rx_stream); |
---|
| 85 | + return err; |
---|
| 86 | + } |
---|
| 87 | + |
---|
| 88 | + err = amdtp_domain_init(&ff->domain); |
---|
| 89 | + if (err < 0) { |
---|
| 90 | + destroy_stream(ff, &ff->rx_stream); |
---|
| 91 | + destroy_stream(ff, &ff->tx_stream); |
---|
| 92 | + } |
---|
| 93 | + |
---|
130 | 94 | return err; |
---|
131 | 95 | } |
---|
132 | 96 | |
---|
.. | .. |
---|
136 | 100 | */ |
---|
137 | 101 | void snd_ff_stream_destroy_duplex(struct snd_ff *ff) |
---|
138 | 102 | { |
---|
139 | | - destroy_stream(ff, AMDTP_IN_STREAM); |
---|
140 | | - destroy_stream(ff, AMDTP_OUT_STREAM); |
---|
| 103 | + amdtp_domain_destroy(&ff->domain); |
---|
| 104 | + |
---|
| 105 | + destroy_stream(ff, &ff->rx_stream); |
---|
| 106 | + destroy_stream(ff, &ff->tx_stream); |
---|
141 | 107 | } |
---|
142 | 108 | |
---|
143 | | -int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) |
---|
| 109 | +int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate, |
---|
| 110 | + unsigned int frames_per_period, |
---|
| 111 | + unsigned int frames_per_buffer) |
---|
144 | 112 | { |
---|
145 | 113 | unsigned int curr_rate; |
---|
146 | 114 | enum snd_ff_clock_src src; |
---|
147 | 115 | int err; |
---|
148 | 116 | |
---|
149 | | - if (ff->substreams_counter == 0) |
---|
150 | | - return 0; |
---|
151 | | - |
---|
152 | 117 | err = ff->spec->protocol->get_clock(ff, &curr_rate, &src); |
---|
153 | 118 | if (err < 0) |
---|
154 | 119 | return err; |
---|
155 | | - if (curr_rate != rate || |
---|
156 | | - amdtp_streaming_error(&ff->tx_stream) || |
---|
157 | | - amdtp_streaming_error(&ff->rx_stream)) { |
---|
| 120 | + |
---|
| 121 | + if (ff->substreams_counter == 0 || curr_rate != rate) { |
---|
| 122 | + enum snd_ff_stream_mode mode; |
---|
| 123 | + int i; |
---|
| 124 | + |
---|
| 125 | + amdtp_domain_stop(&ff->domain); |
---|
158 | 126 | finish_session(ff); |
---|
159 | 127 | |
---|
160 | | - amdtp_stream_stop(&ff->tx_stream); |
---|
161 | | - amdtp_stream_stop(&ff->rx_stream); |
---|
| 128 | + fw_iso_resources_free(&ff->tx_resources); |
---|
| 129 | + fw_iso_resources_free(&ff->rx_resources); |
---|
162 | 130 | |
---|
163 | | - release_resources(ff); |
---|
| 131 | + for (i = 0; i < CIP_SFC_COUNT; ++i) { |
---|
| 132 | + if (amdtp_rate_table[i] == rate) |
---|
| 133 | + break; |
---|
| 134 | + } |
---|
| 135 | + if (i >= CIP_SFC_COUNT) |
---|
| 136 | + return -EINVAL; |
---|
| 137 | + |
---|
| 138 | + err = snd_ff_stream_get_multiplier_mode(i, &mode); |
---|
| 139 | + if (err < 0) |
---|
| 140 | + return err; |
---|
| 141 | + |
---|
| 142 | + err = amdtp_ff_set_parameters(&ff->tx_stream, rate, |
---|
| 143 | + ff->spec->pcm_capture_channels[mode]); |
---|
| 144 | + if (err < 0) |
---|
| 145 | + return err; |
---|
| 146 | + |
---|
| 147 | + err = amdtp_ff_set_parameters(&ff->rx_stream, rate, |
---|
| 148 | + ff->spec->pcm_playback_channels[mode]); |
---|
| 149 | + if (err < 0) |
---|
| 150 | + return err; |
---|
| 151 | + |
---|
| 152 | + err = ff->spec->protocol->allocate_resources(ff, rate); |
---|
| 153 | + if (err < 0) |
---|
| 154 | + return err; |
---|
| 155 | + |
---|
| 156 | + err = amdtp_domain_set_events_per_period(&ff->domain, |
---|
| 157 | + frames_per_period, frames_per_buffer); |
---|
| 158 | + if (err < 0) { |
---|
| 159 | + fw_iso_resources_free(&ff->tx_resources); |
---|
| 160 | + fw_iso_resources_free(&ff->rx_resources); |
---|
| 161 | + return err; |
---|
| 162 | + } |
---|
| 163 | + } |
---|
| 164 | + |
---|
| 165 | + return 0; |
---|
| 166 | +} |
---|
| 167 | + |
---|
| 168 | +int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) |
---|
| 169 | +{ |
---|
| 170 | + int err; |
---|
| 171 | + |
---|
| 172 | + if (ff->substreams_counter == 0) |
---|
| 173 | + return 0; |
---|
| 174 | + |
---|
| 175 | + if (amdtp_streaming_error(&ff->tx_stream) || |
---|
| 176 | + amdtp_streaming_error(&ff->rx_stream)) { |
---|
| 177 | + amdtp_domain_stop(&ff->domain); |
---|
| 178 | + finish_session(ff); |
---|
164 | 179 | } |
---|
165 | 180 | |
---|
166 | 181 | /* |
---|
.. | .. |
---|
168 | 183 | * packets. Then, the device transfers packets. |
---|
169 | 184 | */ |
---|
170 | 185 | if (!amdtp_stream_running(&ff->rx_stream)) { |
---|
171 | | - err = keep_resources(ff, rate); |
---|
172 | | - if (err < 0) |
---|
173 | | - goto error; |
---|
| 186 | + int spd = fw_parent_device(ff->unit)->max_speed; |
---|
174 | 187 | |
---|
175 | 188 | err = ff->spec->protocol->begin_session(ff, rate); |
---|
176 | 189 | if (err < 0) |
---|
177 | 190 | goto error; |
---|
178 | 191 | |
---|
179 | | - err = amdtp_stream_start(&ff->rx_stream, |
---|
180 | | - ff->rx_resources.channel, |
---|
181 | | - fw_parent_device(ff->unit)->max_speed); |
---|
| 192 | + err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream, |
---|
| 193 | + ff->rx_resources.channel, spd); |
---|
| 194 | + if (err < 0) |
---|
| 195 | + goto error; |
---|
| 196 | + |
---|
| 197 | + err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream, |
---|
| 198 | + ff->tx_resources.channel, spd); |
---|
| 199 | + if (err < 0) |
---|
| 200 | + goto error; |
---|
| 201 | + |
---|
| 202 | + err = amdtp_domain_start(&ff->domain, 0); |
---|
182 | 203 | if (err < 0) |
---|
183 | 204 | goto error; |
---|
184 | 205 | |
---|
185 | 206 | if (!amdtp_stream_wait_callback(&ff->rx_stream, |
---|
| 207 | + CALLBACK_TIMEOUT_MS) || |
---|
| 208 | + !amdtp_stream_wait_callback(&ff->tx_stream, |
---|
186 | 209 | CALLBACK_TIMEOUT_MS)) { |
---|
187 | 210 | err = -ETIMEDOUT; |
---|
188 | 211 | goto error; |
---|
.. | .. |
---|
193 | 216 | goto error; |
---|
194 | 217 | } |
---|
195 | 218 | |
---|
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 | 219 | return 0; |
---|
211 | 220 | error: |
---|
212 | | - amdtp_stream_stop(&ff->tx_stream); |
---|
213 | | - amdtp_stream_stop(&ff->rx_stream); |
---|
214 | | - |
---|
| 221 | + amdtp_domain_stop(&ff->domain); |
---|
215 | 222 | finish_session(ff); |
---|
216 | | - release_resources(ff); |
---|
217 | 223 | |
---|
218 | 224 | return err; |
---|
219 | 225 | } |
---|
220 | 226 | |
---|
221 | 227 | void snd_ff_stream_stop_duplex(struct snd_ff *ff) |
---|
222 | 228 | { |
---|
223 | | - if (ff->substreams_counter > 0) |
---|
224 | | - return; |
---|
| 229 | + if (ff->substreams_counter == 0) { |
---|
| 230 | + amdtp_domain_stop(&ff->domain); |
---|
| 231 | + finish_session(ff); |
---|
225 | 232 | |
---|
226 | | - amdtp_stream_stop(&ff->tx_stream); |
---|
227 | | - amdtp_stream_stop(&ff->rx_stream); |
---|
228 | | - finish_session(ff); |
---|
229 | | - release_resources(ff); |
---|
| 233 | + fw_iso_resources_free(&ff->tx_resources); |
---|
| 234 | + fw_iso_resources_free(&ff->rx_resources); |
---|
| 235 | + } |
---|
230 | 236 | } |
---|
231 | 237 | |
---|
232 | 238 | void snd_ff_stream_update_duplex(struct snd_ff *ff) |
---|
233 | 239 | { |
---|
234 | | - /* The device discontinue to transfer packets. */ |
---|
| 240 | + amdtp_domain_stop(&ff->domain); |
---|
| 241 | + |
---|
| 242 | + // The device discontinue to transfer packets. |
---|
235 | 243 | amdtp_stream_pcm_abort(&ff->tx_stream); |
---|
236 | | - amdtp_stream_stop(&ff->tx_stream); |
---|
237 | | - |
---|
238 | 244 | 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 | 245 | } |
---|
244 | 246 | |
---|
245 | 247 | void snd_ff_stream_lock_changed(struct snd_ff *ff) |
---|