Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/tascam/tascam-pcm.c
29266 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* tascam-pcm.c - a part of driver for TASCAM FireWire series
4
*
5
* Copyright (c) 2015 Takashi Sakamoto
6
*/
7
8
#include "tascam.h"
9
10
static int pcm_init_hw_params(struct snd_tscm *tscm,
11
struct snd_pcm_substream *substream)
12
{
13
struct snd_pcm_runtime *runtime = substream->runtime;
14
struct snd_pcm_hardware *hw = &runtime->hw;
15
struct amdtp_stream *stream;
16
unsigned int pcm_channels;
17
18
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
19
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
20
stream = &tscm->tx_stream;
21
pcm_channels = tscm->spec->pcm_capture_analog_channels;
22
} else {
23
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
24
stream = &tscm->rx_stream;
25
pcm_channels = tscm->spec->pcm_playback_analog_channels;
26
}
27
28
if (tscm->spec->has_adat)
29
pcm_channels += 8;
30
if (tscm->spec->has_spdif)
31
pcm_channels += 2;
32
runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
33
34
hw->rates = SNDRV_PCM_RATE_44100 |
35
SNDRV_PCM_RATE_48000 |
36
SNDRV_PCM_RATE_88200 |
37
SNDRV_PCM_RATE_96000;
38
snd_pcm_limit_hw_rates(runtime);
39
40
return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
41
}
42
43
static int pcm_open(struct snd_pcm_substream *substream)
44
{
45
struct snd_tscm *tscm = substream->private_data;
46
struct amdtp_domain *d = &tscm->domain;
47
enum snd_tscm_clock clock;
48
int err;
49
50
err = snd_tscm_stream_lock_try(tscm);
51
if (err < 0)
52
return err;
53
54
err = pcm_init_hw_params(tscm, substream);
55
if (err < 0)
56
goto err_locked;
57
58
err = snd_tscm_stream_get_clock(tscm, &clock);
59
if (err < 0)
60
goto err_locked;
61
62
scoped_guard(mutex, &tscm->mutex) {
63
// When source of clock is not internal or any stream is reserved for
64
// transmission of PCM frames, the available sampling rate is limited
65
// at current one.
66
if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) {
67
unsigned int frames_per_period = d->events_per_period;
68
unsigned int frames_per_buffer = d->events_per_buffer;
69
unsigned int rate;
70
71
err = snd_tscm_stream_get_rate(tscm, &rate);
72
if (err < 0)
73
goto err_locked;
74
substream->runtime->hw.rate_min = rate;
75
substream->runtime->hw.rate_max = rate;
76
77
err = snd_pcm_hw_constraint_minmax(substream->runtime,
78
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
79
frames_per_period, frames_per_period);
80
if (err < 0)
81
goto err_locked;
82
83
err = snd_pcm_hw_constraint_minmax(substream->runtime,
84
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
85
frames_per_buffer, frames_per_buffer);
86
if (err < 0)
87
goto err_locked;
88
}
89
}
90
91
snd_pcm_set_sync(substream);
92
93
return 0;
94
err_locked:
95
snd_tscm_stream_lock_release(tscm);
96
return err;
97
}
98
99
static int pcm_close(struct snd_pcm_substream *substream)
100
{
101
struct snd_tscm *tscm = substream->private_data;
102
103
snd_tscm_stream_lock_release(tscm);
104
105
return 0;
106
}
107
108
static int pcm_hw_params(struct snd_pcm_substream *substream,
109
struct snd_pcm_hw_params *hw_params)
110
{
111
struct snd_tscm *tscm = substream->private_data;
112
int err = 0;
113
114
if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
115
unsigned int rate = params_rate(hw_params);
116
unsigned int frames_per_period = params_period_size(hw_params);
117
unsigned int frames_per_buffer = params_buffer_size(hw_params);
118
119
guard(mutex)(&tscm->mutex);
120
err = snd_tscm_stream_reserve_duplex(tscm, rate,
121
frames_per_period, frames_per_buffer);
122
if (err >= 0)
123
++tscm->substreams_counter;
124
}
125
126
return err;
127
}
128
129
static int pcm_hw_free(struct snd_pcm_substream *substream)
130
{
131
struct snd_tscm *tscm = substream->private_data;
132
133
guard(mutex)(&tscm->mutex);
134
135
if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
136
--tscm->substreams_counter;
137
138
snd_tscm_stream_stop_duplex(tscm);
139
140
return 0;
141
}
142
143
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
144
{
145
struct snd_tscm *tscm = substream->private_data;
146
struct snd_pcm_runtime *runtime = substream->runtime;
147
int err;
148
149
guard(mutex)(&tscm->mutex);
150
151
err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
152
if (err >= 0)
153
amdtp_stream_pcm_prepare(&tscm->tx_stream);
154
155
return err;
156
}
157
158
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
159
{
160
struct snd_tscm *tscm = substream->private_data;
161
struct snd_pcm_runtime *runtime = substream->runtime;
162
int err;
163
164
guard(mutex)(&tscm->mutex);
165
166
err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
167
if (err >= 0)
168
amdtp_stream_pcm_prepare(&tscm->rx_stream);
169
170
return err;
171
}
172
173
static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
174
{
175
struct snd_tscm *tscm = substream->private_data;
176
177
switch (cmd) {
178
case SNDRV_PCM_TRIGGER_START:
179
amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
180
break;
181
case SNDRV_PCM_TRIGGER_STOP:
182
amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
183
break;
184
default:
185
return -EINVAL;
186
}
187
188
return 0;
189
}
190
191
static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
192
{
193
struct snd_tscm *tscm = substream->private_data;
194
195
switch (cmd) {
196
case SNDRV_PCM_TRIGGER_START:
197
amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
198
break;
199
case SNDRV_PCM_TRIGGER_STOP:
200
amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
201
break;
202
default:
203
return -EINVAL;
204
}
205
206
return 0;
207
}
208
209
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
210
{
211
struct snd_tscm *tscm = sbstrm->private_data;
212
213
return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream);
214
}
215
216
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
217
{
218
struct snd_tscm *tscm = sbstrm->private_data;
219
220
return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream);
221
}
222
223
static int pcm_capture_ack(struct snd_pcm_substream *substream)
224
{
225
struct snd_tscm *tscm = substream->private_data;
226
227
return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream);
228
}
229
230
static int pcm_playback_ack(struct snd_pcm_substream *substream)
231
{
232
struct snd_tscm *tscm = substream->private_data;
233
234
return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);
235
}
236
237
int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
238
{
239
static const struct snd_pcm_ops capture_ops = {
240
.open = pcm_open,
241
.close = pcm_close,
242
.hw_params = pcm_hw_params,
243
.hw_free = pcm_hw_free,
244
.prepare = pcm_capture_prepare,
245
.trigger = pcm_capture_trigger,
246
.pointer = pcm_capture_pointer,
247
.ack = pcm_capture_ack,
248
};
249
static const struct snd_pcm_ops playback_ops = {
250
.open = pcm_open,
251
.close = pcm_close,
252
.hw_params = pcm_hw_params,
253
.hw_free = pcm_hw_free,
254
.prepare = pcm_playback_prepare,
255
.trigger = pcm_playback_trigger,
256
.pointer = pcm_playback_pointer,
257
.ack = pcm_playback_ack,
258
};
259
struct snd_pcm *pcm;
260
int err;
261
262
err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
263
if (err < 0)
264
return err;
265
266
pcm->private_data = tscm;
267
pcm->nonatomic = true;
268
snprintf(pcm->name, sizeof(pcm->name),
269
"%s PCM", tscm->card->shortname);
270
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
271
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
272
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
273
274
return 0;
275
}
276
277