1
2
3
4
5
6
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
20const unsigned int
21snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
22
23 [SND_DG00X_RATE_44100] = (8 + 8 + 2),
24 [SND_DG00X_RATE_48000] = (8 + 8 + 2),
25
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 ®, 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 ®, 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 ®, 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 ®, 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 ®, 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
119 else
120 err = -EBUSY;
121
122 return err;
123}
124
125static void finish_session(struct snd_dg00x *dg00x)
126{
127 __be32 data;
128
129 amdtp_stream_stop(&dg00x->tx_stream);
130 amdtp_stream_stop(&dg00x->rx_stream);
131
132 data = cpu_to_be32(0x00000003);
133 snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
134 DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
135 &data, sizeof(data), 0);
136
137
138 data = 0;
139 snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
140 DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
141 &data, sizeof(data), 0);
142
143
144
145 msleep(50);
146}
147
148static int begin_session(struct snd_dg00x *dg00x)
149{
150 __be32 data;
151 u32 curr;
152 int err;
153
154
155 data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
156 dg00x->rx_resources.channel);
157 err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
158 DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
159 &data, sizeof(data), 0);
160 if (err < 0)
161 return err;
162
163 err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
164 DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
165 &data, sizeof(data), 0);
166 if (err < 0)
167 return err;
168 curr = be32_to_cpu(data);
169
170 if (curr == 0)
171 curr = 2;
172
173 curr--;
174 while (curr > 0) {
175 data = cpu_to_be32(curr);
176 err = snd_fw_transaction(dg00x->unit,
177 TCODE_WRITE_QUADLET_REQUEST,
178 DG00X_ADDR_BASE +
179 DG00X_OFFSET_STREAMING_SET,
180 &data, sizeof(data), 0);
181 if (err < 0)
182 break;
183
184 msleep(20);
185 curr--;
186 }
187
188 return err;
189}
190
191static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
192 unsigned int rate)
193{
194 struct fw_iso_resources *resources;
195 int i;
196 int err;
197
198
199 for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
200 if (snd_dg00x_stream_rates[i] == rate)
201 break;
202 }
203 if (i == SND_DG00X_RATE_COUNT)
204 return -EINVAL;
205
206 if (stream == &dg00x->tx_stream)
207 resources = &dg00x->tx_resources;
208 else
209 resources = &dg00x->rx_resources;
210
211 err = amdtp_dot_set_parameters(stream, rate,
212 snd_dg00x_stream_pcm_channels[i]);
213 if (err < 0)
214 return err;
215
216 return fw_iso_resources_allocate(resources,
217 amdtp_stream_get_max_payload(stream),
218 fw_parent_device(dg00x->unit)->max_speed);
219}
220
221int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
222{
223 int err;
224
225
226 err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
227 if (err < 0)
228 goto error;
229 err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
230 if (err < 0)
231 goto error;
232
233
234 err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
235 if (err < 0)
236 goto error;
237 err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
238 if (err < 0)
239 goto error;
240
241 return 0;
242error:
243 snd_dg00x_stream_destroy_duplex(dg00x);
244 return err;
245}
246
247
248
249
250
251void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
252{
253 amdtp_stream_destroy(&dg00x->rx_stream);
254 fw_iso_resources_destroy(&dg00x->rx_resources);
255
256 amdtp_stream_destroy(&dg00x->tx_stream);
257 fw_iso_resources_destroy(&dg00x->tx_resources);
258}
259
260int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
261{
262 unsigned int curr_rate;
263 int err;
264
265 err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
266 if (err < 0)
267 return err;
268 if (rate == 0)
269 rate = curr_rate;
270
271 if (dg00x->substreams_counter == 0 || curr_rate != rate) {
272 finish_session(dg00x);
273
274 fw_iso_resources_free(&dg00x->tx_resources);
275 fw_iso_resources_free(&dg00x->rx_resources);
276
277 err = snd_dg00x_stream_set_local_rate(dg00x, rate);
278 if (err < 0)
279 return err;
280
281 err = keep_resources(dg00x, &dg00x->rx_stream, rate);
282 if (err < 0)
283 return err;
284
285 err = keep_resources(dg00x, &dg00x->tx_stream, rate);
286 if (err < 0) {
287 fw_iso_resources_free(&dg00x->rx_resources);
288 return err;
289 }
290 }
291
292 return 0;
293}
294
295int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
296{
297 unsigned int generation = dg00x->rx_resources.generation;
298 int err = 0;
299
300 if (dg00x->substreams_counter == 0)
301 return 0;
302
303 if (amdtp_streaming_error(&dg00x->tx_stream) ||
304 amdtp_streaming_error(&dg00x->rx_stream))
305 finish_session(dg00x);
306
307 if (generation != fw_parent_device(dg00x->unit)->card->generation) {
308 err = fw_iso_resources_update(&dg00x->tx_resources);
309 if (err < 0)
310 goto error;
311
312 err = fw_iso_resources_update(&dg00x->rx_resources);
313 if (err < 0)
314 goto error;
315 }
316
317
318
319
320
321 if (!amdtp_stream_running(&dg00x->rx_stream)) {
322 err = begin_session(dg00x);
323 if (err < 0)
324 goto error;
325
326 err = amdtp_stream_start(&dg00x->rx_stream,
327 dg00x->rx_resources.channel,
328 fw_parent_device(dg00x->unit)->max_speed);
329 if (err < 0)
330 goto error;
331
332 if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
333 CALLBACK_TIMEOUT)) {
334 err = -ETIMEDOUT;
335 goto error;
336 }
337 }
338
339
340
341
342
343 if (!amdtp_stream_running(&dg00x->tx_stream)) {
344 err = amdtp_stream_start(&dg00x->tx_stream,
345 dg00x->tx_resources.channel,
346 fw_parent_device(dg00x->unit)->max_speed);
347 if (err < 0)
348 goto error;
349
350 if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
351 CALLBACK_TIMEOUT)) {
352 err = -ETIMEDOUT;
353 goto error;
354 }
355 }
356
357 return 0;
358error:
359 finish_session(dg00x);
360
361 return err;
362}
363
364void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
365{
366 if (dg00x->substreams_counter == 0) {
367 finish_session(dg00x);
368
369 fw_iso_resources_free(&dg00x->tx_resources);
370 fw_iso_resources_free(&dg00x->rx_resources);
371 }
372}
373
374void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
375{
376 fw_iso_resources_update(&dg00x->tx_resources);
377 fw_iso_resources_update(&dg00x->rx_resources);
378
379 amdtp_stream_update(&dg00x->tx_stream);
380 amdtp_stream_update(&dg00x->rx_stream);
381}
382
383void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
384{
385 dg00x->dev_lock_changed = true;
386 wake_up(&dg00x->hwdep_wait);
387}
388
389int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
390{
391 int err;
392
393 spin_lock_irq(&dg00x->lock);
394
395
396 if (dg00x->dev_lock_count < 0) {
397 err = -EBUSY;
398 goto end;
399 }
400
401
402 if (dg00x->dev_lock_count++ == 0)
403 snd_dg00x_stream_lock_changed(dg00x);
404 err = 0;
405end:
406 spin_unlock_irq(&dg00x->lock);
407 return err;
408}
409
410void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
411{
412 spin_lock_irq(&dg00x->lock);
413
414 if (WARN_ON(dg00x->dev_lock_count <= 0))
415 goto end;
416 if (--dg00x->dev_lock_count == 0)
417 snd_dg00x_stream_lock_changed(dg00x);
418end:
419 spin_unlock_irq(&dg00x->lock);
420}
421