Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/fireface/ff-pcm.c
29266 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* ff-pcm.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
static int hw_rule_rate(struct snd_pcm_hw_params *params,
11
struct snd_pcm_hw_rule *rule)
12
{
13
const unsigned int *pcm_channels = rule->private;
14
struct snd_interval *r =
15
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
16
const struct snd_interval *c =
17
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
18
struct snd_interval t = {
19
.min = UINT_MAX, .max = 0, .integer = 1
20
};
21
unsigned int i;
22
23
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
24
enum snd_ff_stream_mode mode;
25
int err;
26
27
err = snd_ff_stream_get_multiplier_mode(i, &mode);
28
if (err < 0)
29
continue;
30
31
if (!snd_interval_test(c, pcm_channels[mode]))
32
continue;
33
34
t.min = min(t.min, amdtp_rate_table[i]);
35
t.max = max(t.max, amdtp_rate_table[i]);
36
}
37
38
return snd_interval_refine(r, &t);
39
}
40
41
static int hw_rule_channels(struct snd_pcm_hw_params *params,
42
struct snd_pcm_hw_rule *rule)
43
{
44
const unsigned int *pcm_channels = rule->private;
45
struct snd_interval *c =
46
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
47
const struct snd_interval *r =
48
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
49
struct snd_interval t = {
50
.min = UINT_MAX, .max = 0, .integer = 1
51
};
52
unsigned int i;
53
54
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
55
enum snd_ff_stream_mode mode;
56
int err;
57
58
err = snd_ff_stream_get_multiplier_mode(i, &mode);
59
if (err < 0)
60
continue;
61
62
if (!snd_interval_test(r, amdtp_rate_table[i]))
63
continue;
64
65
t.min = min(t.min, pcm_channels[mode]);
66
t.max = max(t.max, pcm_channels[mode]);
67
}
68
69
return snd_interval_refine(c, &t);
70
}
71
72
static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
73
const unsigned int *pcm_channels)
74
{
75
unsigned int rate, channels;
76
int i;
77
78
hw->channels_min = UINT_MAX;
79
hw->channels_max = 0;
80
hw->rate_min = UINT_MAX;
81
hw->rate_max = 0;
82
83
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
84
enum snd_ff_stream_mode mode;
85
int err;
86
87
err = snd_ff_stream_get_multiplier_mode(i, &mode);
88
if (err < 0)
89
continue;
90
91
channels = pcm_channels[mode];
92
if (pcm_channels[mode] == 0)
93
continue;
94
hw->channels_min = min(hw->channels_min, channels);
95
hw->channels_max = max(hw->channels_max, channels);
96
97
rate = amdtp_rate_table[i];
98
hw->rates |= snd_pcm_rate_to_rate_bit(rate);
99
hw->rate_min = min(hw->rate_min, rate);
100
hw->rate_max = max(hw->rate_max, rate);
101
}
102
}
103
104
static int pcm_init_hw_params(struct snd_ff *ff,
105
struct snd_pcm_substream *substream)
106
{
107
struct snd_pcm_runtime *runtime = substream->runtime;
108
struct amdtp_stream *s;
109
const unsigned int *pcm_channels;
110
int err;
111
112
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
113
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
114
s = &ff->tx_stream;
115
pcm_channels = ff->spec->pcm_capture_channels;
116
} else {
117
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
118
s = &ff->rx_stream;
119
pcm_channels = ff->spec->pcm_playback_channels;
120
}
121
122
limit_channels_and_rates(&runtime->hw, pcm_channels);
123
124
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
125
hw_rule_channels, (void *)pcm_channels,
126
SNDRV_PCM_HW_PARAM_RATE, -1);
127
if (err < 0)
128
return err;
129
130
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
131
hw_rule_rate, (void *)pcm_channels,
132
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
133
if (err < 0)
134
return err;
135
136
return amdtp_ff_add_pcm_hw_constraints(s, runtime);
137
}
138
139
static int pcm_open(struct snd_pcm_substream *substream)
140
{
141
struct snd_ff *ff = substream->private_data;
142
struct amdtp_domain *d = &ff->domain;
143
unsigned int rate;
144
enum snd_ff_clock_src src;
145
int i, err;
146
147
err = snd_ff_stream_lock_try(ff);
148
if (err < 0)
149
return err;
150
151
err = pcm_init_hw_params(ff, substream);
152
if (err < 0)
153
goto release_lock;
154
155
err = ff->spec->protocol->get_clock(ff, &rate, &src);
156
if (err < 0)
157
goto release_lock;
158
159
scoped_guard(mutex, &ff->mutex) {
160
// When source of clock is not internal or any stream is reserved for
161
// transmission of PCM frames, the available sampling rate is limited
162
// at current one.
163
if (src != SND_FF_CLOCK_SRC_INTERNAL) {
164
for (i = 0; i < CIP_SFC_COUNT; ++i) {
165
if (amdtp_rate_table[i] == rate)
166
break;
167
}
168
169
// The unit is configured at sampling frequency which packet
170
// streaming engine can't support.
171
if (i >= CIP_SFC_COUNT) {
172
err = -EIO;
173
goto release_lock;
174
}
175
176
substream->runtime->hw.rate_min = rate;
177
substream->runtime->hw.rate_max = rate;
178
} else {
179
if (ff->substreams_counter > 0) {
180
unsigned int frames_per_period = d->events_per_period;
181
unsigned int frames_per_buffer = d->events_per_buffer;
182
183
rate = amdtp_rate_table[ff->rx_stream.sfc];
184
substream->runtime->hw.rate_min = rate;
185
substream->runtime->hw.rate_max = rate;
186
187
err = snd_pcm_hw_constraint_minmax(substream->runtime,
188
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
189
frames_per_period, frames_per_period);
190
if (err < 0)
191
goto release_lock;
192
193
err = snd_pcm_hw_constraint_minmax(substream->runtime,
194
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
195
frames_per_buffer, frames_per_buffer);
196
if (err < 0)
197
goto release_lock;
198
}
199
}
200
}
201
202
snd_pcm_set_sync(substream);
203
204
return 0;
205
206
release_lock:
207
snd_ff_stream_lock_release(ff);
208
return err;
209
}
210
211
static int pcm_close(struct snd_pcm_substream *substream)
212
{
213
struct snd_ff *ff = substream->private_data;
214
215
snd_ff_stream_lock_release(ff);
216
217
return 0;
218
}
219
220
static int pcm_hw_params(struct snd_pcm_substream *substream,
221
struct snd_pcm_hw_params *hw_params)
222
{
223
struct snd_ff *ff = substream->private_data;
224
int err = 0;
225
226
if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
227
unsigned int rate = params_rate(hw_params);
228
unsigned int frames_per_period = params_period_size(hw_params);
229
unsigned int frames_per_buffer = params_buffer_size(hw_params);
230
231
guard(mutex)(&ff->mutex);
232
err = snd_ff_stream_reserve_duplex(ff, rate, frames_per_period,
233
frames_per_buffer);
234
if (err >= 0)
235
++ff->substreams_counter;
236
}
237
238
return err;
239
}
240
241
static int pcm_hw_free(struct snd_pcm_substream *substream)
242
{
243
struct snd_ff *ff = substream->private_data;
244
245
guard(mutex)(&ff->mutex);
246
247
if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
248
--ff->substreams_counter;
249
250
snd_ff_stream_stop_duplex(ff);
251
252
return 0;
253
}
254
255
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
256
{
257
struct snd_ff *ff = substream->private_data;
258
struct snd_pcm_runtime *runtime = substream->runtime;
259
int err;
260
261
guard(mutex)(&ff->mutex);
262
263
err = snd_ff_stream_start_duplex(ff, runtime->rate);
264
if (err >= 0)
265
amdtp_stream_pcm_prepare(&ff->tx_stream);
266
267
return err;
268
}
269
270
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
271
{
272
struct snd_ff *ff = substream->private_data;
273
struct snd_pcm_runtime *runtime = substream->runtime;
274
int err;
275
276
guard(mutex)(&ff->mutex);
277
278
err = snd_ff_stream_start_duplex(ff, runtime->rate);
279
if (err >= 0)
280
amdtp_stream_pcm_prepare(&ff->rx_stream);
281
282
return err;
283
}
284
285
static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
286
{
287
struct snd_ff *ff = substream->private_data;
288
289
switch (cmd) {
290
case SNDRV_PCM_TRIGGER_START:
291
amdtp_stream_pcm_trigger(&ff->tx_stream, substream);
292
break;
293
case SNDRV_PCM_TRIGGER_STOP:
294
amdtp_stream_pcm_trigger(&ff->tx_stream, NULL);
295
break;
296
default:
297
return -EINVAL;
298
}
299
300
return 0;
301
}
302
303
static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
304
{
305
struct snd_ff *ff = substream->private_data;
306
307
switch (cmd) {
308
case SNDRV_PCM_TRIGGER_START:
309
amdtp_stream_pcm_trigger(&ff->rx_stream, substream);
310
break;
311
case SNDRV_PCM_TRIGGER_STOP:
312
amdtp_stream_pcm_trigger(&ff->rx_stream, NULL);
313
break;
314
default:
315
return -EINVAL;
316
}
317
318
return 0;
319
}
320
321
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
322
{
323
struct snd_ff *ff = sbstrm->private_data;
324
325
return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->tx_stream);
326
}
327
328
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
329
{
330
struct snd_ff *ff = sbstrm->private_data;
331
332
return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->rx_stream);
333
}
334
335
static int pcm_capture_ack(struct snd_pcm_substream *substream)
336
{
337
struct snd_ff *ff = substream->private_data;
338
339
return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->tx_stream);
340
}
341
342
static int pcm_playback_ack(struct snd_pcm_substream *substream)
343
{
344
struct snd_ff *ff = substream->private_data;
345
346
return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->rx_stream);
347
}
348
349
int snd_ff_create_pcm_devices(struct snd_ff *ff)
350
{
351
static const struct snd_pcm_ops pcm_capture_ops = {
352
.open = pcm_open,
353
.close = pcm_close,
354
.hw_params = pcm_hw_params,
355
.hw_free = pcm_hw_free,
356
.prepare = pcm_capture_prepare,
357
.trigger = pcm_capture_trigger,
358
.pointer = pcm_capture_pointer,
359
.ack = pcm_capture_ack,
360
};
361
static const struct snd_pcm_ops pcm_playback_ops = {
362
.open = pcm_open,
363
.close = pcm_close,
364
.hw_params = pcm_hw_params,
365
.hw_free = pcm_hw_free,
366
.prepare = pcm_playback_prepare,
367
.trigger = pcm_playback_trigger,
368
.pointer = pcm_playback_pointer,
369
.ack = pcm_playback_ack,
370
};
371
struct snd_pcm *pcm;
372
int err;
373
374
err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm);
375
if (err < 0)
376
return err;
377
378
pcm->private_data = ff;
379
pcm->nonatomic = true;
380
snprintf(pcm->name, sizeof(pcm->name),
381
"%s PCM", ff->card->shortname);
382
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
383
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
384
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
385
386
return 0;
387
}
388
389