blob: 405d6903bfbc3d0eb1e5ba3c52aa1ef4afac6b5b [file] [log] [blame]
Thomas Gleixnerda607e12019-05-29 16:57:59 -07001// SPDX-License-Identifier: GPL-2.0-only
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +09002/*
3 * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
4 *
5 * Copyright (c) 2014-2015 Takashi Sakamoto
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +09006 */
7
8#include "digi00x.h"
9
10#define CALLBACK_TIMEOUT 500
11
12const 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. */
20const unsigned int
21snd_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
30int 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
51int 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
69int 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
88int 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
102int 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
125static void finish_session(struct snd_dg00x *dg00x)
126{
Takashi Sakamotod18b0a62019-06-11 22:21:08 +0900127 __be32 data;
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900128
Takashi Sakamotod18b0a62019-06-11 22:21:08 +0900129 data = cpu_to_be32(0x00000003);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900130 snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
131 DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
132 &data, sizeof(data), 0);
Takashi Sakamoto6bc93222019-06-11 22:21:07 +0900133
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);
Takashi Sakamotod18b0a62019-06-11 22:21:08 +0900139
140 // Just after finishing the session, the device may lost transmitting
141 // functionality for a short time.
142 msleep(50);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900143}
144
145static int begin_session(struct snd_dg00x *dg00x)
146{
147 __be32 data;
148 u32 curr;
149 int err;
150
Takashi Sakamoto6bc93222019-06-11 22:21:07 +0900151 // 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)
Takashi Sakamoto638e19f2019-06-11 22:21:09 +0900158 return err;
Takashi Sakamoto6bc93222019-06-11 22:21:07 +0900159
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900160 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)
Takashi Sakamoto638e19f2019-06-11 22:21:09 +0900164 return err;
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900165 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)
Takashi Sakamoto638e19f2019-06-11 22:21:09 +0900179 break;
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900180
181 msleep(20);
182 curr--;
183 }
184
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900185 return err;
186}
187
Takashi Sakamotoad306502019-06-11 22:21:10 +0900188static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
189 unsigned int rate)
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900190{
Takashi Sakamotoad306502019-06-11 22:21:10 +0900191 struct fw_iso_resources *resources;
192 int i;
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900193 int err;
194
Takashi Sakamotoad306502019-06-11 22:21:10 +0900195 // Check sampling rate.
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900196 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
Takashi Sakamotoad306502019-06-11 22:21:10 +0900203 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,
Takashi Sakamoto9dc5d312015-10-11 12:30:15 +0900209 snd_dg00x_stream_pcm_channels[i]);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900210 if (err < 0)
211 return err;
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900212
Takashi Sakamotoad306502019-06-11 22:21:10 +0900213 return fw_iso_resources_allocate(resources,
214 amdtp_stream_get_max_payload(stream),
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900215 fw_parent_device(dg00x->unit)->max_speed);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900216}
217
Takashi Sakamotod79360e2019-08-04 15:21:26 +0900218static 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
243static 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
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900253int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
254{
255 int err;
256
Takashi Sakamotod79360e2019-08-04 15:21:26 +0900257 err = init_stream(dg00x, &dg00x->rx_stream);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900258 if (err < 0)
Takashi Sakamotod79360e2019-08-04 15:21:26 +0900259 return err;
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900260
Takashi Sakamotod79360e2019-08-04 15:21:26 +0900261 err = init_stream(dg00x, &dg00x->tx_stream);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900262 if (err < 0)
Takashi Sakamotod79360e2019-08-04 15:21:26 +0900263 destroy_stream(dg00x, &dg00x->rx_stream);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900264
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900265 err = amdtp_domain_init(&dg00x->domain);
266 if (err < 0) {
267 destroy_stream(dg00x, &dg00x->rx_stream);
268 destroy_stream(dg00x, &dg00x->tx_stream);
269 }
270
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900271 return err;
272}
273
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900274/*
275 * This function should be called before starting streams or after stopping
276 * streams.
277 */
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900278void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
279{
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900280 amdtp_domain_destroy(&dg00x->domain);
281
Takashi Sakamotod79360e2019-08-04 15:21:26 +0900282 destroy_stream(dg00x, &dg00x->rx_stream);
283 destroy_stream(dg00x, &dg00x->tx_stream);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900284}
285
Takashi Sakamoto18b7f182019-10-07 20:05:21 +0900286int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,
Takashi Sakamoto76c4ecb2019-10-18 00:54:18 +0900287 unsigned int frames_per_period,
288 unsigned int frames_per_buffer)
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900289{
290 unsigned int curr_rate;
Takashi Sakamotoae8ffbb2019-06-11 22:21:11 +0900291 int err;
292
293 err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
294 if (err < 0)
295 return err;
296 if (rate == 0)
297 rate = curr_rate;
298
299 if (dg00x->substreams_counter == 0 || curr_rate != rate) {
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900300 amdtp_domain_stop(&dg00x->domain);
301
Takashi Sakamotoae8ffbb2019-06-11 22:21:11 +0900302 finish_session(dg00x);
303
304 fw_iso_resources_free(&dg00x->tx_resources);
305 fw_iso_resources_free(&dg00x->rx_resources);
306
307 err = snd_dg00x_stream_set_local_rate(dg00x, rate);
308 if (err < 0)
309 return err;
310
311 err = keep_resources(dg00x, &dg00x->rx_stream, rate);
312 if (err < 0)
313 return err;
314
315 err = keep_resources(dg00x, &dg00x->tx_stream, rate);
316 if (err < 0) {
317 fw_iso_resources_free(&dg00x->rx_resources);
318 return err;
319 }
Takashi Sakamoto18b7f182019-10-07 20:05:21 +0900320
321 err = amdtp_domain_set_events_per_period(&dg00x->domain,
Takashi Sakamoto76c4ecb2019-10-18 00:54:18 +0900322 frames_per_period, frames_per_buffer);
Takashi Sakamoto18b7f182019-10-07 20:05:21 +0900323 if (err < 0) {
324 fw_iso_resources_free(&dg00x->rx_resources);
325 fw_iso_resources_free(&dg00x->tx_resources);
326 return err;
327 }
Takashi Sakamotoae8ffbb2019-06-11 22:21:11 +0900328 }
329
330 return 0;
331}
332
Takashi Sakamotoae8ffbb2019-06-11 22:21:11 +0900333int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
334{
Takashi Sakamoto24bb77b2019-06-11 22:21:12 +0900335 unsigned int generation = dg00x->rx_resources.generation;
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900336 int err = 0;
337
338 if (dg00x->substreams_counter == 0)
Takashi Sakamotoae8ffbb2019-06-11 22:21:11 +0900339 return 0;
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900340
Takashi Sakamotoae8ffbb2019-06-11 22:21:11 +0900341 if (amdtp_streaming_error(&dg00x->tx_stream) ||
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900342 amdtp_streaming_error(&dg00x->rx_stream)) {
343 amdtp_domain_stop(&dg00x->domain);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900344 finish_session(dg00x);
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900345 }
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900346
Takashi Sakamoto24bb77b2019-06-11 22:21:12 +0900347 if (generation != fw_parent_device(dg00x->unit)->card->generation) {
348 err = fw_iso_resources_update(&dg00x->tx_resources);
349 if (err < 0)
350 goto error;
351
352 err = fw_iso_resources_update(&dg00x->rx_resources);
353 if (err < 0)
354 goto error;
355 }
356
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900357 /*
358 * No packets are transmitted without receiving packets, reagardless of
359 * which source of clock is used.
360 */
361 if (!amdtp_stream_running(&dg00x->rx_stream)) {
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900362 int spd = fw_parent_device(dg00x->unit)->max_speed;
363
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900364 err = begin_session(dg00x);
365 if (err < 0)
366 goto error;
367
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900368 err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
369 dg00x->rx_resources.channel, spd);
370 if (err < 0)
371 goto error;
372
373 err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
374 dg00x->tx_resources.channel, spd);
375 if (err < 0)
376 goto error;
377
Takashi Sakamotoacfedcb2019-10-18 15:19:11 +0900378 err = amdtp_domain_start(&dg00x->domain, 0);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900379 if (err < 0)
380 goto error;
381
382 if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900383 CALLBACK_TIMEOUT) ||
384 !amdtp_stream_wait_callback(&dg00x->tx_stream,
385 CALLBACK_TIMEOUT)) {
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900386 err = -ETIMEDOUT;
387 goto error;
388 }
389 }
Takashi Sakamotoae8ffbb2019-06-11 22:21:11 +0900390
391 return 0;
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900392error:
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900393 amdtp_domain_stop(&dg00x->domain);
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900394 finish_session(dg00x);
395
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900396 return err;
397}
398
399void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
400{
Takashi Sakamotob9434542019-06-18 22:26:18 +0900401 if (dg00x->substreams_counter == 0) {
Takashi Sakamoto9a080672019-08-04 15:21:34 +0900402 amdtp_domain_stop(&dg00x->domain);
Takashi Sakamotoae8ffbb2019-06-11 22:21:11 +0900403 finish_session(dg00x);
Takashi Sakamotob9434542019-06-18 22:26:18 +0900404
405 fw_iso_resources_free(&dg00x->tx_resources);
406 fw_iso_resources_free(&dg00x->rx_resources);
407 }
Takashi Sakamoto3a2a1792015-09-30 09:39:18 +0900408}
409
410void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
411{
412 fw_iso_resources_update(&dg00x->tx_resources);
413 fw_iso_resources_update(&dg00x->rx_resources);
414
415 amdtp_stream_update(&dg00x->tx_stream);
416 amdtp_stream_update(&dg00x->rx_stream);
417}
Takashi Sakamoto660dd3d2015-09-30 09:39:21 +0900418
419void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
420{
421 dg00x->dev_lock_changed = true;
422 wake_up(&dg00x->hwdep_wait);
423}
424
425int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
426{
427 int err;
428
429 spin_lock_irq(&dg00x->lock);
430
431 /* user land lock this */
432 if (dg00x->dev_lock_count < 0) {
433 err = -EBUSY;
434 goto end;
435 }
436
437 /* this is the first time */
438 if (dg00x->dev_lock_count++ == 0)
439 snd_dg00x_stream_lock_changed(dg00x);
440 err = 0;
441end:
442 spin_unlock_irq(&dg00x->lock);
443 return err;
444}
445
446void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
447{
448 spin_lock_irq(&dg00x->lock);
449
450 if (WARN_ON(dg00x->dev_lock_count <= 0))
451 goto end;
452 if (--dg00x->dev_lock_count == 0)
453 snd_dg00x_stream_lock_changed(dg00x);
454end:
455 spin_unlock_irq(&dg00x->lock);
456}