Commit | Line | Data |
---|---|---|
e2786ca6 TS |
1 | /* |
2 | * oxfw_stream.c - a part of driver for OXFW970/971 based devices | |
3 | * | |
4 | * Copyright (c) 2014 Takashi Sakamoto | |
5 | * | |
6 | * Licensed under the terms of the GNU General Public License, version 2. | |
7 | */ | |
8 | ||
9 | #include "oxfw.h" | |
f3699e2c | 10 | #include <linux/delay.h> |
e2786ca6 | 11 | |
5cd1d3f4 | 12 | #define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512 |
f3699e2c | 13 | #define CALLBACK_TIMEOUT 200 |
5cd1d3f4 TS |
14 | |
15 | /* | |
16 | * According to datasheet of Oxford Semiconductor: | |
17 | * OXFW970: 32.0/44.1/48.0/96.0 Khz, 8 audio channels I/O | |
18 | * OXFW971: 32.0/44.1/48.0/88.2/96.0/192.0 kHz, 16 audio channels I/O, MIDI I/O | |
19 | */ | |
20 | static const unsigned int oxfw_rate_table[] = { | |
21 | [0] = 32000, | |
22 | [1] = 44100, | |
23 | [2] = 48000, | |
24 | [3] = 88200, | |
25 | [4] = 96000, | |
26 | [5] = 192000, | |
27 | }; | |
28 | ||
29 | /* | |
30 | * See Table 5.7 – Sampling frequency for Multi-bit Audio | |
31 | * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA) | |
32 | */ | |
33 | static const unsigned int avc_stream_rate_table[] = { | |
34 | [0] = 0x02, | |
35 | [1] = 0x03, | |
36 | [2] = 0x04, | |
37 | [3] = 0x0a, | |
38 | [4] = 0x05, | |
39 | [5] = 0x07, | |
40 | }; | |
41 | ||
b0ac0009 TS |
42 | static int set_rate(struct snd_oxfw *oxfw, unsigned int rate) |
43 | { | |
44 | int err; | |
45 | ||
46 | err = avc_general_set_sig_fmt(oxfw->unit, rate, | |
47 | AVC_GENERAL_PLUG_DIR_IN, 0); | |
48 | if (err < 0) | |
49 | goto end; | |
50 | ||
51 | if (oxfw->has_output) | |
52 | err = avc_general_set_sig_fmt(oxfw->unit, rate, | |
53 | AVC_GENERAL_PLUG_DIR_OUT, 0); | |
54 | end: | |
55 | return err; | |
56 | } | |
57 | ||
f3699e2c TS |
58 | static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s, |
59 | unsigned int rate, unsigned int pcm_channels) | |
60 | { | |
61 | u8 **formats; | |
62 | struct snd_oxfw_stream_formation formation; | |
63 | enum avc_general_plug_dir dir; | |
5580ba7b DC |
64 | unsigned int len; |
65 | int i, err; | |
f3699e2c | 66 | |
b0ac0009 TS |
67 | if (s == &oxfw->tx_stream) { |
68 | formats = oxfw->tx_stream_formats; | |
69 | dir = AVC_GENERAL_PLUG_DIR_OUT; | |
70 | } else { | |
71 | formats = oxfw->rx_stream_formats; | |
72 | dir = AVC_GENERAL_PLUG_DIR_IN; | |
73 | } | |
f3699e2c TS |
74 | |
75 | /* Seek stream format for requirements. */ | |
76 | for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { | |
77 | err = snd_oxfw_stream_parse_format(formats[i], &formation); | |
78 | if (err < 0) | |
79 | return err; | |
80 | ||
81 | if ((formation.rate == rate) && (formation.pcm == pcm_channels)) | |
82 | break; | |
83 | } | |
84 | if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) | |
85 | return -EINVAL; | |
86 | ||
87 | /* If assumed, just change rate. */ | |
88 | if (oxfw->assumed) | |
b0ac0009 | 89 | return set_rate(oxfw, rate); |
f3699e2c TS |
90 | |
91 | /* Calculate format length. */ | |
92 | len = 5 + formats[i][4] * 2; | |
93 | ||
94 | err = avc_stream_set_format(oxfw->unit, dir, 0, formats[i], len); | |
95 | if (err < 0) | |
96 | return err; | |
97 | ||
98 | /* Some requests just after changing format causes freezing. */ | |
99 | msleep(100); | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
b0ac0009 | 104 | static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) |
e2786ca6 | 105 | { |
b0ac0009 TS |
106 | amdtp_stream_pcm_abort(stream); |
107 | amdtp_stream_stop(stream); | |
e2786ca6 | 108 | |
b0ac0009 TS |
109 | if (stream == &oxfw->tx_stream) |
110 | cmp_connection_break(&oxfw->out_conn); | |
111 | else | |
112 | cmp_connection_break(&oxfw->in_conn); | |
e2786ca6 TS |
113 | } |
114 | ||
b0ac0009 TS |
115 | static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, |
116 | unsigned int rate, unsigned int pcm_channels) | |
e2786ca6 | 117 | { |
f3699e2c TS |
118 | u8 **formats; |
119 | struct cmp_connection *conn; | |
120 | struct snd_oxfw_stream_formation formation; | |
121 | unsigned int i, midi_ports; | |
f3699e2c | 122 | int err; |
e2786ca6 | 123 | |
b0ac0009 TS |
124 | if (stream == &oxfw->rx_stream) { |
125 | formats = oxfw->rx_stream_formats; | |
126 | conn = &oxfw->in_conn; | |
127 | } else { | |
128 | formats = oxfw->tx_stream_formats; | |
129 | conn = &oxfw->out_conn; | |
130 | } | |
f3699e2c | 131 | |
b0ac0009 | 132 | /* Get stream format */ |
f3699e2c TS |
133 | for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { |
134 | if (formats[i] == NULL) | |
135 | break; | |
136 | ||
137 | err = snd_oxfw_stream_parse_format(formats[i], &formation); | |
138 | if (err < 0) | |
139 | goto end; | |
140 | if (rate != formation.rate) | |
141 | continue; | |
142 | if (pcm_channels == 0 || pcm_channels == formation.pcm) | |
143 | break; | |
144 | } | |
145 | if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) { | |
146 | err = -EINVAL; | |
147 | goto end; | |
148 | } | |
149 | ||
150 | pcm_channels = formation.pcm; | |
bb71da43 | 151 | midi_ports = formation.midi * 8; |
e2786ca6 | 152 | |
f3699e2c TS |
153 | /* The stream should have one pcm channels at least */ |
154 | if (pcm_channels == 0) { | |
155 | err = -EINVAL; | |
e2786ca6 | 156 | goto end; |
f3699e2c | 157 | } |
51c29fd2 TS |
158 | err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports, |
159 | false); | |
547e631c TS |
160 | if (err < 0) |
161 | goto end; | |
e2786ca6 | 162 | |
f3699e2c TS |
163 | err = cmp_connection_establish(conn, |
164 | amdtp_stream_get_max_payload(stream)); | |
e2786ca6 TS |
165 | if (err < 0) |
166 | goto end; | |
167 | ||
f3699e2c TS |
168 | err = amdtp_stream_start(stream, |
169 | conn->resources.channel, | |
170 | conn->speed); | |
171 | if (err < 0) { | |
172 | cmp_connection_break(conn); | |
173 | goto end; | |
174 | } | |
175 | ||
176 | /* Wait first packet */ | |
f2b14c0b | 177 | if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { |
b0ac0009 | 178 | stop_stream(oxfw, stream); |
f2b14c0b TS |
179 | err = -ETIMEDOUT; |
180 | } | |
f3699e2c TS |
181 | end: |
182 | return err; | |
183 | } | |
184 | ||
b0ac0009 TS |
185 | static int check_connection_used_by_others(struct snd_oxfw *oxfw, |
186 | struct amdtp_stream *stream) | |
f3699e2c | 187 | { |
b0ac0009 TS |
188 | struct cmp_connection *conn; |
189 | bool used; | |
190 | int err; | |
191 | ||
192 | if (stream == &oxfw->tx_stream) | |
193 | conn = &oxfw->out_conn; | |
194 | else | |
195 | conn = &oxfw->in_conn; | |
196 | ||
197 | err = cmp_connection_check_used(conn, &used); | |
198 | if ((err >= 0) && used && !amdtp_stream_running(stream)) { | |
199 | dev_err(&oxfw->unit->device, | |
200 | "Connection established by others: %cPCR[%d]\n", | |
201 | (conn->direction == CMP_OUTPUT) ? 'o' : 'i', | |
202 | conn->pcr_index); | |
203 | err = -EBUSY; | |
204 | } | |
205 | ||
206 | return err; | |
207 | } | |
208 | ||
209 | int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, | |
210 | struct amdtp_stream *stream) | |
211 | { | |
212 | struct cmp_connection *conn; | |
213 | enum cmp_direction c_dir; | |
214 | enum amdtp_stream_direction s_dir; | |
215 | int err; | |
216 | ||
217 | if (stream == &oxfw->tx_stream) { | |
218 | conn = &oxfw->out_conn; | |
219 | c_dir = CMP_OUTPUT; | |
220 | s_dir = AMDTP_IN_STREAM; | |
221 | } else { | |
222 | conn = &oxfw->in_conn; | |
223 | c_dir = CMP_INPUT; | |
224 | s_dir = AMDTP_OUT_STREAM; | |
225 | } | |
226 | ||
227 | err = cmp_connection_init(conn, oxfw->unit, c_dir, 0); | |
228 | if (err < 0) | |
229 | goto end; | |
230 | ||
5955815e | 231 | err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING); |
b0ac0009 TS |
232 | if (err < 0) { |
233 | amdtp_stream_destroy(stream); | |
234 | cmp_connection_destroy(conn); | |
235 | goto end; | |
236 | } | |
237 | ||
a2064710 TS |
238 | /* |
239 | * OXFW starts to transmit packets with non-zero dbc. | |
240 | * OXFW postpone transferring packets till handling any asynchronous | |
241 | * packets. As a result, next isochronous packet includes more data | |
242 | * blocks than IEC 61883-6 defines. | |
243 | */ | |
13f3a46d | 244 | if (stream == &oxfw->tx_stream) { |
62f00e40 | 245 | oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD; |
13f3a46d TS |
246 | if (oxfw->wrong_dbs) |
247 | oxfw->tx_stream.flags |= CIP_WRONG_DBS; | |
248 | } | |
b0ac0009 TS |
249 | end: |
250 | return err; | |
251 | } | |
252 | ||
253 | int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, | |
254 | struct amdtp_stream *stream, | |
255 | unsigned int rate, unsigned int pcm_channels) | |
256 | { | |
257 | struct amdtp_stream *opposite; | |
f3699e2c | 258 | struct snd_oxfw_stream_formation formation; |
b0ac0009 TS |
259 | enum avc_general_plug_dir dir; |
260 | unsigned int substreams, opposite_substreams; | |
f3699e2c TS |
261 | int err = 0; |
262 | ||
b0ac0009 TS |
263 | if (stream == &oxfw->tx_stream) { |
264 | substreams = oxfw->capture_substreams; | |
265 | opposite = &oxfw->rx_stream; | |
266 | opposite_substreams = oxfw->playback_substreams; | |
267 | dir = AVC_GENERAL_PLUG_DIR_OUT; | |
268 | } else { | |
269 | substreams = oxfw->playback_substreams; | |
270 | opposite_substreams = oxfw->capture_substreams; | |
271 | ||
272 | if (oxfw->has_output) | |
273 | opposite = &oxfw->rx_stream; | |
274 | else | |
275 | opposite = NULL; | |
276 | ||
277 | dir = AVC_GENERAL_PLUG_DIR_IN; | |
278 | } | |
279 | ||
280 | if (substreams == 0) | |
281 | goto end; | |
282 | ||
283 | /* | |
284 | * Considering JACK/FFADO streaming: | |
285 | * TODO: This can be removed hwdep functionality becomes popular. | |
286 | */ | |
287 | err = check_connection_used_by_others(oxfw, stream); | |
288 | if (err < 0) | |
289 | goto end; | |
290 | ||
f3699e2c | 291 | /* packet queueing error */ |
b0ac0009 TS |
292 | if (amdtp_streaming_error(stream)) |
293 | stop_stream(oxfw, stream); | |
f3699e2c | 294 | |
b0ac0009 | 295 | err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); |
e2786ca6 | 296 | if (err < 0) |
f3699e2c | 297 | goto end; |
05588d34 TS |
298 | if (rate == 0) |
299 | rate = formation.rate; | |
300 | if (pcm_channels == 0) | |
301 | pcm_channels = formation.pcm; | |
f3699e2c TS |
302 | |
303 | if ((formation.rate != rate) || (formation.pcm != pcm_channels)) { | |
b0ac0009 TS |
304 | if (opposite != NULL) { |
305 | err = check_connection_used_by_others(oxfw, opposite); | |
306 | if (err < 0) | |
307 | goto end; | |
308 | stop_stream(oxfw, opposite); | |
309 | } | |
310 | stop_stream(oxfw, stream); | |
f3699e2c | 311 | |
b0ac0009 | 312 | err = set_stream_format(oxfw, stream, rate, pcm_channels); |
f3699e2c TS |
313 | if (err < 0) { |
314 | dev_err(&oxfw->unit->device, | |
315 | "fail to set stream format: %d\n", err); | |
316 | goto end; | |
317 | } | |
b0ac0009 TS |
318 | |
319 | /* Start opposite stream if needed. */ | |
320 | if (opposite && !amdtp_stream_running(opposite) && | |
321 | (opposite_substreams > 0)) { | |
322 | err = start_stream(oxfw, opposite, rate, 0); | |
323 | if (err < 0) { | |
324 | dev_err(&oxfw->unit->device, | |
325 | "fail to restart stream: %d\n", err); | |
326 | goto end; | |
327 | } | |
328 | } | |
f3699e2c TS |
329 | } |
330 | ||
b0ac0009 TS |
331 | /* Start requested stream. */ |
332 | if (!amdtp_stream_running(stream)) { | |
333 | err = start_stream(oxfw, stream, rate, pcm_channels); | |
334 | if (err < 0) | |
335 | dev_err(&oxfw->unit->device, | |
336 | "fail to start stream: %d\n", err); | |
337 | } | |
e2786ca6 TS |
338 | end: |
339 | return err; | |
340 | } | |
341 | ||
b0ac0009 TS |
342 | void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw, |
343 | struct amdtp_stream *stream) | |
e2786ca6 | 344 | { |
b0ac0009 TS |
345 | if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) || |
346 | ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0))) | |
347 | return; | |
348 | ||
349 | stop_stream(oxfw, stream); | |
e2786ca6 TS |
350 | } |
351 | ||
d23c2cc4 TS |
352 | /* |
353 | * This function should be called before starting the stream or after stopping | |
354 | * the streams. | |
355 | */ | |
b0ac0009 TS |
356 | void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, |
357 | struct amdtp_stream *stream) | |
e2786ca6 | 358 | { |
b0ac0009 TS |
359 | struct cmp_connection *conn; |
360 | ||
361 | if (stream == &oxfw->tx_stream) | |
362 | conn = &oxfw->out_conn; | |
363 | else | |
364 | conn = &oxfw->in_conn; | |
e2786ca6 | 365 | |
b0ac0009 TS |
366 | amdtp_stream_destroy(stream); |
367 | cmp_connection_destroy(conn); | |
e2786ca6 TS |
368 | } |
369 | ||
b0ac0009 TS |
370 | void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw, |
371 | struct amdtp_stream *stream) | |
e2786ca6 | 372 | { |
b0ac0009 TS |
373 | struct cmp_connection *conn; |
374 | ||
375 | if (stream == &oxfw->tx_stream) | |
376 | conn = &oxfw->out_conn; | |
377 | else | |
378 | conn = &oxfw->in_conn; | |
379 | ||
380 | if (cmp_connection_update(conn) < 0) | |
381 | stop_stream(oxfw, stream); | |
e2786ca6 | 382 | else |
b0ac0009 | 383 | amdtp_stream_update(stream); |
e2786ca6 | 384 | } |
5cd1d3f4 | 385 | |
3c96101f TS |
386 | int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, |
387 | enum avc_general_plug_dir dir, | |
388 | struct snd_oxfw_stream_formation *formation) | |
389 | { | |
390 | u8 *format; | |
391 | unsigned int len; | |
392 | int err; | |
393 | ||
394 | len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; | |
395 | format = kmalloc(len, GFP_KERNEL); | |
396 | if (format == NULL) | |
397 | return -ENOMEM; | |
398 | ||
399 | err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len); | |
400 | if (err < 0) | |
401 | goto end; | |
402 | if (len < 3) { | |
403 | err = -EIO; | |
404 | goto end; | |
405 | } | |
406 | ||
407 | err = snd_oxfw_stream_parse_format(format, formation); | |
408 | end: | |
409 | kfree(format); | |
410 | return err; | |
411 | } | |
412 | ||
5cd1d3f4 TS |
413 | /* |
414 | * See Table 6.16 - AM824 Stream Format | |
415 | * Figure 6.19 - format_information field for AM824 Compound | |
416 | * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA) | |
417 | * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005 | |
418 | */ | |
419 | int snd_oxfw_stream_parse_format(u8 *format, | |
420 | struct snd_oxfw_stream_formation *formation) | |
421 | { | |
422 | unsigned int i, e, channels, type; | |
423 | ||
424 | memset(formation, 0, sizeof(struct snd_oxfw_stream_formation)); | |
425 | ||
426 | /* | |
427 | * this module can support a hierarchy combination that: | |
428 | * Root: Audio and Music (0x90) | |
429 | * Level 1: AM824 Compound (0x40) | |
430 | */ | |
431 | if ((format[0] != 0x90) || (format[1] != 0x40)) | |
432 | return -ENOSYS; | |
433 | ||
434 | /* check the sampling rate */ | |
435 | for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) { | |
436 | if (format[2] == avc_stream_rate_table[i]) | |
437 | break; | |
438 | } | |
439 | if (i == ARRAY_SIZE(avc_stream_rate_table)) | |
440 | return -ENOSYS; | |
441 | ||
442 | formation->rate = oxfw_rate_table[i]; | |
443 | ||
444 | for (e = 0; e < format[4]; e++) { | |
445 | channels = format[5 + e * 2]; | |
446 | type = format[6 + e * 2]; | |
447 | ||
448 | switch (type) { | |
449 | /* IEC 60958 Conformant, currently handled as MBLA */ | |
450 | case 0x00: | |
451 | /* Multi Bit Linear Audio (Raw) */ | |
452 | case 0x06: | |
453 | formation->pcm += channels; | |
454 | break; | |
455 | /* MIDI Conformant */ | |
456 | case 0x0d: | |
457 | formation->midi = channels; | |
458 | break; | |
459 | /* IEC 61937-3 to 7 */ | |
460 | case 0x01: | |
461 | case 0x02: | |
462 | case 0x03: | |
463 | case 0x04: | |
464 | case 0x05: | |
465 | /* Multi Bit Linear Audio */ | |
466 | case 0x07: /* DVD-Audio */ | |
467 | case 0x0c: /* High Precision */ | |
468 | /* One Bit Audio */ | |
469 | case 0x08: /* (Plain) Raw */ | |
470 | case 0x09: /* (Plain) SACD */ | |
471 | case 0x0a: /* (Encoded) Raw */ | |
472 | case 0x0b: /* (Encoded) SACD */ | |
473 | /* SMPTE Time-Code conformant */ | |
474 | case 0x0e: | |
475 | /* Sample Count */ | |
476 | case 0x0f: | |
477 | /* Anciliary Data */ | |
478 | case 0x10: | |
479 | /* Synchronization Stream (Stereo Raw audio) */ | |
480 | case 0x40: | |
481 | /* Don't care */ | |
482 | case 0xff: | |
483 | default: | |
484 | return -ENOSYS; /* not supported */ | |
485 | } | |
486 | } | |
487 | ||
49c7b3fc TS |
488 | if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM || |
489 | formation->midi > AM824_MAX_CHANNELS_FOR_MIDI) | |
5cd1d3f4 TS |
490 | return -ENOSYS; |
491 | ||
492 | return 0; | |
493 | } | |
494 | ||
495 | static int | |
496 | assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, | |
497 | unsigned int pid, u8 *buf, unsigned int *len, | |
498 | u8 **formats) | |
499 | { | |
500 | struct snd_oxfw_stream_formation formation; | |
501 | unsigned int i, eid; | |
502 | int err; | |
503 | ||
504 | /* get format at current sampling rate */ | |
505 | err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len); | |
506 | if (err < 0) { | |
507 | dev_err(&oxfw->unit->device, | |
508 | "fail to get current stream format for isoc %s plug %d:%d\n", | |
509 | (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out", | |
510 | pid, err); | |
511 | goto end; | |
512 | } | |
513 | ||
514 | /* parse and set stream format */ | |
515 | eid = 0; | |
516 | err = snd_oxfw_stream_parse_format(buf, &formation); | |
517 | if (err < 0) | |
518 | goto end; | |
519 | ||
cd3b7116 TS |
520 | formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len, |
521 | GFP_KERNEL); | |
522 | if (!formats[eid]) { | |
5cd1d3f4 TS |
523 | err = -ENOMEM; |
524 | goto end; | |
525 | } | |
5cd1d3f4 TS |
526 | |
527 | /* apply the format for each available sampling rate */ | |
528 | for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) { | |
529 | if (formation.rate == oxfw_rate_table[i]) | |
530 | continue; | |
531 | ||
532 | err = avc_general_inquiry_sig_fmt(oxfw->unit, | |
533 | oxfw_rate_table[i], | |
534 | dir, pid); | |
535 | if (err < 0) | |
536 | continue; | |
537 | ||
538 | eid++; | |
cd3b7116 TS |
539 | formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len, |
540 | GFP_KERNEL); | |
5cd1d3f4 TS |
541 | if (formats[eid] == NULL) { |
542 | err = -ENOMEM; | |
543 | goto end; | |
544 | } | |
5cd1d3f4 TS |
545 | formats[eid][2] = avc_stream_rate_table[i]; |
546 | } | |
547 | ||
548 | err = 0; | |
549 | oxfw->assumed = true; | |
550 | end: | |
551 | return err; | |
552 | } | |
553 | ||
554 | static int fill_stream_formats(struct snd_oxfw *oxfw, | |
555 | enum avc_general_plug_dir dir, | |
556 | unsigned short pid) | |
557 | { | |
558 | u8 *buf, **formats; | |
559 | unsigned int len, eid = 0; | |
560 | struct snd_oxfw_stream_formation dummy; | |
561 | int err; | |
562 | ||
563 | buf = kmalloc(AVC_GENERIC_FRAME_MAXIMUM_BYTES, GFP_KERNEL); | |
564 | if (buf == NULL) | |
565 | return -ENOMEM; | |
566 | ||
b0ac0009 TS |
567 | if (dir == AVC_GENERAL_PLUG_DIR_OUT) |
568 | formats = oxfw->tx_stream_formats; | |
569 | else | |
570 | formats = oxfw->rx_stream_formats; | |
5cd1d3f4 TS |
571 | |
572 | /* get first entry */ | |
573 | len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; | |
574 | err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0); | |
575 | if (err == -ENOSYS) { | |
576 | /* LIST subfunction is not implemented */ | |
577 | len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; | |
578 | err = assume_stream_formats(oxfw, dir, pid, buf, &len, | |
579 | formats); | |
580 | goto end; | |
581 | } else if (err < 0) { | |
582 | dev_err(&oxfw->unit->device, | |
583 | "fail to get stream format %d for isoc %s plug %d:%d\n", | |
584 | eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out", | |
585 | pid, err); | |
586 | goto end; | |
587 | } | |
588 | ||
589 | /* LIST subfunction is implemented */ | |
590 | while (eid < SND_OXFW_STREAM_FORMAT_ENTRIES) { | |
591 | /* The format is too short. */ | |
592 | if (len < 3) { | |
593 | err = -EIO; | |
594 | break; | |
595 | } | |
596 | ||
597 | /* parse and set stream format */ | |
598 | err = snd_oxfw_stream_parse_format(buf, &dummy); | |
599 | if (err < 0) | |
600 | break; | |
601 | ||
cd3b7116 TS |
602 | formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, len, |
603 | GFP_KERNEL); | |
604 | if (!formats[eid]) { | |
5cd1d3f4 TS |
605 | err = -ENOMEM; |
606 | break; | |
607 | } | |
5cd1d3f4 TS |
608 | |
609 | /* get next entry */ | |
610 | len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; | |
611 | err = avc_stream_get_format_list(oxfw->unit, dir, 0, | |
612 | buf, &len, ++eid); | |
613 | /* No entries remained. */ | |
614 | if (err == -EINVAL) { | |
615 | err = 0; | |
616 | break; | |
617 | } else if (err < 0) { | |
618 | dev_err(&oxfw->unit->device, | |
619 | "fail to get stream format %d for isoc %s plug %d:%d\n", | |
620 | eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : | |
621 | "out", | |
622 | pid, err); | |
623 | break; | |
624 | } | |
625 | } | |
626 | end: | |
627 | kfree(buf); | |
628 | return err; | |
629 | } | |
630 | ||
631 | int snd_oxfw_stream_discover(struct snd_oxfw *oxfw) | |
632 | { | |
633 | u8 plugs[AVC_PLUG_INFO_BUF_BYTES]; | |
32056041 TS |
634 | struct snd_oxfw_stream_formation formation; |
635 | u8 *format; | |
636 | unsigned int i; | |
5cd1d3f4 TS |
637 | int err; |
638 | ||
639 | /* the number of plugs for isoc in/out, ext in/out */ | |
640 | err = avc_general_get_plug_info(oxfw->unit, 0x1f, 0x07, 0x00, plugs); | |
641 | if (err < 0) { | |
642 | dev_err(&oxfw->unit->device, | |
643 | "fail to get info for isoc/external in/out plugs: %d\n", | |
644 | err); | |
645 | goto end; | |
b0ac0009 | 646 | } else if ((plugs[0] == 0) && (plugs[1] == 0)) { |
5cd1d3f4 TS |
647 | err = -ENOSYS; |
648 | goto end; | |
649 | } | |
650 | ||
b0ac0009 TS |
651 | /* use oPCR[0] if exists */ |
652 | if (plugs[1] > 0) { | |
653 | err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0); | |
654 | if (err < 0) | |
655 | goto end; | |
32056041 TS |
656 | |
657 | for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { | |
658 | format = oxfw->tx_stream_formats[i]; | |
659 | if (format == NULL) | |
660 | continue; | |
661 | err = snd_oxfw_stream_parse_format(format, &formation); | |
662 | if (err < 0) | |
663 | continue; | |
664 | ||
665 | /* Add one MIDI port. */ | |
666 | if (formation.midi > 0) | |
667 | oxfw->midi_input_ports = 1; | |
668 | } | |
669 | ||
b0ac0009 TS |
670 | oxfw->has_output = true; |
671 | } | |
672 | ||
5cd1d3f4 | 673 | /* use iPCR[0] if exists */ |
32056041 | 674 | if (plugs[0] > 0) { |
5cd1d3f4 | 675 | err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0); |
32056041 TS |
676 | if (err < 0) |
677 | goto end; | |
678 | ||
679 | for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { | |
680 | format = oxfw->rx_stream_formats[i]; | |
681 | if (format == NULL) | |
682 | continue; | |
683 | err = snd_oxfw_stream_parse_format(format, &formation); | |
684 | if (err < 0) | |
685 | continue; | |
686 | ||
687 | /* Add one MIDI port. */ | |
688 | if (formation.midi > 0) | |
689 | oxfw->midi_output_ports = 1; | |
690 | } | |
691 | } | |
5cd1d3f4 TS |
692 | end: |
693 | return err; | |
694 | } | |
8985f4ac TS |
695 | |
696 | void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw) | |
697 | { | |
698 | oxfw->dev_lock_changed = true; | |
699 | wake_up(&oxfw->hwdep_wait); | |
700 | } | |
701 | ||
702 | int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw) | |
703 | { | |
704 | int err; | |
705 | ||
706 | spin_lock_irq(&oxfw->lock); | |
707 | ||
708 | /* user land lock this */ | |
709 | if (oxfw->dev_lock_count < 0) { | |
710 | err = -EBUSY; | |
711 | goto end; | |
712 | } | |
713 | ||
714 | /* this is the first time */ | |
715 | if (oxfw->dev_lock_count++ == 0) | |
716 | snd_oxfw_stream_lock_changed(oxfw); | |
717 | err = 0; | |
718 | end: | |
719 | spin_unlock_irq(&oxfw->lock); | |
720 | return err; | |
721 | } | |
722 | ||
723 | void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw) | |
724 | { | |
725 | spin_lock_irq(&oxfw->lock); | |
726 | ||
727 | if (WARN_ON(oxfw->dev_lock_count <= 0)) | |
728 | goto end; | |
729 | if (--oxfw->dev_lock_count == 0) | |
730 | snd_oxfw_stream_lock_changed(oxfw); | |
731 | end: | |
732 | spin_unlock_irq(&oxfw->lock); | |
733 | } |