Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/digi00x/digi00x-stream.c
26451 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
4
*
5
* Copyright (c) 2014-2015 Takashi Sakamoto
6
*/
7
8
#include "digi00x.h"
9
10
#define READY_TIMEOUT_MS 200
11
12
const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
13
[SND_DG00X_RATE_44100] = 44100,
14
[SND_DG00X_RATE_48000] = 48000,
15
[SND_DG00X_RATE_88200] = 88200,
16
[SND_DG00X_RATE_96000] = 96000,
17
};
18
19
/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
20
const unsigned int
21
snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
22
/* Analog/ADAT/SPDIF */
23
[SND_DG00X_RATE_44100] = (8 + 8 + 2),
24
[SND_DG00X_RATE_48000] = (8 + 8 + 2),
25
/* Analog/SPDIF */
26
[SND_DG00X_RATE_88200] = (8 + 2),
27
[SND_DG00X_RATE_96000] = (8 + 2),
28
};
29
30
int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
31
{
32
u32 data;
33
__be32 reg;
34
int err;
35
36
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
37
DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
38
&reg, sizeof(reg), 0);
39
if (err < 0)
40
return err;
41
42
data = be32_to_cpu(reg) & 0x0f;
43
if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
44
*rate = snd_dg00x_stream_rates[data];
45
else
46
err = -EIO;
47
48
return err;
49
}
50
51
int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
52
{
53
__be32 reg;
54
unsigned int i;
55
56
for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
57
if (rate == snd_dg00x_stream_rates[i])
58
break;
59
}
60
if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
61
return -EINVAL;
62
63
reg = cpu_to_be32(i);
64
return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
65
DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
66
&reg, sizeof(reg), 0);
67
}
68
69
int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
70
enum snd_dg00x_clock *clock)
71
{
72
__be32 reg;
73
int err;
74
75
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
76
DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
77
&reg, sizeof(reg), 0);
78
if (err < 0)
79
return err;
80
81
*clock = be32_to_cpu(reg) & 0x0f;
82
if (*clock >= SND_DG00X_CLOCK_COUNT)
83
err = -EIO;
84
85
return err;
86
}
87
88
int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
89
{
90
__be32 reg;
91
int err;
92
93
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
94
DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
95
&reg, sizeof(reg), 0);
96
if (err >= 0)
97
*detect = be32_to_cpu(reg) > 0;
98
99
return err;
100
}
101
102
int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
103
unsigned int *rate)
104
{
105
u32 data;
106
__be32 reg;
107
int err;
108
109
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
110
DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
111
&reg, sizeof(reg), 0);
112
if (err < 0)
113
return err;
114
115
data = be32_to_cpu(reg) & 0x0f;
116
if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
117
*rate = snd_dg00x_stream_rates[data];
118
/* This means desync. */
119
else
120
err = -EBUSY;
121
122
return err;
123
}
124
125
static void finish_session(struct snd_dg00x *dg00x)
126
{
127
__be32 data;
128
129
data = cpu_to_be32(0x00000003);
130
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
131
DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
132
&data, sizeof(data), 0);
133
134
// Unregister isochronous channels for both direction.
135
data = 0;
136
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
137
DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
138
&data, sizeof(data), 0);
139
140
// Just after finishing the session, the device may lost transmitting
141
// functionality for a short time.
142
msleep(50);
143
}
144
145
static int begin_session(struct snd_dg00x *dg00x)
146
{
147
__be32 data;
148
u32 curr;
149
int err;
150
151
// Register isochronous channels for both direction.
152
data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
153
dg00x->rx_resources.channel);
154
err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
155
DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
156
&data, sizeof(data), 0);
157
if (err < 0)
158
return err;
159
160
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
161
DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
162
&data, sizeof(data), 0);
163
if (err < 0)
164
return err;
165
curr = be32_to_cpu(data);
166
167
if (curr == 0)
168
curr = 2;
169
170
curr--;
171
while (curr > 0) {
172
data = cpu_to_be32(curr);
173
err = snd_fw_transaction(dg00x->unit,
174
TCODE_WRITE_QUADLET_REQUEST,
175
DG00X_ADDR_BASE +
176
DG00X_OFFSET_STREAMING_SET,
177
&data, sizeof(data), 0);
178
if (err < 0)
179
break;
180
181
msleep(20);
182
curr--;
183
}
184
185
return err;
186
}
187
188
static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
189
unsigned int rate)
190
{
191
struct fw_iso_resources *resources;
192
int i;
193
int err;
194
195
// Check sampling rate.
196
for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
197
if (snd_dg00x_stream_rates[i] == rate)
198
break;
199
}
200
if (i == SND_DG00X_RATE_COUNT)
201
return -EINVAL;
202
203
if (stream == &dg00x->tx_stream)
204
resources = &dg00x->tx_resources;
205
else
206
resources = &dg00x->rx_resources;
207
208
err = amdtp_dot_set_parameters(stream, rate,
209
snd_dg00x_stream_pcm_channels[i]);
210
if (err < 0)
211
return err;
212
213
return fw_iso_resources_allocate(resources,
214
amdtp_stream_get_max_payload(stream),
215
fw_parent_device(dg00x->unit)->max_speed);
216
}
217
218
static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
219
{
220
struct fw_iso_resources *resources;
221
enum amdtp_stream_direction dir;
222
int err;
223
224
if (s == &dg00x->tx_stream) {
225
resources = &dg00x->tx_resources;
226
dir = AMDTP_IN_STREAM;
227
} else {
228
resources = &dg00x->rx_resources;
229
dir = AMDTP_OUT_STREAM;
230
}
231
232
err = fw_iso_resources_init(resources, dg00x->unit);
233
if (err < 0)
234
return err;
235
236
err = amdtp_dot_init(s, dg00x->unit, dir);
237
if (err < 0)
238
fw_iso_resources_destroy(resources);
239
240
return err;
241
}
242
243
static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
244
{
245
amdtp_stream_destroy(s);
246
247
if (s == &dg00x->tx_stream)
248
fw_iso_resources_destroy(&dg00x->tx_resources);
249
else
250
fw_iso_resources_destroy(&dg00x->rx_resources);
251
}
252
253
int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
254
{
255
int err;
256
257
err = init_stream(dg00x, &dg00x->rx_stream);
258
if (err < 0)
259
return err;
260
261
err = init_stream(dg00x, &dg00x->tx_stream);
262
if (err < 0) {
263
destroy_stream(dg00x, &dg00x->rx_stream);
264
return err;
265
}
266
267
err = amdtp_domain_init(&dg00x->domain);
268
if (err < 0) {
269
destroy_stream(dg00x, &dg00x->rx_stream);
270
destroy_stream(dg00x, &dg00x->tx_stream);
271
}
272
273
return err;
274
}
275
276
/*
277
* This function should be called before starting streams or after stopping
278
* streams.
279
*/
280
void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
281
{
282
amdtp_domain_destroy(&dg00x->domain);
283
284
destroy_stream(dg00x, &dg00x->rx_stream);
285
destroy_stream(dg00x, &dg00x->tx_stream);
286
}
287
288
int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,
289
unsigned int frames_per_period,
290
unsigned int frames_per_buffer)
291
{
292
unsigned int curr_rate;
293
int err;
294
295
err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
296
if (err < 0)
297
return err;
298
if (rate == 0)
299
rate = curr_rate;
300
301
if (dg00x->substreams_counter == 0 || curr_rate != rate) {
302
amdtp_domain_stop(&dg00x->domain);
303
304
finish_session(dg00x);
305
306
fw_iso_resources_free(&dg00x->tx_resources);
307
fw_iso_resources_free(&dg00x->rx_resources);
308
309
err = snd_dg00x_stream_set_local_rate(dg00x, rate);
310
if (err < 0)
311
return err;
312
313
err = keep_resources(dg00x, &dg00x->rx_stream, rate);
314
if (err < 0)
315
return err;
316
317
err = keep_resources(dg00x, &dg00x->tx_stream, rate);
318
if (err < 0) {
319
fw_iso_resources_free(&dg00x->rx_resources);
320
return err;
321
}
322
323
err = amdtp_domain_set_events_per_period(&dg00x->domain,
324
frames_per_period, frames_per_buffer);
325
if (err < 0) {
326
fw_iso_resources_free(&dg00x->rx_resources);
327
fw_iso_resources_free(&dg00x->tx_resources);
328
return err;
329
}
330
}
331
332
return 0;
333
}
334
335
int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
336
{
337
unsigned int generation = dg00x->rx_resources.generation;
338
int err = 0;
339
340
if (dg00x->substreams_counter == 0)
341
return 0;
342
343
if (amdtp_streaming_error(&dg00x->tx_stream) ||
344
amdtp_streaming_error(&dg00x->rx_stream)) {
345
amdtp_domain_stop(&dg00x->domain);
346
finish_session(dg00x);
347
}
348
349
if (generation != fw_parent_device(dg00x->unit)->card->generation) {
350
err = fw_iso_resources_update(&dg00x->tx_resources);
351
if (err < 0)
352
goto error;
353
354
err = fw_iso_resources_update(&dg00x->rx_resources);
355
if (err < 0)
356
goto error;
357
}
358
359
/*
360
* No packets are transmitted without receiving packets, reagardless of
361
* which source of clock is used.
362
*/
363
if (!amdtp_stream_running(&dg00x->rx_stream)) {
364
int spd = fw_parent_device(dg00x->unit)->max_speed;
365
366
err = begin_session(dg00x);
367
if (err < 0)
368
goto error;
369
370
err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
371
dg00x->rx_resources.channel, spd);
372
if (err < 0)
373
goto error;
374
375
err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
376
dg00x->tx_resources.channel, spd);
377
if (err < 0)
378
goto error;
379
380
// NOTE: The device doesn't start packet transmission till receiving any packet.
381
// It ignores presentation time expressed by the value of syt field of CIP header
382
// in received packets. The sequence of the number of data blocks per packet is
383
// important for media clock recovery.
384
err = amdtp_domain_start(&dg00x->domain, 0, true, true);
385
if (err < 0)
386
goto error;
387
388
if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) {
389
err = -ETIMEDOUT;
390
goto error;
391
}
392
}
393
394
return 0;
395
error:
396
amdtp_domain_stop(&dg00x->domain);
397
finish_session(dg00x);
398
399
return err;
400
}
401
402
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
403
{
404
if (dg00x->substreams_counter == 0) {
405
amdtp_domain_stop(&dg00x->domain);
406
finish_session(dg00x);
407
408
fw_iso_resources_free(&dg00x->tx_resources);
409
fw_iso_resources_free(&dg00x->rx_resources);
410
}
411
}
412
413
void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
414
{
415
fw_iso_resources_update(&dg00x->tx_resources);
416
fw_iso_resources_update(&dg00x->rx_resources);
417
418
amdtp_stream_update(&dg00x->tx_stream);
419
amdtp_stream_update(&dg00x->rx_stream);
420
}
421
422
void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
423
{
424
dg00x->dev_lock_changed = true;
425
wake_up(&dg00x->hwdep_wait);
426
}
427
428
int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
429
{
430
int err;
431
432
spin_lock_irq(&dg00x->lock);
433
434
/* user land lock this */
435
if (dg00x->dev_lock_count < 0) {
436
err = -EBUSY;
437
goto end;
438
}
439
440
/* this is the first time */
441
if (dg00x->dev_lock_count++ == 0)
442
snd_dg00x_stream_lock_changed(dg00x);
443
err = 0;
444
end:
445
spin_unlock_irq(&dg00x->lock);
446
return err;
447
}
448
449
void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
450
{
451
spin_lock_irq(&dg00x->lock);
452
453
if (WARN_ON(dg00x->dev_lock_count <= 0))
454
goto end;
455
if (--dg00x->dev_lock_count == 0)
456
snd_dg00x_stream_lock_changed(dg00x);
457
end:
458
spin_unlock_irq(&dg00x->lock);
459
}
460
461