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(unit: dg00x->unit, TCODE_READ_QUADLET_REQUEST, |
37 | DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE, |
38 | buffer: ®, length: sizeof(reg), flags: 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(unit: dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, |
65 | DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE, |
66 | buffer: ®, length: sizeof(reg), flags: 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(unit: dg00x->unit, TCODE_READ_QUADLET_REQUEST, |
76 | DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE, |
77 | buffer: ®, length: sizeof(reg), flags: 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(unit: dg00x->unit, TCODE_READ_QUADLET_REQUEST, |
94 | DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL, |
95 | buffer: ®, length: sizeof(reg), flags: 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(unit: dg00x->unit, TCODE_READ_QUADLET_REQUEST, |
110 | DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE, |
111 | buffer: ®, length: sizeof(reg), flags: 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(unit: dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, |
131 | DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET, |
132 | buffer: &data, length: sizeof(data), flags: 0); |
133 | |
134 | // Unregister isochronous channels for both direction. |
135 | data = 0; |
136 | snd_fw_transaction(unit: dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, |
137 | DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, |
138 | buffer: &data, length: sizeof(data), flags: 0); |
139 | |
140 | // Just after finishing the session, the device may lost transmitting |
141 | // functionality for a short time. |
142 | msleep(msecs: 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(unit: dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, |
155 | DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, |
156 | buffer: &data, length: sizeof(data), flags: 0); |
157 | if (err < 0) |
158 | return err; |
159 | |
160 | err = snd_fw_transaction(unit: dg00x->unit, TCODE_READ_QUADLET_REQUEST, |
161 | DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE, |
162 | buffer: &data, length: sizeof(data), flags: 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(unit: dg00x->unit, |
174 | TCODE_WRITE_QUADLET_REQUEST, |
175 | DG00X_ADDR_BASE + |
176 | DG00X_OFFSET_STREAMING_SET, |
177 | buffer: &data, length: sizeof(data), flags: 0); |
178 | if (err < 0) |
179 | break; |
180 | |
181 | msleep(msecs: 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(s: stream, rate, |
209 | pcm_channels: snd_dg00x_stream_pcm_channels[i]); |
210 | if (err < 0) |
211 | return err; |
212 | |
213 | return fw_iso_resources_allocate(r: resources, |
214 | max_payload_bytes: amdtp_stream_get_max_payload(s: 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(r: resources, unit: dg00x->unit); |
233 | if (err < 0) |
234 | return err; |
235 | |
236 | err = amdtp_dot_init(s, unit: dg00x->unit, dir); |
237 | if (err < 0) |
238 | fw_iso_resources_destroy(r: 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(r: &dg00x->tx_resources); |
249 | else |
250 | fw_iso_resources_destroy(r: &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, s: &dg00x->rx_stream); |
258 | if (err < 0) |
259 | return err; |
260 | |
261 | err = init_stream(dg00x, s: &dg00x->tx_stream); |
262 | if (err < 0) { |
263 | destroy_stream(dg00x, s: &dg00x->rx_stream); |
264 | return err; |
265 | } |
266 | |
267 | err = amdtp_domain_init(d: &dg00x->domain); |
268 | if (err < 0) { |
269 | destroy_stream(dg00x, s: &dg00x->rx_stream); |
270 | destroy_stream(dg00x, s: &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(d: &dg00x->domain); |
283 | |
284 | destroy_stream(dg00x, s: &dg00x->rx_stream); |
285 | destroy_stream(dg00x, s: &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, rate: &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(d: &dg00x->domain); |
303 | |
304 | finish_session(dg00x); |
305 | |
306 | fw_iso_resources_free(r: &dg00x->tx_resources); |
307 | fw_iso_resources_free(r: &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, stream: &dg00x->rx_stream, rate); |
314 | if (err < 0) |
315 | return err; |
316 | |
317 | err = keep_resources(dg00x, stream: &dg00x->tx_stream, rate); |
318 | if (err < 0) { |
319 | fw_iso_resources_free(r: &dg00x->rx_resources); |
320 | return err; |
321 | } |
322 | |
323 | err = amdtp_domain_set_events_per_period(d: &dg00x->domain, |
324 | events_per_period: frames_per_period, events_per_buffer: frames_per_buffer); |
325 | if (err < 0) { |
326 | fw_iso_resources_free(r: &dg00x->rx_resources); |
327 | fw_iso_resources_free(r: &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(s: &dg00x->tx_stream) || |
344 | amdtp_streaming_error(s: &dg00x->rx_stream)) { |
345 | amdtp_domain_stop(d: &dg00x->domain); |
346 | finish_session(dg00x); |
347 | } |
348 | |
349 | if (generation != fw_parent_device(dg00x->unit)->card->generation) { |
350 | err = fw_iso_resources_update(r: &dg00x->tx_resources); |
351 | if (err < 0) |
352 | goto error; |
353 | |
354 | err = fw_iso_resources_update(r: &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(s: &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(d: &dg00x->domain, s: &dg00x->rx_stream, |
371 | channel: dg00x->rx_resources.channel, speed: spd); |
372 | if (err < 0) |
373 | goto error; |
374 | |
375 | err = amdtp_domain_add_stream(d: &dg00x->domain, s: &dg00x->tx_stream, |
376 | channel: dg00x->tx_resources.channel, speed: 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(d: &dg00x->domain, tx_init_skip_cycles: 0, replay_seq: true, replay_on_the_fly: true); |
385 | if (err < 0) |
386 | goto error; |
387 | |
388 | if (!amdtp_domain_wait_ready(d: &dg00x->domain, READY_TIMEOUT_MS)) { |
389 | err = -ETIMEDOUT; |
390 | goto error; |
391 | } |
392 | } |
393 | |
394 | return 0; |
395 | error: |
396 | amdtp_domain_stop(d: &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(d: &dg00x->domain); |
406 | finish_session(dg00x); |
407 | |
408 | fw_iso_resources_free(r: &dg00x->tx_resources); |
409 | fw_iso_resources_free(r: &dg00x->rx_resources); |
410 | } |
411 | } |
412 | |
413 | void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x) |
414 | { |
415 | fw_iso_resources_update(r: &dg00x->tx_resources); |
416 | fw_iso_resources_update(r: &dg00x->rx_resources); |
417 | |
418 | amdtp_stream_update(s: &dg00x->tx_stream); |
419 | amdtp_stream_update(s: &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(lock: &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(lock: &dg00x->lock); |
446 | return err; |
447 | } |
448 | |
449 | void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x) |
450 | { |
451 | spin_lock_irq(lock: &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(lock: &dg00x->lock); |
459 | } |
460 | |