Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/bebob/bebob_pcm.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* bebob_pcm.c - a part of driver for BeBoB based devices
4
*
5
* Copyright (c) 2013-2014 Takashi Sakamoto
6
*/
7
8
#include "./bebob.h"
9
10
static int
11
hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
12
{
13
struct snd_bebob_stream_formation *formations = 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 < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
24
/* entry is invalid */
25
if (formations[i].pcm == 0)
26
continue;
27
28
if (!snd_interval_test(c, formations[i].pcm))
29
continue;
30
31
t.min = min(t.min, snd_bebob_rate_table[i]);
32
t.max = max(t.max, snd_bebob_rate_table[i]);
33
34
}
35
return snd_interval_refine(r, &t);
36
}
37
38
static int
39
hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
40
{
41
struct snd_bebob_stream_formation *formations = rule->private;
42
struct snd_interval *c =
43
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
44
const struct snd_interval *r =
45
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
46
struct snd_interval t = {
47
.min = UINT_MAX, .max = 0, .integer = 1
48
};
49
50
unsigned int i;
51
52
for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
53
/* entry is invalid */
54
if (formations[i].pcm == 0)
55
continue;
56
57
if (!snd_interval_test(r, snd_bebob_rate_table[i]))
58
continue;
59
60
t.min = min(t.min, formations[i].pcm);
61
t.max = max(t.max, formations[i].pcm);
62
}
63
64
return snd_interval_refine(c, &t);
65
}
66
67
static void
68
limit_channels_and_rates(struct snd_pcm_hardware *hw,
69
struct snd_bebob_stream_formation *formations)
70
{
71
unsigned int i;
72
73
hw->channels_min = UINT_MAX;
74
hw->channels_max = 0;
75
76
hw->rate_min = UINT_MAX;
77
hw->rate_max = 0;
78
hw->rates = 0;
79
80
for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
81
/* entry has no PCM channels */
82
if (formations[i].pcm == 0)
83
continue;
84
85
hw->channels_min = min(hw->channels_min, formations[i].pcm);
86
hw->channels_max = max(hw->channels_max, formations[i].pcm);
87
88
hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]);
89
hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]);
90
hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]);
91
}
92
}
93
94
static int
95
pcm_init_hw_params(struct snd_bebob *bebob,
96
struct snd_pcm_substream *substream)
97
{
98
struct snd_pcm_runtime *runtime = substream->runtime;
99
struct amdtp_stream *s;
100
struct snd_bebob_stream_formation *formations;
101
int err;
102
103
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
104
runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
105
s = &bebob->tx_stream;
106
formations = bebob->tx_stream_formations;
107
} else {
108
runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
109
s = &bebob->rx_stream;
110
formations = bebob->rx_stream_formations;
111
}
112
113
limit_channels_and_rates(&runtime->hw, formations);
114
115
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
116
hw_rule_channels, formations,
117
SNDRV_PCM_HW_PARAM_RATE, -1);
118
if (err < 0)
119
goto end;
120
121
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
122
hw_rule_rate, formations,
123
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
124
if (err < 0)
125
goto end;
126
127
err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
128
end:
129
return err;
130
}
131
132
static int pcm_open(struct snd_pcm_substream *substream)
133
{
134
struct snd_bebob *bebob = substream->private_data;
135
const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
136
struct amdtp_domain *d = &bebob->domain;
137
enum snd_bebob_clock_type src;
138
int err;
139
140
err = snd_bebob_stream_lock_try(bebob);
141
if (err < 0)
142
return err;
143
144
err = pcm_init_hw_params(bebob, substream);
145
if (err < 0)
146
goto err_locked;
147
148
err = snd_bebob_stream_get_clock_src(bebob, &src);
149
if (err < 0)
150
goto err_locked;
151
152
mutex_lock(&bebob->mutex);
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_BEBOB_CLOCK_TYPE_EXTERNAL ||
158
(bebob->substreams_counter > 0 && d->events_per_period > 0)) {
159
unsigned int frames_per_period = d->events_per_period;
160
unsigned int frames_per_buffer = d->events_per_buffer;
161
unsigned int sampling_rate;
162
163
err = spec->get(bebob, &sampling_rate);
164
if (err < 0) {
165
mutex_unlock(&bebob->mutex);
166
dev_err(&bebob->unit->device,
167
"fail to get sampling rate: %d\n", err);
168
goto err_locked;
169
}
170
171
substream->runtime->hw.rate_min = sampling_rate;
172
substream->runtime->hw.rate_max = sampling_rate;
173
174
if (frames_per_period > 0) {
175
err = snd_pcm_hw_constraint_minmax(substream->runtime,
176
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
177
frames_per_period, frames_per_period);
178
if (err < 0) {
179
mutex_unlock(&bebob->mutex);
180
goto err_locked;
181
}
182
183
err = snd_pcm_hw_constraint_minmax(substream->runtime,
184
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
185
frames_per_buffer, frames_per_buffer);
186
if (err < 0) {
187
mutex_unlock(&bebob->mutex);
188
goto err_locked;
189
}
190
}
191
}
192
193
mutex_unlock(&bebob->mutex);
194
195
snd_pcm_set_sync(substream);
196
197
return 0;
198
err_locked:
199
snd_bebob_stream_lock_release(bebob);
200
return err;
201
}
202
203
static int
204
pcm_close(struct snd_pcm_substream *substream)
205
{
206
struct snd_bebob *bebob = substream->private_data;
207
snd_bebob_stream_lock_release(bebob);
208
return 0;
209
}
210
211
static int pcm_hw_params(struct snd_pcm_substream *substream,
212
struct snd_pcm_hw_params *hw_params)
213
{
214
struct snd_bebob *bebob = substream->private_data;
215
int err = 0;
216
217
if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
218
unsigned int rate = params_rate(hw_params);
219
unsigned int frames_per_period = params_period_size(hw_params);
220
unsigned int frames_per_buffer = params_buffer_size(hw_params);
221
222
mutex_lock(&bebob->mutex);
223
err = snd_bebob_stream_reserve_duplex(bebob, rate,
224
frames_per_period, frames_per_buffer);
225
if (err >= 0)
226
++bebob->substreams_counter;
227
mutex_unlock(&bebob->mutex);
228
}
229
230
return err;
231
}
232
233
static int pcm_hw_free(struct snd_pcm_substream *substream)
234
{
235
struct snd_bebob *bebob = substream->private_data;
236
237
mutex_lock(&bebob->mutex);
238
239
if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
240
bebob->substreams_counter--;
241
242
snd_bebob_stream_stop_duplex(bebob);
243
244
mutex_unlock(&bebob->mutex);
245
246
return 0;
247
}
248
249
static int
250
pcm_capture_prepare(struct snd_pcm_substream *substream)
251
{
252
struct snd_bebob *bebob = substream->private_data;
253
int err;
254
255
err = snd_bebob_stream_start_duplex(bebob);
256
if (err >= 0)
257
amdtp_stream_pcm_prepare(&bebob->tx_stream);
258
259
return err;
260
}
261
static int
262
pcm_playback_prepare(struct snd_pcm_substream *substream)
263
{
264
struct snd_bebob *bebob = substream->private_data;
265
int err;
266
267
err = snd_bebob_stream_start_duplex(bebob);
268
if (err >= 0)
269
amdtp_stream_pcm_prepare(&bebob->rx_stream);
270
271
return err;
272
}
273
274
static int
275
pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
276
{
277
struct snd_bebob *bebob = substream->private_data;
278
279
switch (cmd) {
280
case SNDRV_PCM_TRIGGER_START:
281
amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);
282
break;
283
case SNDRV_PCM_TRIGGER_STOP:
284
amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);
285
break;
286
default:
287
return -EINVAL;
288
}
289
290
return 0;
291
}
292
static int
293
pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
294
{
295
struct snd_bebob *bebob = substream->private_data;
296
297
switch (cmd) {
298
case SNDRV_PCM_TRIGGER_START:
299
amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);
300
break;
301
case SNDRV_PCM_TRIGGER_STOP:
302
amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);
303
break;
304
default:
305
return -EINVAL;
306
}
307
308
return 0;
309
}
310
311
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
312
{
313
struct snd_bebob *bebob = sbstrm->private_data;
314
315
return amdtp_domain_stream_pcm_pointer(&bebob->domain,
316
&bebob->tx_stream);
317
}
318
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
319
{
320
struct snd_bebob *bebob = sbstrm->private_data;
321
322
return amdtp_domain_stream_pcm_pointer(&bebob->domain,
323
&bebob->rx_stream);
324
}
325
326
static int pcm_capture_ack(struct snd_pcm_substream *substream)
327
{
328
struct snd_bebob *bebob = substream->private_data;
329
330
return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->tx_stream);
331
}
332
333
static int pcm_playback_ack(struct snd_pcm_substream *substream)
334
{
335
struct snd_bebob *bebob = substream->private_data;
336
337
return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream);
338
}
339
340
int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
341
{
342
static const struct snd_pcm_ops capture_ops = {
343
.open = pcm_open,
344
.close = pcm_close,
345
.hw_params = pcm_hw_params,
346
.hw_free = pcm_hw_free,
347
.prepare = pcm_capture_prepare,
348
.trigger = pcm_capture_trigger,
349
.pointer = pcm_capture_pointer,
350
.ack = pcm_capture_ack,
351
};
352
static const struct snd_pcm_ops playback_ops = {
353
.open = pcm_open,
354
.close = pcm_close,
355
.hw_params = pcm_hw_params,
356
.hw_free = pcm_hw_free,
357
.prepare = pcm_playback_prepare,
358
.trigger = pcm_playback_trigger,
359
.pointer = pcm_playback_pointer,
360
.ack = pcm_playback_ack,
361
};
362
struct snd_pcm *pcm;
363
int err;
364
365
err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);
366
if (err < 0)
367
goto end;
368
369
pcm->private_data = bebob;
370
pcm->nonatomic = true;
371
snprintf(pcm->name, sizeof(pcm->name),
372
"%s PCM", bebob->card->shortname);
373
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
374
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
375
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
376
end:
377
return err;
378
}
379
380