]>
Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
76b188c4 CA |
2 | /* |
3 | * Scarlett Driver for ALSA | |
4 | * | |
5 | * Copyright (c) 2013 by Tobias Hoffmann | |
6 | * Copyright (c) 2013 by Robin Gareus <robin at gareus.org> | |
7 | * Copyright (c) 2002 by Takashi Iwai <tiwai at suse.de> | |
8 | * Copyright (c) 2014 by Chris J Arges <chris.j.arges at canonical.com> | |
9 | * | |
10 | * Many codes borrowed from audio.c by | |
11 | * Alan Cox (alan at lxorguk.ukuu.org.uk) | |
12 | * Thomas Sailer (sailer at ife.ee.ethz.ch) | |
13 | * | |
14 | * Code cleanup: | |
15 | * David Henningsson <david.henningsson at canonical.com> | |
76b188c4 CA |
16 | */ |
17 | ||
18 | /* | |
19 | * Rewritten and extended to support more models, e.g. Scarlett 18i8. | |
20 | * | |
21 | * Auto-detection via UAC2 is not feasible to properly discover the vast | |
22 | * majority of features. It's related to both Linux/ALSA's UAC2 as well as | |
23 | * Focusrite's implementation of it. Eventually quirks may be sufficient but | |
ff630b6a | 24 | * right now it's a major headache to work around these things. |
76b188c4 CA |
25 | * |
26 | * NB. Neither the OSX nor the win driver provided by Focusrite performs | |
27 | * discovery, they seem to operate the same as this driver. | |
28 | */ | |
29 | ||
30 | /* Mixer Interface for the Focusrite Scarlett 18i6 audio interface. | |
31 | * | |
32 | * The protocol was reverse engineered by looking at communication between | |
33 | * Scarlett MixControl (v 1.2.128.0) and the Focusrite(R) Scarlett 18i6 | |
34 | * (firmware v305) using wireshark and usbmon in January 2013. | |
35 | * Extended in July 2013. | |
36 | * | |
37 | * this mixer gives complete access to all features of the device: | |
38 | * - change Impedance of inputs (Line-in, Mic / Instrument, Hi-Z) | |
39 | * - select clock source | |
40 | * - dynamic input to mixer-matrix assignment | |
41 | * - 18 x 6 mixer-matrix gain stages | |
42 | * - bus routing & volume control | |
43 | * - automatic re-initialization on connect if device was power-cycled | |
44 | * | |
45 | * USB URB commands overview (bRequest = 0x01 = UAC2_CS_CUR) | |
46 | * wIndex | |
47 | * 0x01 Analog Input line/instrument impedance switch, wValue=0x0901 + | |
48 | * channel, data=Line/Inst (2bytes) | |
49 | * pad (-10dB) switch, wValue=0x0b01 + channel, data=Off/On (2bytes) | |
50 | * ?? wValue=0x0803/04, ?? (2bytes) | |
51 | * 0x0a Master Volume, wValue=0x0200+bus[0:all + only 1..4?] data(2bytes) | |
52 | * Bus Mute/Unmute wValue=0x0100+bus[0:all + only 1..4?], data(2bytes) | |
53 | * 0x28 Clock source, wValue=0x0100, data={1:int,2:spdif,3:adat} (1byte) | |
54 | * 0x29 Set Sample-rate, wValue=0x0100, data=sample-rate(4bytes) | |
55 | * 0x32 Mixer mux, wValue=0x0600 + mixer-channel, data=input-to-connect(2bytes) | |
56 | * 0x33 Output mux, wValue=bus, data=input-to-connect(2bytes) | |
57 | * 0x34 Capture mux, wValue=0...18, data=input-to-connect(2bytes) | |
58 | * 0x3c Matrix Mixer gains, wValue=mixer-node data=gain(2bytes) | |
59 | * ?? [sometimes](4bytes, e.g 0x000003be 0x000003bf ...03ff) | |
60 | * | |
61 | * USB reads: (i.e. actually issued by original software) | |
62 | * 0x01 wValue=0x0901+channel (1byte!!), wValue=0x0b01+channed (1byte!!) | |
63 | * 0x29 wValue=0x0100 sample-rate(4bytes) | |
64 | * wValue=0x0200 ?? 1byte (only once) | |
65 | * 0x2a wValue=0x0100 ?? 4bytes, sample-rate2 ?? | |
66 | * | |
67 | * USB reads with bRequest = 0x03 = UAC2_CS_MEM | |
68 | * 0x3c wValue=0x0002 1byte: sync status (locked=1) | |
69 | * wValue=0x0000 18*2byte: peak meter (inputs) | |
70 | * wValue=0x0001 8(?)*2byte: peak meter (mix) | |
71 | * wValue=0x0003 6*2byte: peak meter (pcm/daw) | |
72 | * | |
73 | * USB write with bRequest = 0x03 | |
74 | * 0x3c Save settings to hardware: wValue=0x005a, data=0xa5 | |
75 | * | |
76 | * | |
77 | * <ditaa> | |
78 | * /--------------\ 18chn 6chn /--------------\ | |
79 | * | Hardware in +--+-------\ /------+--+ ALSA PCM out | | |
80 | * \--------------/ | | | | \--------------/ | |
81 | * | | | | | |
82 | * | v v | | |
83 | * | +---------------+ | | |
84 | * | \ Matrix Mux / | | |
85 | * | +-----+-----+ | | |
86 | * | | | | |
87 | * | | 18chn | | |
88 | * | v | | |
89 | * | +-----------+ | | |
90 | * | | Mixer | | | |
91 | * | | Matrix | | | |
92 | * | | | | | |
93 | * | | 18x6 Gain | | | |
94 | * | | stages | | | |
95 | * | +-----+-----+ | | |
96 | * | | | | |
97 | * | | | | |
98 | * | 18chn | 6chn | 6chn | |
99 | * v v v | |
100 | * ========================= | |
101 | * +---------------+ +--—------------+ | |
102 | * \ Output Mux / \ Capture Mux / | |
103 | * +-----+-----+ +-----+-----+ | |
104 | * | | | |
105 | * | 6chn | | |
106 | * v | | |
107 | * +-------------+ | | |
108 | * | Master Gain | | | |
109 | * +------+------+ | | |
110 | * | | | |
111 | * | 6chn | 18chn | |
112 | * | (3 stereo pairs) | | |
113 | * /--------------\ | | /--------------\ | |
114 | * | Hardware out |<--/ \-->| ALSA PCM in | | |
115 | * \--------------/ \--------------/ | |
116 | * </ditaa> | |
117 | * | |
118 | */ | |
119 | ||
120 | #include <linux/slab.h> | |
121 | #include <linux/usb.h> | |
122 | #include <linux/usb/audio-v2.h> | |
123 | ||
124 | #include <sound/core.h> | |
125 | #include <sound/control.h> | |
126 | #include <sound/tlv.h> | |
127 | ||
128 | #include "usbaudio.h" | |
129 | #include "mixer.h" | |
130 | #include "helper.h" | |
131 | #include "power.h" | |
132 | ||
133 | #include "mixer_scarlett.h" | |
134 | ||
135 | /* some gui mixers can't handle negative ctl values */ | |
136 | #define SND_SCARLETT_LEVEL_BIAS 128 | |
137 | #define SND_SCARLETT_MATRIX_IN_MAX 18 | |
138 | #define SND_SCARLETT_CONTROLS_MAX 10 | |
139 | #define SND_SCARLETT_OFFSETS_MAX 5 | |
140 | ||
141 | enum { | |
142 | SCARLETT_OUTPUTS, | |
143 | SCARLETT_SWITCH_IMPEDANCE, | |
144 | SCARLETT_SWITCH_PAD, | |
bf2aa5ca | 145 | SCARLETT_SWITCH_GAIN, |
76b188c4 CA |
146 | }; |
147 | ||
148 | enum { | |
149 | SCARLETT_OFFSET_PCM = 0, | |
150 | SCARLETT_OFFSET_ANALOG = 1, | |
151 | SCARLETT_OFFSET_SPDIF = 2, | |
152 | SCARLETT_OFFSET_ADAT = 3, | |
153 | SCARLETT_OFFSET_MIX = 4, | |
154 | }; | |
155 | ||
156 | struct scarlett_mixer_elem_enum_info { | |
157 | int start; | |
158 | int len; | |
159 | int offsets[SND_SCARLETT_OFFSETS_MAX]; | |
160 | char const * const *names; | |
161 | }; | |
162 | ||
163 | struct scarlett_mixer_control { | |
164 | unsigned char num; | |
165 | unsigned char type; | |
166 | const char *name; | |
167 | }; | |
168 | ||
169 | struct scarlett_device_info { | |
170 | int matrix_in; | |
171 | int matrix_out; | |
172 | int input_len; | |
173 | int output_len; | |
174 | ||
175 | struct scarlett_mixer_elem_enum_info opt_master; | |
176 | struct scarlett_mixer_elem_enum_info opt_matrix; | |
177 | ||
178 | /* initial values for matrix mux */ | |
179 | int matrix_mux_init[SND_SCARLETT_MATRIX_IN_MAX]; | |
180 | ||
181 | int num_controls; /* number of items in controls */ | |
182 | const struct scarlett_mixer_control controls[SND_SCARLETT_CONTROLS_MAX]; | |
183 | }; | |
184 | ||
185 | /********************** Enum Strings *************************/ | |
186 | ||
187 | static const struct scarlett_mixer_elem_enum_info opt_pad = { | |
188 | .start = 0, | |
189 | .len = 2, | |
190 | .offsets = {}, | |
191 | .names = (char const * const []){ | |
192 | "0dB", "-10dB" | |
193 | } | |
194 | }; | |
195 | ||
bf2aa5ca JV |
196 | static const struct scarlett_mixer_elem_enum_info opt_gain = { |
197 | .start = 0, | |
198 | .len = 2, | |
199 | .offsets = {}, | |
200 | .names = (char const * const []){ | |
201 | "Lo", "Hi" | |
202 | } | |
203 | }; | |
204 | ||
76b188c4 CA |
205 | static const struct scarlett_mixer_elem_enum_info opt_impedance = { |
206 | .start = 0, | |
207 | .len = 2, | |
208 | .offsets = {}, | |
209 | .names = (char const * const []){ | |
210 | "Line", "Hi-Z" | |
211 | } | |
212 | }; | |
213 | ||
214 | static const struct scarlett_mixer_elem_enum_info opt_clock = { | |
215 | .start = 1, | |
216 | .len = 3, | |
217 | .offsets = {}, | |
218 | .names = (char const * const []){ | |
219 | "Internal", "SPDIF", "ADAT" | |
220 | } | |
221 | }; | |
222 | ||
223 | static const struct scarlett_mixer_elem_enum_info opt_sync = { | |
224 | .start = 0, | |
225 | .len = 2, | |
226 | .offsets = {}, | |
227 | .names = (char const * const []){ | |
228 | "No Lock", "Locked" | |
229 | } | |
230 | }; | |
231 | ||
232 | static int scarlett_ctl_switch_info(struct snd_kcontrol *kctl, | |
233 | struct snd_ctl_elem_info *uinfo) | |
234 | { | |
235 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
236 | ||
237 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | |
238 | uinfo->count = elem->channels; | |
239 | uinfo->value.integer.min = 0; | |
240 | uinfo->value.integer.max = 1; | |
241 | return 0; | |
242 | } | |
243 | ||
244 | static int scarlett_ctl_switch_get(struct snd_kcontrol *kctl, | |
245 | struct snd_ctl_elem_value *ucontrol) | |
246 | { | |
247 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
248 | int i, err, val; | |
249 | ||
250 | for (i = 0; i < elem->channels; i++) { | |
251 | err = snd_usb_get_cur_mix_value(elem, i, i, &val); | |
252 | if (err < 0) | |
253 | return err; | |
254 | ||
255 | val = !val; /* invert mute logic for mixer */ | |
256 | ucontrol->value.integer.value[i] = val; | |
257 | } | |
258 | ||
259 | return 0; | |
260 | } | |
261 | ||
262 | static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl, | |
263 | struct snd_ctl_elem_value *ucontrol) | |
264 | { | |
265 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
266 | int i, changed = 0; | |
267 | int err, oval, val; | |
268 | ||
269 | for (i = 0; i < elem->channels; i++) { | |
270 | err = snd_usb_get_cur_mix_value(elem, i, i, &oval); | |
271 | if (err < 0) | |
272 | return err; | |
273 | ||
274 | val = ucontrol->value.integer.value[i]; | |
275 | val = !val; | |
276 | if (oval != val) { | |
277 | err = snd_usb_set_cur_mix_value(elem, i, i, val); | |
278 | if (err < 0) | |
279 | return err; | |
280 | ||
281 | changed = 1; | |
282 | } | |
283 | } | |
284 | ||
285 | return changed; | |
286 | } | |
287 | ||
b61f90ea TI |
288 | static int scarlett_ctl_resume(struct usb_mixer_elem_list *list) |
289 | { | |
8c558076 | 290 | struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list); |
b61f90ea TI |
291 | int i; |
292 | ||
293 | for (i = 0; i < elem->channels; i++) | |
294 | if (elem->cached & (1 << i)) | |
295 | snd_usb_set_cur_mix_value(elem, i, i, | |
296 | elem->cache_val[i]); | |
297 | return 0; | |
298 | } | |
299 | ||
76b188c4 CA |
300 | static int scarlett_ctl_info(struct snd_kcontrol *kctl, |
301 | struct snd_ctl_elem_info *uinfo) | |
302 | { | |
303 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
304 | ||
305 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
306 | uinfo->count = elem->channels; | |
307 | uinfo->value.integer.min = 0; | |
308 | uinfo->value.integer.max = (int)kctl->private_value + | |
309 | SND_SCARLETT_LEVEL_BIAS; | |
310 | uinfo->value.integer.step = 1; | |
311 | return 0; | |
312 | } | |
313 | ||
314 | static int scarlett_ctl_get(struct snd_kcontrol *kctl, | |
315 | struct snd_ctl_elem_value *ucontrol) | |
316 | { | |
317 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
318 | int i, err, val; | |
319 | ||
320 | for (i = 0; i < elem->channels; i++) { | |
321 | err = snd_usb_get_cur_mix_value(elem, i, i, &val); | |
322 | if (err < 0) | |
323 | return err; | |
324 | ||
325 | val = clamp(val / 256, -128, (int)kctl->private_value) + | |
326 | SND_SCARLETT_LEVEL_BIAS; | |
327 | ucontrol->value.integer.value[i] = val; | |
328 | } | |
329 | ||
330 | return 0; | |
331 | } | |
332 | ||
333 | static int scarlett_ctl_put(struct snd_kcontrol *kctl, | |
334 | struct snd_ctl_elem_value *ucontrol) | |
335 | { | |
336 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
337 | int i, changed = 0; | |
338 | int err, oval, val; | |
339 | ||
340 | for (i = 0; i < elem->channels; i++) { | |
341 | err = snd_usb_get_cur_mix_value(elem, i, i, &oval); | |
342 | if (err < 0) | |
343 | return err; | |
344 | ||
345 | val = ucontrol->value.integer.value[i] - | |
346 | SND_SCARLETT_LEVEL_BIAS; | |
347 | val = val * 256; | |
348 | if (oval != val) { | |
349 | err = snd_usb_set_cur_mix_value(elem, i, i, val); | |
350 | if (err < 0) | |
351 | return err; | |
352 | ||
353 | changed = 1; | |
354 | } | |
355 | } | |
356 | ||
357 | return changed; | |
358 | } | |
359 | ||
360 | static void scarlett_generate_name(int i, char *dst, int offsets[]) | |
361 | { | |
362 | if (i > offsets[SCARLETT_OFFSET_MIX]) | |
363 | sprintf(dst, "Mix %c", | |
364 | 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1)); | |
365 | else if (i > offsets[SCARLETT_OFFSET_ADAT]) | |
366 | sprintf(dst, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]); | |
367 | else if (i > offsets[SCARLETT_OFFSET_SPDIF]) | |
368 | sprintf(dst, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]); | |
369 | else if (i > offsets[SCARLETT_OFFSET_ANALOG]) | |
370 | sprintf(dst, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]); | |
371 | else if (i > offsets[SCARLETT_OFFSET_PCM]) | |
372 | sprintf(dst, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]); | |
373 | else | |
374 | sprintf(dst, "Off"); | |
375 | } | |
376 | ||
377 | static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl, | |
378 | struct snd_ctl_elem_info *uinfo) | |
379 | { | |
380 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
381 | struct scarlett_mixer_elem_enum_info *opt = elem->private_data; | |
382 | unsigned int items = opt->len; | |
383 | ||
384 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | |
385 | uinfo->count = elem->channels; | |
386 | uinfo->value.enumerated.items = items; | |
387 | ||
388 | if (uinfo->value.enumerated.item >= items) | |
389 | uinfo->value.enumerated.item = items - 1; | |
390 | ||
391 | /* generate name dynamically based on item number and offset info */ | |
392 | scarlett_generate_name(uinfo->value.enumerated.item, | |
393 | uinfo->value.enumerated.name, | |
394 | opt->offsets); | |
395 | ||
396 | return 0; | |
397 | } | |
398 | ||
399 | static int scarlett_ctl_enum_info(struct snd_kcontrol *kctl, | |
400 | struct snd_ctl_elem_info *uinfo) | |
401 | { | |
402 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
403 | struct scarlett_mixer_elem_enum_info *opt = elem->private_data; | |
404 | ||
405 | return snd_ctl_enum_info(uinfo, elem->channels, opt->len, | |
406 | (const char * const *)opt->names); | |
407 | } | |
408 | ||
409 | static int scarlett_ctl_enum_get(struct snd_kcontrol *kctl, | |
410 | struct snd_ctl_elem_value *ucontrol) | |
411 | { | |
412 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
413 | struct scarlett_mixer_elem_enum_info *opt = elem->private_data; | |
414 | int err, val; | |
415 | ||
416 | err = snd_usb_get_cur_mix_value(elem, 0, 0, &val); | |
417 | if (err < 0) | |
418 | return err; | |
419 | ||
420 | val = clamp(val - opt->start, 0, opt->len-1); | |
421 | ||
422 | ucontrol->value.enumerated.item[0] = val; | |
423 | ||
424 | return 0; | |
425 | } | |
426 | ||
427 | static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, | |
428 | struct snd_ctl_elem_value *ucontrol) | |
429 | { | |
430 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
431 | struct scarlett_mixer_elem_enum_info *opt = elem->private_data; | |
432 | int err, oval, val; | |
433 | ||
434 | err = snd_usb_get_cur_mix_value(elem, 0, 0, &oval); | |
435 | if (err < 0) | |
436 | return err; | |
437 | ||
438 | val = ucontrol->value.integer.value[0]; | |
439 | val = val + opt->start; | |
440 | if (val != oval) { | |
441 | snd_usb_set_cur_mix_value(elem, 0, 0, val); | |
442 | return 1; | |
443 | } | |
444 | return 0; | |
445 | } | |
446 | ||
b61f90ea TI |
447 | static int scarlett_ctl_enum_resume(struct usb_mixer_elem_list *list) |
448 | { | |
8c558076 | 449 | struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list); |
b61f90ea TI |
450 | |
451 | if (elem->cached) | |
452 | snd_usb_set_cur_mix_value(elem, 0, 0, *elem->cache_val); | |
453 | return 0; | |
454 | } | |
455 | ||
76b188c4 CA |
456 | static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl, |
457 | struct snd_ctl_elem_value *ucontrol) | |
458 | { | |
459 | struct usb_mixer_elem_info *elem = kctl->private_data; | |
3360b84b | 460 | struct snd_usb_audio *chip = elem->head.mixer->chip; |
76b188c4 CA |
461 | unsigned char buf[2 * MAX_CHANNELS] = {0, }; |
462 | int wValue = (elem->control << 8) | elem->idx_off; | |
3360b84b | 463 | int idx = snd_usb_ctrl_intf(chip) | (elem->head.id << 8); |
76b188c4 CA |
464 | int err; |
465 | ||
466 | err = snd_usb_ctl_msg(chip->dev, | |
467 | usb_rcvctrlpipe(chip->dev, 0), | |
468 | UAC2_CS_MEM, | |
469 | USB_RECIP_INTERFACE | USB_TYPE_CLASS | | |
470 | USB_DIR_IN, wValue, idx, buf, elem->channels); | |
471 | if (err < 0) | |
472 | return err; | |
473 | ||
474 | ucontrol->value.enumerated.item[0] = clamp((int)buf[0], 0, 1); | |
475 | return 0; | |
476 | } | |
477 | ||
04bab350 | 478 | static const struct snd_kcontrol_new usb_scarlett_ctl_switch = { |
76b188c4 CA |
479 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
480 | .name = "", | |
481 | .info = scarlett_ctl_switch_info, | |
482 | .get = scarlett_ctl_switch_get, | |
483 | .put = scarlett_ctl_switch_put, | |
484 | }; | |
485 | ||
486 | static const DECLARE_TLV_DB_SCALE(db_scale_scarlett_gain, -12800, 100, 0); | |
487 | ||
04bab350 | 488 | static const struct snd_kcontrol_new usb_scarlett_ctl = { |
76b188c4 CA |
489 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
490 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | |
491 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | |
492 | .name = "", | |
493 | .info = scarlett_ctl_info, | |
494 | .get = scarlett_ctl_get, | |
495 | .put = scarlett_ctl_put, | |
496 | .private_value = 6, /* max value */ | |
497 | .tlv = { .p = db_scale_scarlett_gain } | |
498 | }; | |
499 | ||
04bab350 | 500 | static const struct snd_kcontrol_new usb_scarlett_ctl_master = { |
76b188c4 CA |
501 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
502 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | |
503 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | |
504 | .name = "", | |
505 | .info = scarlett_ctl_info, | |
506 | .get = scarlett_ctl_get, | |
507 | .put = scarlett_ctl_put, | |
508 | .private_value = 6, /* max value */ | |
509 | .tlv = { .p = db_scale_scarlett_gain } | |
510 | }; | |
511 | ||
04bab350 | 512 | static const struct snd_kcontrol_new usb_scarlett_ctl_enum = { |
76b188c4 CA |
513 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
514 | .name = "", | |
515 | .info = scarlett_ctl_enum_info, | |
516 | .get = scarlett_ctl_enum_get, | |
517 | .put = scarlett_ctl_enum_put, | |
518 | }; | |
519 | ||
04bab350 | 520 | static const struct snd_kcontrol_new usb_scarlett_ctl_dynamic_enum = { |
76b188c4 CA |
521 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
522 | .name = "", | |
523 | .info = scarlett_ctl_enum_dynamic_info, | |
524 | .get = scarlett_ctl_enum_get, | |
525 | .put = scarlett_ctl_enum_put, | |
526 | }; | |
527 | ||
04bab350 | 528 | static const struct snd_kcontrol_new usb_scarlett_ctl_sync = { |
76b188c4 CA |
529 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
530 | .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | |
531 | .name = "", | |
532 | .info = scarlett_ctl_enum_info, | |
533 | .get = scarlett_ctl_meter_get, | |
534 | }; | |
535 | ||
536 | static int add_new_ctl(struct usb_mixer_interface *mixer, | |
537 | const struct snd_kcontrol_new *ncontrol, | |
b61f90ea | 538 | usb_mixer_elem_resume_func_t resume, |
76b188c4 CA |
539 | int index, int offset, int num, |
540 | int val_type, int channels, const char *name, | |
541 | const struct scarlett_mixer_elem_enum_info *opt, | |
542 | struct usb_mixer_elem_info **elem_ret | |
543 | ) | |
544 | { | |
545 | struct snd_kcontrol *kctl; | |
546 | struct usb_mixer_elem_info *elem; | |
547 | int err; | |
548 | ||
549 | elem = kzalloc(sizeof(*elem), GFP_KERNEL); | |
550 | if (!elem) | |
551 | return -ENOMEM; | |
552 | ||
3360b84b | 553 | elem->head.mixer = mixer; |
b61f90ea | 554 | elem->head.resume = resume; |
76b188c4 CA |
555 | elem->control = offset; |
556 | elem->idx_off = num; | |
3360b84b | 557 | elem->head.id = index; |
76b188c4 CA |
558 | elem->val_type = val_type; |
559 | ||
560 | elem->channels = channels; | |
561 | ||
562 | /* add scarlett_mixer_elem_enum_info struct */ | |
563 | elem->private_data = (void *)opt; | |
564 | ||
565 | kctl = snd_ctl_new1(ncontrol, elem); | |
566 | if (!kctl) { | |
567 | kfree(elem); | |
568 | return -ENOMEM; | |
569 | } | |
570 | kctl->private_free = snd_usb_mixer_elem_free; | |
571 | ||
75b1a8f9 | 572 | strscpy(kctl->id.name, name, sizeof(kctl->id.name)); |
76b188c4 | 573 | |
b61f90ea | 574 | err = snd_usb_mixer_add_control(&elem->head, kctl); |
76b188c4 CA |
575 | if (err < 0) |
576 | return err; | |
577 | ||
578 | if (elem_ret) | |
579 | *elem_ret = elem; | |
580 | ||
581 | return 0; | |
582 | } | |
583 | ||
584 | static int add_output_ctls(struct usb_mixer_interface *mixer, | |
585 | int index, const char *name, | |
586 | const struct scarlett_device_info *info) | |
587 | { | |
588 | int err; | |
589 | char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; | |
590 | struct usb_mixer_elem_info *elem; | |
591 | ||
592 | /* Add mute switch */ | |
593 | snprintf(mx, sizeof(mx), "Master %d (%s) Playback Switch", | |
594 | index + 1, name); | |
b61f90ea TI |
595 | err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, |
596 | scarlett_ctl_resume, 0x0a, 0x01, | |
76b188c4 CA |
597 | 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); |
598 | if (err < 0) | |
599 | return err; | |
600 | ||
601 | /* Add volume control and initialize to 0 */ | |
602 | snprintf(mx, sizeof(mx), "Master %d (%s) Playback Volume", | |
603 | index + 1, name); | |
b61f90ea TI |
604 | err = add_new_ctl(mixer, &usb_scarlett_ctl_master, |
605 | scarlett_ctl_resume, 0x0a, 0x02, | |
76b188c4 CA |
606 | 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); |
607 | if (err < 0) | |
608 | return err; | |
609 | ||
610 | /* Add L channel source playback enumeration */ | |
611 | snprintf(mx, sizeof(mx), "Master %dL (%s) Source Playback Enum", | |
612 | index + 1, name); | |
b61f90ea TI |
613 | err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, |
614 | scarlett_ctl_enum_resume, 0x33, 0x00, | |
76b188c4 CA |
615 | 2*index, USB_MIXER_S16, 1, mx, &info->opt_master, |
616 | &elem); | |
617 | if (err < 0) | |
618 | return err; | |
619 | ||
620 | /* Add R channel source playback enumeration */ | |
621 | snprintf(mx, sizeof(mx), "Master %dR (%s) Source Playback Enum", | |
622 | index + 1, name); | |
b61f90ea TI |
623 | err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, |
624 | scarlett_ctl_enum_resume, 0x33, 0x00, | |
76b188c4 CA |
625 | 2*index+1, USB_MIXER_S16, 1, mx, &info->opt_master, |
626 | &elem); | |
627 | if (err < 0) | |
628 | return err; | |
629 | ||
630 | return 0; | |
631 | } | |
632 | ||
633 | /********************** device-specific config *************************/ | |
634 | ||
635 | /* untested... */ | |
a01df925 | 636 | static const struct scarlett_device_info s6i6_info = { |
76b188c4 CA |
637 | .matrix_in = 18, |
638 | .matrix_out = 8, | |
639 | .input_len = 6, | |
640 | .output_len = 6, | |
641 | ||
642 | .opt_master = { | |
643 | .start = -1, | |
644 | .len = 27, | |
645 | .offsets = {0, 12, 16, 18, 18}, | |
646 | .names = NULL | |
647 | }, | |
648 | ||
649 | .opt_matrix = { | |
650 | .start = -1, | |
651 | .len = 19, | |
652 | .offsets = {0, 12, 16, 18, 18}, | |
653 | .names = NULL | |
654 | }, | |
655 | ||
c99b9e85 | 656 | .num_controls = 9, |
76b188c4 CA |
657 | .controls = { |
658 | { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, | |
659 | { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, | |
660 | { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, | |
661 | { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, | |
662 | { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
663 | { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, | |
664 | { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
bf2aa5ca JV |
665 | { .num = 3, .type = SCARLETT_SWITCH_GAIN, .name = NULL}, |
666 | { .num = 4, .type = SCARLETT_SWITCH_GAIN, .name = NULL}, | |
76b188c4 CA |
667 | }, |
668 | ||
669 | .matrix_mux_init = { | |
670 | 12, 13, 14, 15, /* Analog -> 1..4 */ | |
671 | 16, 17, /* SPDIF -> 5,6 */ | |
672 | 0, 1, 2, 3, 4, 5, 6, 7, /* PCM[1..12] -> 7..18 */ | |
673 | 8, 9, 10, 11 | |
674 | } | |
675 | }; | |
676 | ||
677 | /* untested... */ | |
a01df925 | 678 | static const struct scarlett_device_info s8i6_info = { |
76b188c4 CA |
679 | .matrix_in = 18, |
680 | .matrix_out = 6, | |
681 | .input_len = 8, | |
682 | .output_len = 6, | |
683 | ||
684 | .opt_master = { | |
685 | .start = -1, | |
686 | .len = 25, | |
687 | .offsets = {0, 12, 16, 18, 18}, | |
688 | .names = NULL | |
689 | }, | |
690 | ||
691 | .opt_matrix = { | |
692 | .start = -1, | |
693 | .len = 19, | |
694 | .offsets = {0, 12, 16, 18, 18}, | |
695 | .names = NULL | |
696 | }, | |
697 | ||
698 | .num_controls = 7, | |
699 | .controls = { | |
700 | { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, | |
701 | { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, | |
702 | { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, | |
703 | { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, | |
704 | { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, | |
705 | { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
706 | { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
707 | }, | |
708 | ||
709 | .matrix_mux_init = { | |
710 | 12, 13, 14, 15, /* Analog -> 1..4 */ | |
711 | 16, 17, /* SPDIF -> 5,6 */ | |
712 | 0, 1, 2, 3, 4, 5, 6, 7, /* PCM[1..12] -> 7..18 */ | |
713 | 8, 9, 10, 11 | |
714 | } | |
715 | }; | |
716 | ||
a01df925 | 717 | static const struct scarlett_device_info s18i6_info = { |
76b188c4 CA |
718 | .matrix_in = 18, |
719 | .matrix_out = 6, | |
720 | .input_len = 18, | |
721 | .output_len = 6, | |
722 | ||
723 | .opt_master = { | |
724 | .start = -1, | |
725 | .len = 31, | |
726 | .offsets = {0, 6, 14, 16, 24}, | |
727 | .names = NULL, | |
728 | }, | |
729 | ||
730 | .opt_matrix = { | |
731 | .start = -1, | |
732 | .len = 25, | |
733 | .offsets = {0, 6, 14, 16, 24}, | |
734 | .names = NULL, | |
735 | }, | |
736 | ||
737 | .num_controls = 5, | |
738 | .controls = { | |
739 | { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, | |
740 | { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, | |
741 | { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, | |
742 | { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, | |
743 | { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, | |
744 | }, | |
745 | ||
746 | .matrix_mux_init = { | |
747 | 6, 7, 8, 9, 10, 11, 12, 13, /* Analog -> 1..8 */ | |
748 | 16, 17, 18, 19, 20, 21, /* ADAT[1..6] -> 9..14 */ | |
749 | 14, 15, /* SPDIF -> 15,16 */ | |
750 | 0, 1 /* PCM[1,2] -> 17,18 */ | |
751 | } | |
752 | }; | |
753 | ||
a01df925 | 754 | static const struct scarlett_device_info s18i8_info = { |
76b188c4 CA |
755 | .matrix_in = 18, |
756 | .matrix_out = 8, | |
757 | .input_len = 18, | |
758 | .output_len = 8, | |
759 | ||
760 | .opt_master = { | |
761 | .start = -1, | |
762 | .len = 35, | |
763 | .offsets = {0, 8, 16, 18, 26}, | |
764 | .names = NULL | |
765 | }, | |
766 | ||
767 | .opt_matrix = { | |
768 | .start = -1, | |
769 | .len = 27, | |
770 | .offsets = {0, 8, 16, 18, 26}, | |
771 | .names = NULL | |
772 | }, | |
773 | ||
774 | .num_controls = 10, | |
775 | .controls = { | |
776 | { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, | |
777 | { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone 1" }, | |
778 | { .num = 2, .type = SCARLETT_OUTPUTS, .name = "Headphone 2" }, | |
779 | { .num = 3, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, | |
780 | { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, | |
781 | { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
782 | { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, | |
783 | { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
784 | { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
785 | { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
786 | }, | |
787 | ||
788 | .matrix_mux_init = { | |
789 | 8, 9, 10, 11, 12, 13, 14, 15, /* Analog -> 1..8 */ | |
790 | 18, 19, 20, 21, 22, 23, /* ADAT[1..6] -> 9..14 */ | |
791 | 16, 17, /* SPDIF -> 15,16 */ | |
792 | 0, 1 /* PCM[1,2] -> 17,18 */ | |
793 | } | |
794 | }; | |
795 | ||
a01df925 | 796 | static const struct scarlett_device_info s18i20_info = { |
76b188c4 CA |
797 | .matrix_in = 18, |
798 | .matrix_out = 8, | |
799 | .input_len = 18, | |
800 | .output_len = 20, | |
801 | ||
802 | .opt_master = { | |
803 | .start = -1, | |
804 | .len = 47, | |
805 | .offsets = {0, 20, 28, 30, 38}, | |
806 | .names = NULL | |
807 | }, | |
808 | ||
809 | .opt_matrix = { | |
810 | .start = -1, | |
811 | .len = 39, | |
812 | .offsets = {0, 20, 28, 30, 38}, | |
813 | .names = NULL | |
814 | }, | |
815 | ||
816 | .num_controls = 10, | |
817 | .controls = { | |
818 | { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, | |
819 | { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Line 3/4" }, | |
820 | { .num = 2, .type = SCARLETT_OUTPUTS, .name = "Line 5/6" }, | |
821 | { .num = 3, .type = SCARLETT_OUTPUTS, .name = "Line 7/8" }, | |
822 | { .num = 4, .type = SCARLETT_OUTPUTS, .name = "Line 9/10" }, | |
823 | { .num = 5, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, | |
824 | { .num = 6, .type = SCARLETT_OUTPUTS, .name = "ADAT 1/2" }, | |
825 | { .num = 7, .type = SCARLETT_OUTPUTS, .name = "ADAT 3/4" }, | |
826 | { .num = 8, .type = SCARLETT_OUTPUTS, .name = "ADAT 5/6" }, | |
827 | { .num = 9, .type = SCARLETT_OUTPUTS, .name = "ADAT 7/8" }, | |
828 | /*{ .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, | |
829 | { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
830 | { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, | |
831 | { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
832 | { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, | |
833 | { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL},*/ | |
834 | }, | |
835 | ||
836 | .matrix_mux_init = { | |
837 | 20, 21, 22, 23, 24, 25, 26, 27, /* Analog -> 1..8 */ | |
838 | 30, 31, 32, 33, 34, 35, /* ADAT[1..6] -> 9..14 */ | |
839 | 28, 29, /* SPDIF -> 15,16 */ | |
840 | 0, 1 /* PCM[1,2] -> 17,18 */ | |
841 | } | |
842 | }; | |
843 | ||
844 | ||
845 | static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, | |
a01df925 | 846 | const struct scarlett_device_info *info) |
76b188c4 CA |
847 | { |
848 | int i, err; | |
849 | char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; | |
850 | const struct scarlett_mixer_control *ctl; | |
851 | struct usb_mixer_elem_info *elem; | |
852 | ||
853 | /* create master switch and playback volume */ | |
b61f90ea TI |
854 | err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, |
855 | scarlett_ctl_resume, 0x0a, 0x01, 0, | |
76b188c4 CA |
856 | USB_MIXER_S16, 1, "Master Playback Switch", NULL, |
857 | &elem); | |
858 | if (err < 0) | |
859 | return err; | |
860 | ||
b61f90ea TI |
861 | err = add_new_ctl(mixer, &usb_scarlett_ctl_master, |
862 | scarlett_ctl_resume, 0x0a, 0x02, 0, | |
76b188c4 CA |
863 | USB_MIXER_S16, 1, "Master Playback Volume", NULL, |
864 | &elem); | |
865 | if (err < 0) | |
866 | return err; | |
867 | ||
868 | /* iterate through controls in info struct and create each one */ | |
869 | for (i = 0; i < info->num_controls; i++) { | |
870 | ctl = &info->controls[i]; | |
871 | ||
872 | switch (ctl->type) { | |
873 | case SCARLETT_OUTPUTS: | |
874 | err = add_output_ctls(mixer, ctl->num, ctl->name, info); | |
875 | if (err < 0) | |
876 | return err; | |
877 | break; | |
878 | case SCARLETT_SWITCH_IMPEDANCE: | |
879 | sprintf(mx, "Input %d Impedance Switch", ctl->num); | |
b61f90ea TI |
880 | err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, |
881 | scarlett_ctl_enum_resume, 0x01, | |
76b188c4 CA |
882 | 0x09, ctl->num, USB_MIXER_S16, 1, mx, |
883 | &opt_impedance, &elem); | |
884 | if (err < 0) | |
885 | return err; | |
886 | break; | |
887 | case SCARLETT_SWITCH_PAD: | |
888 | sprintf(mx, "Input %d Pad Switch", ctl->num); | |
b61f90ea TI |
889 | err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, |
890 | scarlett_ctl_enum_resume, 0x01, | |
76b188c4 CA |
891 | 0x0b, ctl->num, USB_MIXER_S16, 1, mx, |
892 | &opt_pad, &elem); | |
893 | if (err < 0) | |
894 | return err; | |
895 | break; | |
bf2aa5ca JV |
896 | case SCARLETT_SWITCH_GAIN: |
897 | sprintf(mx, "Input %d Gain Switch", ctl->num); | |
898 | err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, | |
899 | scarlett_ctl_enum_resume, 0x01, | |
900 | 0x08, ctl->num, USB_MIXER_S16, 1, mx, | |
901 | &opt_gain, &elem); | |
902 | if (err < 0) | |
903 | return err; | |
904 | break; | |
76b188c4 CA |
905 | } |
906 | } | |
907 | ||
908 | return 0; | |
909 | } | |
910 | ||
911 | /* | |
912 | * Create and initialize a mixer for the Focusrite(R) Scarlett | |
913 | */ | |
914 | int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) | |
915 | { | |
916 | int err, i, o; | |
917 | char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; | |
a01df925 | 918 | const struct scarlett_device_info *info; |
76b188c4 CA |
919 | struct usb_mixer_elem_info *elem; |
920 | static char sample_rate_buffer[4] = { '\x80', '\xbb', '\x00', '\x00' }; | |
921 | ||
922 | /* only use UAC_VERSION_2 */ | |
923 | if (!mixer->protocol) | |
924 | return 0; | |
925 | ||
926 | switch (mixer->chip->usb_id) { | |
927 | case USB_ID(0x1235, 0x8012): | |
928 | info = &s6i6_info; | |
929 | break; | |
930 | case USB_ID(0x1235, 0x8002): | |
931 | info = &s8i6_info; | |
932 | break; | |
933 | case USB_ID(0x1235, 0x8004): | |
934 | info = &s18i6_info; | |
935 | break; | |
936 | case USB_ID(0x1235, 0x8014): | |
937 | info = &s18i8_info; | |
938 | break; | |
939 | case USB_ID(0x1235, 0x800c): | |
940 | info = &s18i20_info; | |
941 | break; | |
942 | default: /* device not (yet) supported */ | |
943 | return -EINVAL; | |
944 | } | |
945 | ||
946 | /* generic function to create controls */ | |
947 | err = scarlett_controls_create_generic(mixer, info); | |
948 | if (err < 0) | |
949 | return err; | |
950 | ||
951 | /* setup matrix controls */ | |
952 | for (i = 0; i < info->matrix_in; i++) { | |
953 | snprintf(mx, sizeof(mx), "Matrix %02d Input Playback Route", | |
954 | i+1); | |
b61f90ea TI |
955 | err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, |
956 | scarlett_ctl_enum_resume, 0x32, | |
76b188c4 CA |
957 | 0x06, i, USB_MIXER_S16, 1, mx, |
958 | &info->opt_matrix, &elem); | |
959 | if (err < 0) | |
960 | return err; | |
961 | ||
962 | for (o = 0; o < info->matrix_out; o++) { | |
963 | sprintf(mx, "Matrix %02d Mix %c Playback Volume", i+1, | |
964 | o+'A'); | |
b61f90ea TI |
965 | err = add_new_ctl(mixer, &usb_scarlett_ctl, |
966 | scarlett_ctl_resume, 0x3c, 0x00, | |
76b188c4 CA |
967 | (i << 3) + (o & 0x07), USB_MIXER_S16, |
968 | 1, mx, NULL, &elem); | |
969 | if (err < 0) | |
970 | return err; | |
971 | ||
972 | } | |
973 | } | |
974 | ||
975 | for (i = 0; i < info->input_len; i++) { | |
976 | snprintf(mx, sizeof(mx), "Input Source %02d Capture Route", | |
977 | i+1); | |
b61f90ea TI |
978 | err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, |
979 | scarlett_ctl_enum_resume, 0x34, | |
76b188c4 CA |
980 | 0x00, i, USB_MIXER_S16, 1, mx, |
981 | &info->opt_master, &elem); | |
982 | if (err < 0) | |
983 | return err; | |
984 | } | |
985 | ||
986 | /* val_len == 1 needed here */ | |
b61f90ea TI |
987 | err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, |
988 | scarlett_ctl_enum_resume, 0x28, 0x01, 0, | |
76b188c4 CA |
989 | USB_MIXER_U8, 1, "Sample Clock Source", |
990 | &opt_clock, &elem); | |
991 | if (err < 0) | |
992 | return err; | |
993 | ||
994 | /* val_len == 1 and UAC2_CS_MEM */ | |
b61f90ea | 995 | err = add_new_ctl(mixer, &usb_scarlett_ctl_sync, NULL, 0x3c, 0x00, 2, |
76b188c4 CA |
996 | USB_MIXER_U8, 1, "Sample Clock Sync Status", |
997 | &opt_sync, &elem); | |
998 | if (err < 0) | |
999 | return err; | |
1000 | ||
1001 | /* initialize sampling rate to 48000 */ | |
1002 | err = snd_usb_ctl_msg(mixer->chip->dev, | |
1003 | usb_sndctrlpipe(mixer->chip->dev, 0), UAC2_CS_CUR, | |
1004 | USB_RECIP_INTERFACE | USB_TYPE_CLASS | | |
1005 | USB_DIR_OUT, 0x0100, snd_usb_ctrl_intf(mixer->chip) | | |
1006 | (0x29 << 8), sample_rate_buffer, 4); | |
1007 | if (err < 0) | |
1008 | return err; | |
1009 | ||
1010 | return err; | |
1011 | } |