Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/motu/motu-pcm.c
29266 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* motu-pcm.c - a part of driver for MOTU FireWire series
4
*
5
* Copyright (c) 2015-2017 Takashi Sakamoto <[email protected]>
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
29
pcm_channels = formats->pcm_chunks[mode];
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
61
pcm_channels = formats->pcm_chunks[mode];
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
83
pcm_channels = formats->pcm_chunks[mode];
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
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
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);
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;
133
struct amdtp_domain *d = &motu->domain;
134
enum snd_motu_clock_source src;
135
int err;
136
137
err = snd_motu_stream_lock_try(motu);
138
if (err < 0)
139
return err;
140
141
scoped_guard(mutex, &motu->mutex) {
142
err = snd_motu_stream_cache_packet_formats(motu);
143
if (err < 0)
144
goto err_locked;
145
146
err = init_hw_info(motu, substream);
147
if (err < 0)
148
goto err_locked;
149
150
err = snd_motu_protocol_get_clock_source(motu, &src);
151
if (err < 0)
152
goto err_locked;
153
154
// When source of clock is not internal or any stream is reserved for
155
// transmission of PCM frames, the available sampling rate is limited
156
// at current one.
157
if ((src != SND_MOTU_CLOCK_SOURCE_INTERNAL &&
158
src != SND_MOTU_CLOCK_SOURCE_SPH) ||
159
(motu->substreams_counter > 0 && d->events_per_period > 0)) {
160
unsigned int frames_per_period = d->events_per_period;
161
unsigned int frames_per_buffer = d->events_per_buffer;
162
unsigned int rate;
163
164
err = snd_motu_protocol_get_clock_rate(motu, &rate);
165
if (err < 0)
166
goto err_locked;
167
168
substream->runtime->hw.rate_min = rate;
169
substream->runtime->hw.rate_max = rate;
170
171
if (frames_per_period > 0) {
172
err = snd_pcm_hw_constraint_minmax(substream->runtime,
173
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
174
frames_per_period, frames_per_period);
175
if (err < 0)
176
goto err_locked;
177
178
err = snd_pcm_hw_constraint_minmax(substream->runtime,
179
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
180
frames_per_buffer, frames_per_buffer);
181
if (err < 0)
182
goto err_locked;
183
}
184
}
185
}
186
187
snd_pcm_set_sync(substream);
188
189
return 0;
190
err_locked:
191
snd_motu_stream_lock_release(motu);
192
return err;
193
}
194
195
static int pcm_close(struct snd_pcm_substream *substream)
196
{
197
struct snd_motu *motu = substream->private_data;
198
199
snd_motu_stream_lock_release(motu);
200
201
return 0;
202
}
203
204
static int pcm_hw_params(struct snd_pcm_substream *substream,
205
struct snd_pcm_hw_params *hw_params)
206
{
207
struct snd_motu *motu = substream->private_data;
208
int err = 0;
209
210
if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
211
unsigned int rate = params_rate(hw_params);
212
unsigned int frames_per_period = params_period_size(hw_params);
213
unsigned int frames_per_buffer = params_buffer_size(hw_params);
214
215
guard(mutex)(&motu->mutex);
216
err = snd_motu_stream_reserve_duplex(motu, rate,
217
frames_per_period, frames_per_buffer);
218
if (err >= 0)
219
++motu->substreams_counter;
220
}
221
222
return err;
223
}
224
225
static int pcm_hw_free(struct snd_pcm_substream *substream)
226
{
227
struct snd_motu *motu = substream->private_data;
228
229
guard(mutex)(&motu->mutex);
230
231
if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
232
--motu->substreams_counter;
233
234
snd_motu_stream_stop_duplex(motu);
235
236
return 0;
237
}
238
239
static int capture_prepare(struct snd_pcm_substream *substream)
240
{
241
struct snd_motu *motu = substream->private_data;
242
int err;
243
244
scoped_guard(mutex, &motu->mutex) {
245
err = snd_motu_stream_start_duplex(motu);
246
}
247
if (err >= 0)
248
amdtp_stream_pcm_prepare(&motu->tx_stream);
249
250
return 0;
251
}
252
static int playback_prepare(struct snd_pcm_substream *substream)
253
{
254
struct snd_motu *motu = substream->private_data;
255
int err;
256
257
scoped_guard(mutex, &motu->mutex) {
258
err = snd_motu_stream_start_duplex(motu);
259
}
260
if (err >= 0)
261
amdtp_stream_pcm_prepare(&motu->rx_stream);
262
263
return err;
264
}
265
266
static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
267
{
268
struct snd_motu *motu = substream->private_data;
269
270
switch (cmd) {
271
case SNDRV_PCM_TRIGGER_START:
272
amdtp_stream_pcm_trigger(&motu->tx_stream, substream);
273
break;
274
case SNDRV_PCM_TRIGGER_STOP:
275
amdtp_stream_pcm_trigger(&motu->tx_stream, NULL);
276
break;
277
default:
278
return -EINVAL;
279
}
280
281
return 0;
282
}
283
static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
284
{
285
struct snd_motu *motu = substream->private_data;
286
287
switch (cmd) {
288
case SNDRV_PCM_TRIGGER_START:
289
amdtp_stream_pcm_trigger(&motu->rx_stream, substream);
290
break;
291
case SNDRV_PCM_TRIGGER_STOP:
292
amdtp_stream_pcm_trigger(&motu->rx_stream, NULL);
293
break;
294
default:
295
return -EINVAL;
296
}
297
298
return 0;
299
}
300
301
static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
302
{
303
struct snd_motu *motu = substream->private_data;
304
305
return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->tx_stream);
306
}
307
static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
308
{
309
struct snd_motu *motu = substream->private_data;
310
311
return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->rx_stream);
312
}
313
314
static int capture_ack(struct snd_pcm_substream *substream)
315
{
316
struct snd_motu *motu = substream->private_data;
317
318
return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->tx_stream);
319
}
320
321
static int playback_ack(struct snd_pcm_substream *substream)
322
{
323
struct snd_motu *motu = substream->private_data;
324
325
return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->rx_stream);
326
}
327
328
int snd_motu_create_pcm_devices(struct snd_motu *motu)
329
{
330
static const struct snd_pcm_ops capture_ops = {
331
.open = pcm_open,
332
.close = pcm_close,
333
.hw_params = pcm_hw_params,
334
.hw_free = pcm_hw_free,
335
.prepare = capture_prepare,
336
.trigger = capture_trigger,
337
.pointer = capture_pointer,
338
.ack = capture_ack,
339
};
340
static const struct snd_pcm_ops playback_ops = {
341
.open = pcm_open,
342
.close = pcm_close,
343
.hw_params = pcm_hw_params,
344
.hw_free = pcm_hw_free,
345
.prepare = playback_prepare,
346
.trigger = playback_trigger,
347
.pointer = playback_pointer,
348
.ack = playback_ack,
349
};
350
struct snd_pcm *pcm;
351
int err;
352
353
err = snd_pcm_new(motu->card, motu->card->driver, 0, 1, 1, &pcm);
354
if (err < 0)
355
return err;
356
pcm->private_data = motu;
357
pcm->nonatomic = true;
358
strscpy(pcm->name, motu->card->shortname);
359
360
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
361
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
362
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
363
364
return 0;
365
}
366
367