Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/tascam/tascam-pcm.c
26424 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
mutex_lock(&tscm->mutex);
63
64
// When source of clock is not internal or any stream is reserved for
65
// transmission of PCM frames, the available sampling rate is limited
66
// at current one.
67
if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) {
68
unsigned int frames_per_period = d->events_per_period;
69
unsigned int frames_per_buffer = d->events_per_buffer;
70
unsigned int rate;
71
72
err = snd_tscm_stream_get_rate(tscm, &rate);
73
if (err < 0) {
74
mutex_unlock(&tscm->mutex);
75
goto err_locked;
76
}
77
substream->runtime->hw.rate_min = rate;
78
substream->runtime->hw.rate_max = rate;
79
80
err = snd_pcm_hw_constraint_minmax(substream->runtime,
81
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
82
frames_per_period, frames_per_period);
83
if (err < 0) {
84
mutex_unlock(&tscm->mutex);
85
goto err_locked;
86
}
87
88
err = snd_pcm_hw_constraint_minmax(substream->runtime,
89
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
90
frames_per_buffer, frames_per_buffer);
91
if (err < 0) {
92
mutex_unlock(&tscm->mutex);
93
goto err_locked;
94
}
95
}
96
97
mutex_unlock(&tscm->mutex);
98
99
snd_pcm_set_sync(substream);
100
101
return 0;
102
err_locked:
103
snd_tscm_stream_lock_release(tscm);
104
return err;
105
}
106
107
static int pcm_close(struct snd_pcm_substream *substream)
108
{
109
struct snd_tscm *tscm = substream->private_data;
110
111
snd_tscm_stream_lock_release(tscm);
112
113
return 0;
114
}
115
116
static int pcm_hw_params(struct snd_pcm_substream *substream,
117
struct snd_pcm_hw_params *hw_params)
118
{
119
struct snd_tscm *tscm = substream->private_data;
120
int err = 0;
121
122
if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
123
unsigned int rate = params_rate(hw_params);
124
unsigned int frames_per_period = params_period_size(hw_params);
125
unsigned int frames_per_buffer = params_buffer_size(hw_params);
126
127
mutex_lock(&tscm->mutex);
128
err = snd_tscm_stream_reserve_duplex(tscm, rate,
129
frames_per_period, frames_per_buffer);
130
if (err >= 0)
131
++tscm->substreams_counter;
132
mutex_unlock(&tscm->mutex);
133
}
134
135
return err;
136
}
137
138
static int pcm_hw_free(struct snd_pcm_substream *substream)
139
{
140
struct snd_tscm *tscm = substream->private_data;
141
142
mutex_lock(&tscm->mutex);
143
144
if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
145
--tscm->substreams_counter;
146
147
snd_tscm_stream_stop_duplex(tscm);
148
149
mutex_unlock(&tscm->mutex);
150
151
return 0;
152
}
153
154
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
155
{
156
struct snd_tscm *tscm = substream->private_data;
157
struct snd_pcm_runtime *runtime = substream->runtime;
158
int err;
159
160
mutex_lock(&tscm->mutex);
161
162
err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
163
if (err >= 0)
164
amdtp_stream_pcm_prepare(&tscm->tx_stream);
165
166
mutex_unlock(&tscm->mutex);
167
168
return err;
169
}
170
171
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
172
{
173
struct snd_tscm *tscm = substream->private_data;
174
struct snd_pcm_runtime *runtime = substream->runtime;
175
int err;
176
177
mutex_lock(&tscm->mutex);
178
179
err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
180
if (err >= 0)
181
amdtp_stream_pcm_prepare(&tscm->rx_stream);
182
183
mutex_unlock(&tscm->mutex);
184
185
return err;
186
}
187
188
static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
189
{
190
struct snd_tscm *tscm = substream->private_data;
191
192
switch (cmd) {
193
case SNDRV_PCM_TRIGGER_START:
194
amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
195
break;
196
case SNDRV_PCM_TRIGGER_STOP:
197
amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
198
break;
199
default:
200
return -EINVAL;
201
}
202
203
return 0;
204
}
205
206
static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
207
{
208
struct snd_tscm *tscm = substream->private_data;
209
210
switch (cmd) {
211
case SNDRV_PCM_TRIGGER_START:
212
amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
213
break;
214
case SNDRV_PCM_TRIGGER_STOP:
215
amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
216
break;
217
default:
218
return -EINVAL;
219
}
220
221
return 0;
222
}
223
224
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
225
{
226
struct snd_tscm *tscm = sbstrm->private_data;
227
228
return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream);
229
}
230
231
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
232
{
233
struct snd_tscm *tscm = sbstrm->private_data;
234
235
return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream);
236
}
237
238
static int pcm_capture_ack(struct snd_pcm_substream *substream)
239
{
240
struct snd_tscm *tscm = substream->private_data;
241
242
return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream);
243
}
244
245
static int pcm_playback_ack(struct snd_pcm_substream *substream)
246
{
247
struct snd_tscm *tscm = substream->private_data;
248
249
return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);
250
}
251
252
int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
253
{
254
static const struct snd_pcm_ops capture_ops = {
255
.open = pcm_open,
256
.close = pcm_close,
257
.hw_params = pcm_hw_params,
258
.hw_free = pcm_hw_free,
259
.prepare = pcm_capture_prepare,
260
.trigger = pcm_capture_trigger,
261
.pointer = pcm_capture_pointer,
262
.ack = pcm_capture_ack,
263
};
264
static const struct snd_pcm_ops playback_ops = {
265
.open = pcm_open,
266
.close = pcm_close,
267
.hw_params = pcm_hw_params,
268
.hw_free = pcm_hw_free,
269
.prepare = pcm_playback_prepare,
270
.trigger = pcm_playback_trigger,
271
.pointer = pcm_playback_pointer,
272
.ack = pcm_playback_ack,
273
};
274
struct snd_pcm *pcm;
275
int err;
276
277
err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
278
if (err < 0)
279
return err;
280
281
pcm->private_data = tscm;
282
pcm->nonatomic = true;
283
snprintf(pcm->name, sizeof(pcm->name),
284
"%s PCM", tscm->card->shortname);
285
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
286
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
287
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
288
289
return 0;
290
}
291
292