Commit | Line | Data |
---|---|---|
da607e19 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
dd49b2d1 TS |
2 | /* |
3 | * motu-pcm.c - a part of driver for MOTU FireWire series | |
4 | * | |
5 | * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp> | |
dd49b2d1 TS |
6 | */ |
7 | ||
8 | #include <sound/pcm_params.h> | |
9 | #include "motu.h" | |
10 | ||
11 | static int motu_rate_constraint(struct snd_pcm_hw_params *params, | |
12 | struct snd_pcm_hw_rule *rule) | |
13 | { | |
14 | struct snd_motu_packet_format *formats = rule->private; | |
15 | ||
16 | const struct snd_interval *c = | |
17 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); | |
18 | struct snd_interval *r = | |
19 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | |
20 | struct snd_interval rates = { | |
21 | .min = UINT_MAX, .max = 0, .integer = 1 | |
22 | }; | |
23 | unsigned int i, pcm_channels, rate, mode; | |
24 | ||
25 | for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) { | |
26 | rate = snd_motu_clock_rates[i]; | |
27 | mode = i / 2; | |
28 | ||
9883b385 | 29 | pcm_channels = formats->pcm_chunks[mode]; |
dd49b2d1 TS |
30 | if (!snd_interval_test(c, pcm_channels)) |
31 | continue; | |
32 | ||
33 | rates.min = min(rates.min, rate); | |
34 | rates.max = max(rates.max, rate); | |
35 | } | |
36 | ||
37 | return snd_interval_refine(r, &rates); | |
38 | } | |
39 | ||
40 | static int motu_channels_constraint(struct snd_pcm_hw_params *params, | |
41 | struct snd_pcm_hw_rule *rule) | |
42 | { | |
43 | struct snd_motu_packet_format *formats = rule->private; | |
44 | ||
45 | const struct snd_interval *r = | |
46 | hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); | |
47 | struct snd_interval *c = | |
48 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); | |
49 | struct snd_interval channels = { | |
50 | .min = UINT_MAX, .max = 0, .integer = 1 | |
51 | }; | |
52 | unsigned int i, pcm_channels, rate, mode; | |
53 | ||
54 | for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) { | |
55 | rate = snd_motu_clock_rates[i]; | |
56 | mode = i / 2; | |
57 | ||
58 | if (!snd_interval_test(r, rate)) | |
59 | continue; | |
60 | ||
9883b385 | 61 | pcm_channels = formats->pcm_chunks[mode]; |
dd49b2d1 TS |
62 | channels.min = min(channels.min, pcm_channels); |
63 | channels.max = max(channels.max, pcm_channels); | |
64 | } | |
65 | ||
66 | return snd_interval_refine(c, &channels); | |
67 | } | |
68 | ||
69 | static void limit_channels_and_rates(struct snd_motu *motu, | |
70 | struct snd_pcm_runtime *runtime, | |
71 | struct snd_motu_packet_format *formats) | |
72 | { | |
73 | struct snd_pcm_hardware *hw = &runtime->hw; | |
74 | unsigned int i, pcm_channels, rate, mode; | |
75 | ||
76 | hw->channels_min = UINT_MAX; | |
77 | hw->channels_max = 0; | |
78 | ||
79 | for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) { | |
80 | rate = snd_motu_clock_rates[i]; | |
81 | mode = i / 2; | |
82 | ||
9883b385 | 83 | pcm_channels = formats->pcm_chunks[mode]; |
dd49b2d1 TS |
84 | if (pcm_channels == 0) |
85 | continue; | |
86 | ||
87 | hw->rates |= snd_pcm_rate_to_rate_bit(rate); | |
88 | hw->channels_min = min(hw->channels_min, pcm_channels); | |
89 | hw->channels_max = max(hw->channels_max, pcm_channels); | |
90 | } | |
91 | ||
92 | snd_pcm_limit_hw_rates(runtime); | |
93 | } | |
94 | ||
dd49b2d1 TS |
95 | static int init_hw_info(struct snd_motu *motu, |
96 | struct snd_pcm_substream *substream) | |
97 | { | |
98 | struct snd_pcm_runtime *runtime = substream->runtime; | |
99 | struct snd_pcm_hardware *hw = &runtime->hw; | |
100 | struct amdtp_stream *stream; | |
101 | struct snd_motu_packet_format *formats; | |
102 | int err; | |
103 | ||
dd49b2d1 TS |
104 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
105 | hw->formats = SNDRV_PCM_FMTBIT_S32; | |
106 | stream = &motu->tx_stream; | |
107 | formats = &motu->tx_packet_formats; | |
108 | } else { | |
109 | hw->formats = SNDRV_PCM_FMTBIT_S32; | |
110 | stream = &motu->rx_stream; | |
111 | formats = &motu->rx_packet_formats; | |
112 | } | |
113 | ||
114 | limit_channels_and_rates(motu, runtime, formats); | |
dd49b2d1 TS |
115 | |
116 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | |
117 | motu_rate_constraint, formats, | |
118 | SNDRV_PCM_HW_PARAM_CHANNELS, -1); | |
119 | if (err < 0) | |
120 | return err; | |
121 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, | |
122 | motu_channels_constraint, formats, | |
123 | SNDRV_PCM_HW_PARAM_RATE, -1); | |
124 | if (err < 0) | |
125 | return err; | |
126 | ||
127 | return amdtp_motu_add_pcm_hw_constraints(stream, runtime); | |
128 | } | |
129 | ||
130 | static int pcm_open(struct snd_pcm_substream *substream) | |
131 | { | |
132 | struct snd_motu *motu = substream->private_data; | |
3fd80b20 | 133 | struct amdtp_domain *d = &motu->domain; |
dd49b2d1 | 134 | enum snd_motu_clock_source src; |
dd49b2d1 TS |
135 | int err; |
136 | ||
71c37977 TS |
137 | err = snd_motu_stream_lock_try(motu); |
138 | if (err < 0) | |
139 | return err; | |
140 | ||
dd49b2d1 TS |
141 | mutex_lock(&motu->mutex); |
142 | ||
8b460c76 | 143 | err = snd_motu_stream_cache_packet_formats(motu); |
dd49b2d1 | 144 | if (err < 0) |
71c37977 | 145 | goto err_locked; |
dd49b2d1 TS |
146 | |
147 | err = init_hw_info(motu, substream); | |
148 | if (err < 0) | |
71c37977 | 149 | goto err_locked; |
dd49b2d1 | 150 | |
ff222b7e | 151 | err = snd_motu_protocol_get_clock_source(motu, &src); |
dd49b2d1 | 152 | if (err < 0) |
71c37977 | 153 | goto err_locked; |
3fd80b20 TS |
154 | |
155 | // When source of clock is not internal or any stream is reserved for | |
156 | // transmission of PCM frames, the available sampling rate is limited | |
157 | // at current one. | |
3f58f004 TS |
158 | if ((src != SND_MOTU_CLOCK_SOURCE_INTERNAL && |
159 | src != SND_MOTU_CLOCK_SOURCE_SPH) || | |
3fd80b20 TS |
160 | (motu->substreams_counter > 0 && d->events_per_period > 0)) { |
161 | unsigned int frames_per_period = d->events_per_period; | |
0f5482e7 | 162 | unsigned int frames_per_buffer = d->events_per_buffer; |
3fd80b20 TS |
163 | unsigned int rate; |
164 | ||
ff222b7e | 165 | err = snd_motu_protocol_get_clock_rate(motu, &rate); |
dd49b2d1 | 166 | if (err < 0) |
71c37977 | 167 | goto err_locked; |
3fd80b20 | 168 | |
dd49b2d1 TS |
169 | substream->runtime->hw.rate_min = rate; |
170 | substream->runtime->hw.rate_max = rate; | |
3fd80b20 TS |
171 | |
172 | if (frames_per_period > 0) { | |
173 | err = snd_pcm_hw_constraint_minmax(substream->runtime, | |
174 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, | |
175 | frames_per_period, frames_per_period); | |
9ff3483e | 176 | if (err < 0) |
3fd80b20 | 177 | goto err_locked; |
0f5482e7 TS |
178 | |
179 | err = snd_pcm_hw_constraint_minmax(substream->runtime, | |
180 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, | |
181 | frames_per_buffer, frames_per_buffer); | |
9ff3483e | 182 | if (err < 0) |
0f5482e7 | 183 | goto err_locked; |
3fd80b20 | 184 | } |
dd49b2d1 TS |
185 | } |
186 | ||
187 | snd_pcm_set_sync(substream); | |
188 | ||
189 | mutex_unlock(&motu->mutex); | |
190 | ||
3fd80b20 | 191 | return 0; |
71c37977 TS |
192 | err_locked: |
193 | mutex_unlock(&motu->mutex); | |
194 | snd_motu_stream_lock_release(motu); | |
dd49b2d1 TS |
195 | return err; |
196 | } | |
197 | ||
198 | static int pcm_close(struct snd_pcm_substream *substream) | |
199 | { | |
71c37977 TS |
200 | struct snd_motu *motu = substream->private_data; |
201 | ||
202 | snd_motu_stream_lock_release(motu); | |
203 | ||
dd49b2d1 TS |
204 | return 0; |
205 | } | |
206 | ||
15d472ec TS |
207 | static int pcm_hw_params(struct snd_pcm_substream *substream, |
208 | struct snd_pcm_hw_params *hw_params) | |
dd49b2d1 TS |
209 | { |
210 | struct snd_motu *motu = substream->private_data; | |
7641d549 | 211 | int err = 0; |
dd49b2d1 | 212 | |
23cb0767 | 213 | if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) { |
8edc56ec | 214 | unsigned int rate = params_rate(hw_params); |
0d39cd0e | 215 | unsigned int frames_per_period = params_period_size(hw_params); |
0f5482e7 | 216 | unsigned int frames_per_buffer = params_buffer_size(hw_params); |
8edc56ec | 217 | |
dd49b2d1 | 218 | mutex_lock(&motu->mutex); |
0d39cd0e | 219 | err = snd_motu_stream_reserve_duplex(motu, rate, |
0f5482e7 | 220 | frames_per_period, frames_per_buffer); |
8edc56ec TS |
221 | if (err >= 0) |
222 | ++motu->substreams_counter; | |
dd49b2d1 TS |
223 | mutex_unlock(&motu->mutex); |
224 | } | |
225 | ||
8edc56ec | 226 | return err; |
dd49b2d1 | 227 | } |
dd49b2d1 | 228 | |
15d472ec | 229 | static int pcm_hw_free(struct snd_pcm_substream *substream) |
dd49b2d1 TS |
230 | { |
231 | struct snd_motu *motu = substream->private_data; | |
232 | ||
233 | mutex_lock(&motu->mutex); | |
234 | ||
23cb0767 | 235 | if (substream->runtime->state != SNDRV_PCM_STATE_OPEN) |
8edc56ec | 236 | --motu->substreams_counter; |
dd49b2d1 TS |
237 | |
238 | snd_motu_stream_stop_duplex(motu); | |
239 | ||
240 | mutex_unlock(&motu->mutex); | |
241 | ||
7641d549 | 242 | return 0; |
dd49b2d1 TS |
243 | } |
244 | ||
245 | static int capture_prepare(struct snd_pcm_substream *substream) | |
246 | { | |
247 | struct snd_motu *motu = substream->private_data; | |
248 | int err; | |
249 | ||
250 | mutex_lock(&motu->mutex); | |
8edc56ec | 251 | err = snd_motu_stream_start_duplex(motu); |
dd49b2d1 TS |
252 | mutex_unlock(&motu->mutex); |
253 | if (err >= 0) | |
254 | amdtp_stream_pcm_prepare(&motu->tx_stream); | |
255 | ||
256 | return 0; | |
257 | } | |
258 | static int playback_prepare(struct snd_pcm_substream *substream) | |
259 | { | |
260 | struct snd_motu *motu = substream->private_data; | |
261 | int err; | |
262 | ||
263 | mutex_lock(&motu->mutex); | |
8edc56ec | 264 | err = snd_motu_stream_start_duplex(motu); |
dd49b2d1 TS |
265 | mutex_unlock(&motu->mutex); |
266 | if (err >= 0) | |
267 | amdtp_stream_pcm_prepare(&motu->rx_stream); | |
268 | ||
269 | return err; | |
270 | } | |
271 | ||
272 | static int capture_trigger(struct snd_pcm_substream *substream, int cmd) | |
273 | { | |
274 | struct snd_motu *motu = substream->private_data; | |
275 | ||
276 | switch (cmd) { | |
277 | case SNDRV_PCM_TRIGGER_START: | |
278 | amdtp_stream_pcm_trigger(&motu->tx_stream, substream); | |
279 | break; | |
280 | case SNDRV_PCM_TRIGGER_STOP: | |
281 | amdtp_stream_pcm_trigger(&motu->tx_stream, NULL); | |
282 | break; | |
283 | default: | |
284 | return -EINVAL; | |
285 | } | |
286 | ||
287 | return 0; | |
288 | } | |
289 | static int playback_trigger(struct snd_pcm_substream *substream, int cmd) | |
290 | { | |
291 | struct snd_motu *motu = substream->private_data; | |
292 | ||
293 | switch (cmd) { | |
294 | case SNDRV_PCM_TRIGGER_START: | |
295 | amdtp_stream_pcm_trigger(&motu->rx_stream, substream); | |
296 | break; | |
297 | case SNDRV_PCM_TRIGGER_STOP: | |
298 | amdtp_stream_pcm_trigger(&motu->rx_stream, NULL); | |
299 | break; | |
300 | default: | |
301 | return -EINVAL; | |
302 | } | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) | |
308 | { | |
309 | struct snd_motu *motu = substream->private_data; | |
310 | ||
f890f9a0 | 311 | return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->tx_stream); |
dd49b2d1 TS |
312 | } |
313 | static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) | |
314 | { | |
315 | struct snd_motu *motu = substream->private_data; | |
316 | ||
f890f9a0 | 317 | return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->rx_stream); |
dd49b2d1 TS |
318 | } |
319 | ||
875becf8 TS |
320 | static int capture_ack(struct snd_pcm_substream *substream) |
321 | { | |
322 | struct snd_motu *motu = substream->private_data; | |
323 | ||
e6dcc92f | 324 | return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->tx_stream); |
875becf8 TS |
325 | } |
326 | ||
327 | static int playback_ack(struct snd_pcm_substream *substream) | |
328 | { | |
329 | struct snd_motu *motu = substream->private_data; | |
330 | ||
e6dcc92f | 331 | return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->rx_stream); |
875becf8 TS |
332 | } |
333 | ||
dd49b2d1 TS |
334 | int snd_motu_create_pcm_devices(struct snd_motu *motu) |
335 | { | |
b2165f38 | 336 | static const struct snd_pcm_ops capture_ops = { |
dd49b2d1 TS |
337 | .open = pcm_open, |
338 | .close = pcm_close, | |
15d472ec TS |
339 | .hw_params = pcm_hw_params, |
340 | .hw_free = pcm_hw_free, | |
dd49b2d1 TS |
341 | .prepare = capture_prepare, |
342 | .trigger = capture_trigger, | |
343 | .pointer = capture_pointer, | |
875becf8 | 344 | .ack = capture_ack, |
dd49b2d1 | 345 | }; |
b2165f38 | 346 | static const struct snd_pcm_ops playback_ops = { |
dd49b2d1 TS |
347 | .open = pcm_open, |
348 | .close = pcm_close, | |
15d472ec TS |
349 | .hw_params = pcm_hw_params, |
350 | .hw_free = pcm_hw_free, | |
dd49b2d1 TS |
351 | .prepare = playback_prepare, |
352 | .trigger = playback_trigger, | |
353 | .pointer = playback_pointer, | |
875becf8 | 354 | .ack = playback_ack, |
dd49b2d1 TS |
355 | }; |
356 | struct snd_pcm *pcm; | |
357 | int err; | |
358 | ||
359 | err = snd_pcm_new(motu->card, motu->card->driver, 0, 1, 1, &pcm); | |
360 | if (err < 0) | |
361 | return err; | |
362 | pcm->private_data = motu; | |
363 | strcpy(pcm->name, motu->card->shortname); | |
364 | ||
365 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); | |
366 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); | |
7641d549 | 367 | snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); |
dd49b2d1 TS |
368 | |
369 | return 0; | |
370 | } |