Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/oxfw/oxfw-pcm.c
26442 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* oxfw_pcm.c - a part of driver for OXFW970/971 based devices
4
*
5
* Copyright (c) Clemens Ladisch <[email protected]>
6
*/
7
8
#include "oxfw.h"
9
10
static int hw_rule_rate(struct snd_pcm_hw_params *params,
11
struct snd_pcm_hw_rule *rule)
12
{
13
u8 **formats = 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
struct snd_oxfw_stream_formation formation;
22
int i, err;
23
24
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
25
if (formats[i] == NULL)
26
continue;
27
28
err = snd_oxfw_stream_parse_format(formats[i], &formation);
29
if (err < 0)
30
continue;
31
if (!snd_interval_test(c, formation.pcm))
32
continue;
33
34
t.min = min(t.min, formation.rate);
35
t.max = max(t.max, formation.rate);
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
u8 **formats = 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_oxfw_stream_formation formation;
50
int i, j, err;
51
unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0};
52
53
count = 0;
54
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
55
if (formats[i] == NULL)
56
break;
57
58
err = snd_oxfw_stream_parse_format(formats[i], &formation);
59
if (err < 0)
60
continue;
61
if (!snd_interval_test(r, formation.rate))
62
continue;
63
if (list[count] == formation.pcm)
64
continue;
65
66
for (j = 0; j < ARRAY_SIZE(list); j++) {
67
if (list[j] == formation.pcm)
68
break;
69
}
70
if (j == ARRAY_SIZE(list)) {
71
list[count] = formation.pcm;
72
if (++count == ARRAY_SIZE(list))
73
break;
74
}
75
}
76
77
return snd_interval_list(c, count, list, 0);
78
}
79
80
static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats)
81
{
82
struct snd_oxfw_stream_formation formation;
83
int i, err;
84
85
hw->channels_min = UINT_MAX;
86
hw->channels_max = 0;
87
88
hw->rate_min = UINT_MAX;
89
hw->rate_max = 0;
90
hw->rates = 0;
91
92
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
93
if (formats[i] == NULL)
94
break;
95
96
err = snd_oxfw_stream_parse_format(formats[i], &formation);
97
if (err < 0)
98
continue;
99
100
hw->channels_min = min(hw->channels_min, formation.pcm);
101
hw->channels_max = max(hw->channels_max, formation.pcm);
102
103
hw->rate_min = min(hw->rate_min, formation.rate);
104
hw->rate_max = max(hw->rate_max, formation.rate);
105
hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate);
106
}
107
}
108
109
static int init_hw_params(struct snd_oxfw *oxfw,
110
struct snd_pcm_substream *substream)
111
{
112
struct snd_pcm_runtime *runtime = substream->runtime;
113
u8 **formats;
114
struct amdtp_stream *stream;
115
int err;
116
117
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
118
runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
119
stream = &oxfw->tx_stream;
120
formats = oxfw->tx_stream_formats;
121
} else {
122
runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
123
stream = &oxfw->rx_stream;
124
formats = oxfw->rx_stream_formats;
125
}
126
127
limit_channels_and_rates(&runtime->hw, formats);
128
129
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
130
hw_rule_channels, formats,
131
SNDRV_PCM_HW_PARAM_RATE, -1);
132
if (err < 0)
133
goto end;
134
135
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
136
hw_rule_rate, formats,
137
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
138
if (err < 0)
139
goto end;
140
141
err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
142
end:
143
return err;
144
}
145
146
static int limit_to_current_params(struct snd_pcm_substream *substream)
147
{
148
struct snd_oxfw *oxfw = substream->private_data;
149
struct snd_oxfw_stream_formation formation;
150
enum avc_general_plug_dir dir;
151
int err;
152
153
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
154
dir = AVC_GENERAL_PLUG_DIR_OUT;
155
else
156
dir = AVC_GENERAL_PLUG_DIR_IN;
157
158
err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
159
if (err < 0)
160
goto end;
161
162
substream->runtime->hw.channels_min = formation.pcm;
163
substream->runtime->hw.channels_max = formation.pcm;
164
substream->runtime->hw.rate_min = formation.rate;
165
substream->runtime->hw.rate_max = formation.rate;
166
end:
167
return err;
168
}
169
170
static int pcm_open(struct snd_pcm_substream *substream)
171
{
172
struct snd_oxfw *oxfw = substream->private_data;
173
struct amdtp_domain *d = &oxfw->domain;
174
int err;
175
176
err = snd_oxfw_stream_lock_try(oxfw);
177
if (err < 0)
178
return err;
179
180
err = init_hw_params(oxfw, substream);
181
if (err < 0)
182
goto err_locked;
183
184
mutex_lock(&oxfw->mutex);
185
186
// When source of clock is not internal or any stream is reserved for
187
// transmission of PCM frames, the available sampling rate is limited
188
// at current one.
189
if (oxfw->substreams_count > 0 && d->events_per_period > 0) {
190
unsigned int frames_per_period = d->events_per_period;
191
unsigned int frames_per_buffer = d->events_per_buffer;
192
193
err = limit_to_current_params(substream);
194
if (err < 0) {
195
mutex_unlock(&oxfw->mutex);
196
goto err_locked;
197
}
198
199
if (frames_per_period > 0) {
200
err = snd_pcm_hw_constraint_minmax(substream->runtime,
201
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
202
frames_per_period, frames_per_period);
203
if (err < 0) {
204
mutex_unlock(&oxfw->mutex);
205
goto err_locked;
206
}
207
208
err = snd_pcm_hw_constraint_minmax(substream->runtime,
209
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
210
frames_per_buffer, frames_per_buffer);
211
if (err < 0) {
212
mutex_unlock(&oxfw->mutex);
213
goto err_locked;
214
}
215
}
216
}
217
218
mutex_unlock(&oxfw->mutex);
219
220
snd_pcm_set_sync(substream);
221
222
return 0;
223
err_locked:
224
snd_oxfw_stream_lock_release(oxfw);
225
return err;
226
}
227
228
static int pcm_close(struct snd_pcm_substream *substream)
229
{
230
struct snd_oxfw *oxfw = substream->private_data;
231
232
snd_oxfw_stream_lock_release(oxfw);
233
return 0;
234
}
235
236
static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
237
struct snd_pcm_hw_params *hw_params)
238
{
239
struct snd_oxfw *oxfw = substream->private_data;
240
int err = 0;
241
242
if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
243
unsigned int rate = params_rate(hw_params);
244
unsigned int channels = params_channels(hw_params);
245
unsigned int frames_per_period = params_period_size(hw_params);
246
unsigned int frames_per_buffer = params_buffer_size(hw_params);
247
248
mutex_lock(&oxfw->mutex);
249
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
250
rate, channels, frames_per_period,
251
frames_per_buffer);
252
if (err >= 0)
253
++oxfw->substreams_count;
254
mutex_unlock(&oxfw->mutex);
255
}
256
257
return err;
258
}
259
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
260
struct snd_pcm_hw_params *hw_params)
261
{
262
struct snd_oxfw *oxfw = substream->private_data;
263
int err = 0;
264
265
if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
266
unsigned int rate = params_rate(hw_params);
267
unsigned int channels = params_channels(hw_params);
268
unsigned int frames_per_period = params_period_size(hw_params);
269
unsigned int frames_per_buffer = params_buffer_size(hw_params);
270
271
mutex_lock(&oxfw->mutex);
272
err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream,
273
rate, channels, frames_per_period,
274
frames_per_buffer);
275
if (err >= 0)
276
++oxfw->substreams_count;
277
mutex_unlock(&oxfw->mutex);
278
}
279
280
return err;
281
}
282
283
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
284
{
285
struct snd_oxfw *oxfw = substream->private_data;
286
287
mutex_lock(&oxfw->mutex);
288
289
if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
290
--oxfw->substreams_count;
291
292
snd_oxfw_stream_stop_duplex(oxfw);
293
294
mutex_unlock(&oxfw->mutex);
295
296
return 0;
297
}
298
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
299
{
300
struct snd_oxfw *oxfw = substream->private_data;
301
302
mutex_lock(&oxfw->mutex);
303
304
if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
305
--oxfw->substreams_count;
306
307
snd_oxfw_stream_stop_duplex(oxfw);
308
309
mutex_unlock(&oxfw->mutex);
310
311
return 0;
312
}
313
314
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
315
{
316
struct snd_oxfw *oxfw = substream->private_data;
317
int err;
318
319
mutex_lock(&oxfw->mutex);
320
err = snd_oxfw_stream_start_duplex(oxfw);
321
mutex_unlock(&oxfw->mutex);
322
if (err < 0)
323
goto end;
324
325
amdtp_stream_pcm_prepare(&oxfw->tx_stream);
326
end:
327
return err;
328
}
329
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
330
{
331
struct snd_oxfw *oxfw = substream->private_data;
332
int err;
333
334
mutex_lock(&oxfw->mutex);
335
err = snd_oxfw_stream_start_duplex(oxfw);
336
mutex_unlock(&oxfw->mutex);
337
if (err < 0)
338
goto end;
339
340
amdtp_stream_pcm_prepare(&oxfw->rx_stream);
341
end:
342
return err;
343
}
344
345
static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
346
{
347
struct snd_oxfw *oxfw = substream->private_data;
348
struct snd_pcm_substream *pcm;
349
350
switch (cmd) {
351
case SNDRV_PCM_TRIGGER_START:
352
pcm = substream;
353
break;
354
case SNDRV_PCM_TRIGGER_STOP:
355
pcm = NULL;
356
break;
357
default:
358
return -EINVAL;
359
}
360
amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm);
361
return 0;
362
}
363
static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
364
{
365
struct snd_oxfw *oxfw = substream->private_data;
366
struct snd_pcm_substream *pcm;
367
368
switch (cmd) {
369
case SNDRV_PCM_TRIGGER_START:
370
pcm = substream;
371
break;
372
case SNDRV_PCM_TRIGGER_STOP:
373
pcm = NULL;
374
break;
375
default:
376
return -EINVAL;
377
}
378
amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm);
379
return 0;
380
}
381
382
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
383
{
384
struct snd_oxfw *oxfw = sbstm->private_data;
385
386
return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream);
387
}
388
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
389
{
390
struct snd_oxfw *oxfw = sbstm->private_data;
391
392
return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream);
393
}
394
395
static int pcm_capture_ack(struct snd_pcm_substream *substream)
396
{
397
struct snd_oxfw *oxfw = substream->private_data;
398
399
return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream);
400
}
401
402
static int pcm_playback_ack(struct snd_pcm_substream *substream)
403
{
404
struct snd_oxfw *oxfw = substream->private_data;
405
406
return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream);
407
}
408
409
int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
410
{
411
static const struct snd_pcm_ops capture_ops = {
412
.open = pcm_open,
413
.close = pcm_close,
414
.hw_params = pcm_capture_hw_params,
415
.hw_free = pcm_capture_hw_free,
416
.prepare = pcm_capture_prepare,
417
.trigger = pcm_capture_trigger,
418
.pointer = pcm_capture_pointer,
419
.ack = pcm_capture_ack,
420
};
421
static const struct snd_pcm_ops playback_ops = {
422
.open = pcm_open,
423
.close = pcm_close,
424
.hw_params = pcm_playback_hw_params,
425
.hw_free = pcm_playback_hw_free,
426
.prepare = pcm_playback_prepare,
427
.trigger = pcm_playback_trigger,
428
.pointer = pcm_playback_pointer,
429
.ack = pcm_playback_ack,
430
};
431
struct snd_pcm *pcm;
432
unsigned int cap = 0;
433
int err;
434
435
if (oxfw->has_output)
436
cap = 1;
437
438
err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);
439
if (err < 0)
440
return err;
441
442
pcm->private_data = oxfw;
443
pcm->nonatomic = true;
444
strscpy(pcm->name, oxfw->card->shortname);
445
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
446
if (cap > 0)
447
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
448
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
449
450
return 0;
451
}
452
453