Commit | Line | Data |
---|---|---|
7b1eda22 DM |
1 | /* |
2 | * USB Audio Driver for ALSA | |
3 | * | |
4 | * Quirks and vendor-specific extensions for mixer interfaces | |
5 | * | |
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | |
7 | * | |
8 | * Many codes borrowed from audio.c by | |
9 | * Alan Cox (alan@lxorguk.ukuu.org.uk) | |
10 | * Thomas Sailer (sailer@ife.ee.ethz.ch) | |
11 | * | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of the GNU General Public License as published by | |
15 | * the Free Software Foundation; either version 2 of the License, or | |
16 | * (at your option) any later version. | |
17 | * | |
18 | * This program is distributed in the hope that it will be useful, | |
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | * GNU General Public License for more details. | |
22 | * | |
23 | * You should have received a copy of the GNU General Public License | |
24 | * along with this program; if not, write to the Free Software | |
25 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
26 | */ | |
27 | ||
28 | #include <linux/init.h> | |
36db0456 | 29 | #include <linux/slab.h> |
7b1eda22 DM |
30 | #include <linux/usb.h> |
31 | #include <linux/usb/audio.h> | |
32 | ||
33 | #include <sound/core.h> | |
34 | #include <sound/control.h> | |
35 | #include <sound/hwdep.h> | |
36 | #include <sound/info.h> | |
37 | ||
38 | #include "usbaudio.h" | |
f0b5e634 | 39 | #include "mixer.h" |
7b1eda22 DM |
40 | #include "mixer_quirks.h" |
41 | #include "helper.h" | |
42 | ||
d5a0bf6c DM |
43 | extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl; |
44 | ||
7b1eda22 DM |
45 | /* |
46 | * Sound Blaster remote control configuration | |
47 | * | |
48 | * format of remote control data: | |
49 | * Extigy: xx 00 | |
50 | * Audigy 2 NX: 06 80 xx 00 00 00 | |
51 | * Live! 24-bit: 06 80 xx yy 22 83 | |
52 | */ | |
53 | static const struct rc_config { | |
54 | u32 usb_id; | |
55 | u8 offset; | |
56 | u8 length; | |
57 | u8 packet_length; | |
58 | u8 min_packet_length; /* minimum accepted length of the URB result */ | |
59 | u8 mute_mixer_id; | |
60 | u32 mute_code; | |
61 | } rc_configs[] = { | |
62 | { USB_ID(0x041e, 0x3000), 0, 1, 2, 1, 18, 0x0013 }, /* Extigy */ | |
63 | { USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */ | |
64 | { USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */ | |
ca8dc34e | 65 | { USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */ |
7cdd8d73 | 66 | { USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */ |
7b1eda22 DM |
67 | { USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */ |
68 | }; | |
69 | ||
70 | static void snd_usb_soundblaster_remote_complete(struct urb *urb) | |
71 | { | |
72 | struct usb_mixer_interface *mixer = urb->context; | |
73 | const struct rc_config *rc = mixer->rc_cfg; | |
74 | u32 code; | |
75 | ||
76 | if (urb->status < 0 || urb->actual_length < rc->min_packet_length) | |
77 | return; | |
78 | ||
79 | code = mixer->rc_buffer[rc->offset]; | |
80 | if (rc->length == 2) | |
81 | code |= mixer->rc_buffer[rc->offset + 1] << 8; | |
82 | ||
83 | /* the Mute button actually changes the mixer control */ | |
84 | if (code == rc->mute_code) | |
85 | snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id); | |
86 | mixer->rc_code = code; | |
87 | wmb(); | |
88 | wake_up(&mixer->rc_waitq); | |
89 | } | |
90 | ||
91 | static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf, | |
92 | long count, loff_t *offset) | |
93 | { | |
94 | struct usb_mixer_interface *mixer = hw->private_data; | |
95 | int err; | |
96 | u32 rc_code; | |
97 | ||
98 | if (count != 1 && count != 4) | |
99 | return -EINVAL; | |
100 | err = wait_event_interruptible(mixer->rc_waitq, | |
101 | (rc_code = xchg(&mixer->rc_code, 0)) != 0); | |
102 | if (err == 0) { | |
103 | if (count == 1) | |
104 | err = put_user(rc_code, buf); | |
105 | else | |
106 | err = put_user(rc_code, (u32 __user *)buf); | |
107 | } | |
108 | return err < 0 ? err : count; | |
109 | } | |
110 | ||
111 | static unsigned int snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file, | |
112 | poll_table *wait) | |
113 | { | |
114 | struct usb_mixer_interface *mixer = hw->private_data; | |
115 | ||
116 | poll_wait(file, &mixer->rc_waitq, wait); | |
117 | return mixer->rc_code ? POLLIN | POLLRDNORM : 0; | |
118 | } | |
119 | ||
120 | static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer) | |
121 | { | |
122 | struct snd_hwdep *hwdep; | |
123 | int err, len, i; | |
124 | ||
125 | for (i = 0; i < ARRAY_SIZE(rc_configs); ++i) | |
126 | if (rc_configs[i].usb_id == mixer->chip->usb_id) | |
127 | break; | |
128 | if (i >= ARRAY_SIZE(rc_configs)) | |
129 | return 0; | |
130 | mixer->rc_cfg = &rc_configs[i]; | |
131 | ||
132 | len = mixer->rc_cfg->packet_length; | |
133 | ||
134 | init_waitqueue_head(&mixer->rc_waitq); | |
135 | err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep); | |
136 | if (err < 0) | |
137 | return err; | |
138 | snprintf(hwdep->name, sizeof(hwdep->name), | |
139 | "%s remote control", mixer->chip->card->shortname); | |
140 | hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC; | |
141 | hwdep->private_data = mixer; | |
142 | hwdep->ops.read = snd_usb_sbrc_hwdep_read; | |
143 | hwdep->ops.poll = snd_usb_sbrc_hwdep_poll; | |
144 | hwdep->exclusive = 1; | |
145 | ||
146 | mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL); | |
147 | if (!mixer->rc_urb) | |
148 | return -ENOMEM; | |
149 | mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL); | |
150 | if (!mixer->rc_setup_packet) { | |
151 | usb_free_urb(mixer->rc_urb); | |
152 | mixer->rc_urb = NULL; | |
153 | return -ENOMEM; | |
154 | } | |
155 | mixer->rc_setup_packet->bRequestType = | |
156 | USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; | |
157 | mixer->rc_setup_packet->bRequest = UAC_GET_MEM; | |
158 | mixer->rc_setup_packet->wValue = cpu_to_le16(0); | |
159 | mixer->rc_setup_packet->wIndex = cpu_to_le16(0); | |
160 | mixer->rc_setup_packet->wLength = cpu_to_le16(len); | |
161 | usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev, | |
162 | usb_rcvctrlpipe(mixer->chip->dev, 0), | |
163 | (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len, | |
164 | snd_usb_soundblaster_remote_complete, mixer); | |
165 | return 0; | |
166 | } | |
167 | ||
168 | #define snd_audigy2nx_led_info snd_ctl_boolean_mono_info | |
169 | ||
170 | static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) | |
171 | { | |
172 | struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); | |
173 | int index = kcontrol->private_value; | |
174 | ||
175 | ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index]; | |
176 | return 0; | |
177 | } | |
178 | ||
179 | static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) | |
180 | { | |
181 | struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); | |
182 | int index = kcontrol->private_value; | |
183 | int value = ucontrol->value.integer.value[0]; | |
184 | int err, changed; | |
185 | ||
186 | if (value > 1) | |
187 | return -EINVAL; | |
188 | changed = value != mixer->audigy2nx_leds[index]; | |
ca8dc34e MJ |
189 | if (mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) |
190 | err = snd_usb_ctl_msg(mixer->chip->dev, | |
191 | usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, | |
192 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, | |
17d900c4 | 193 | !value, 0, NULL, 0); |
7cdd8d73 MB |
194 | /* USB X-Fi S51 Pro */ |
195 | if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) | |
196 | err = snd_usb_ctl_msg(mixer->chip->dev, | |
197 | usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, | |
198 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, | |
17d900c4 | 199 | !value, 0, NULL, 0); |
ca8dc34e MJ |
200 | else |
201 | err = snd_usb_ctl_msg(mixer->chip->dev, | |
7b1eda22 DM |
202 | usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, |
203 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, | |
17d900c4 | 204 | value, index + 2, NULL, 0); |
7b1eda22 DM |
205 | if (err < 0) |
206 | return err; | |
207 | mixer->audigy2nx_leds[index] = value; | |
208 | return changed; | |
209 | } | |
210 | ||
211 | static struct snd_kcontrol_new snd_audigy2nx_controls[] = { | |
212 | { | |
213 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
214 | .name = "CMSS LED Switch", | |
215 | .info = snd_audigy2nx_led_info, | |
216 | .get = snd_audigy2nx_led_get, | |
217 | .put = snd_audigy2nx_led_put, | |
218 | .private_value = 0, | |
219 | }, | |
220 | { | |
221 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
222 | .name = "Power LED Switch", | |
223 | .info = snd_audigy2nx_led_info, | |
224 | .get = snd_audigy2nx_led_get, | |
225 | .put = snd_audigy2nx_led_put, | |
226 | .private_value = 1, | |
227 | }, | |
228 | { | |
229 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
230 | .name = "Dolby Digital LED Switch", | |
231 | .info = snd_audigy2nx_led_info, | |
232 | .get = snd_audigy2nx_led_get, | |
233 | .put = snd_audigy2nx_led_put, | |
234 | .private_value = 2, | |
235 | }, | |
236 | }; | |
237 | ||
238 | static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) | |
239 | { | |
240 | int i, err; | |
241 | ||
242 | for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) { | |
ca8dc34e MJ |
243 | /* USB X-Fi S51 doesn't have a CMSS LED */ |
244 | if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0) | |
245 | continue; | |
7cdd8d73 MB |
246 | /* USB X-Fi S51 Pro doesn't have one either */ |
247 | if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0) | |
248 | continue; | |
7b1eda22 DM |
249 | if (i > 1 && /* Live24ext has 2 LEDs only */ |
250 | (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || | |
ca8dc34e | 251 | mixer->chip->usb_id == USB_ID(0x041e, 0x3042) || |
7cdd8d73 | 252 | mixer->chip->usb_id == USB_ID(0x041e, 0x30df) || |
7b1eda22 DM |
253 | mixer->chip->usb_id == USB_ID(0x041e, 0x3048))) |
254 | break; | |
255 | err = snd_ctl_add(mixer->chip->card, | |
256 | snd_ctl_new1(&snd_audigy2nx_controls[i], mixer)); | |
257 | if (err < 0) | |
258 | return err; | |
259 | } | |
260 | mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */ | |
261 | return 0; | |
262 | } | |
263 | ||
264 | static void snd_audigy2nx_proc_read(struct snd_info_entry *entry, | |
265 | struct snd_info_buffer *buffer) | |
266 | { | |
267 | static const struct sb_jack { | |
268 | int unitid; | |
269 | const char *name; | |
270 | } jacks_audigy2nx[] = { | |
271 | {4, "dig in "}, | |
272 | {7, "line in"}, | |
273 | {19, "spk out"}, | |
274 | {20, "hph out"}, | |
275 | {-1, NULL} | |
276 | }, jacks_live24ext[] = { | |
277 | {4, "line in"}, /* &1=Line, &2=Mic*/ | |
278 | {3, "hph out"}, /* headphones */ | |
279 | {0, "RC "}, /* last command, 6 bytes see rc_config above */ | |
280 | {-1, NULL} | |
281 | }; | |
282 | const struct sb_jack *jacks; | |
283 | struct usb_mixer_interface *mixer = entry->private_data; | |
284 | int i, err; | |
285 | u8 buf[3]; | |
286 | ||
287 | snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname); | |
288 | if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020)) | |
289 | jacks = jacks_audigy2nx; | |
290 | else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || | |
291 | mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) | |
292 | jacks = jacks_live24ext; | |
293 | else | |
294 | return; | |
295 | ||
296 | for (i = 0; jacks[i].name; ++i) { | |
297 | snd_iprintf(buffer, "%s: ", jacks[i].name); | |
298 | err = snd_usb_ctl_msg(mixer->chip->dev, | |
299 | usb_rcvctrlpipe(mixer->chip->dev, 0), | |
300 | UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS | | |
301 | USB_RECIP_INTERFACE, 0, | |
17d900c4 | 302 | jacks[i].unitid << 8, buf, 3); |
7b1eda22 DM |
303 | if (err == 3 && (buf[0] == 3 || buf[0] == 6)) |
304 | snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]); | |
305 | else | |
306 | snd_iprintf(buffer, "?\n"); | |
307 | } | |
308 | } | |
309 | ||
310 | static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, | |
311 | struct snd_ctl_elem_value *ucontrol) | |
312 | { | |
313 | struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); | |
314 | ||
315 | ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02); | |
316 | return 0; | |
317 | } | |
318 | ||
319 | static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol, | |
320 | struct snd_ctl_elem_value *ucontrol) | |
321 | { | |
322 | struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); | |
323 | u8 old_status, new_status; | |
324 | int err, changed; | |
325 | ||
326 | old_status = mixer->xonar_u1_status; | |
327 | if (ucontrol->value.integer.value[0]) | |
328 | new_status = old_status | 0x02; | |
329 | else | |
330 | new_status = old_status & ~0x02; | |
331 | changed = new_status != old_status; | |
332 | err = snd_usb_ctl_msg(mixer->chip->dev, | |
333 | usb_sndctrlpipe(mixer->chip->dev, 0), 0x08, | |
334 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, | |
17d900c4 | 335 | 50, 0, &new_status, 1); |
7b1eda22 DM |
336 | if (err < 0) |
337 | return err; | |
338 | mixer->xonar_u1_status = new_status; | |
339 | return changed; | |
340 | } | |
341 | ||
342 | static struct snd_kcontrol_new snd_xonar_u1_output_switch = { | |
343 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
344 | .name = "Digital Playback Switch", | |
345 | .info = snd_ctl_boolean_mono_info, | |
346 | .get = snd_xonar_u1_switch_get, | |
347 | .put = snd_xonar_u1_switch_put, | |
348 | }; | |
349 | ||
350 | static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer) | |
351 | { | |
352 | int err; | |
353 | ||
354 | err = snd_ctl_add(mixer->chip->card, | |
355 | snd_ctl_new1(&snd_xonar_u1_output_switch, mixer)); | |
356 | if (err < 0) | |
357 | return err; | |
358 | mixer->xonar_u1_status = 0x05; | |
359 | return 0; | |
360 | } | |
361 | ||
54a8c500 DM |
362 | /* Native Instruments device quirks */ |
363 | ||
364 | #define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex)) | |
365 | ||
366 | static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol, | |
367 | struct snd_ctl_elem_value *ucontrol) | |
368 | { | |
369 | struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); | |
370 | struct usb_device *dev = mixer->chip->dev; | |
371 | u8 bRequest = (kcontrol->private_value >> 16) & 0xff; | |
372 | u16 wIndex = kcontrol->private_value & 0xffff; | |
373 | u8 tmp; | |
374 | ||
375 | int ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest, | |
376 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | |
377 | 0, cpu_to_le16(wIndex), | |
378 | &tmp, sizeof(tmp), 1000); | |
379 | ||
380 | if (ret < 0) { | |
381 | snd_printk(KERN_ERR | |
382 | "unable to issue vendor read request (ret = %d)", ret); | |
383 | return ret; | |
384 | } | |
385 | ||
386 | ucontrol->value.integer.value[0] = tmp; | |
387 | ||
388 | return 0; | |
389 | } | |
390 | ||
391 | static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, | |
392 | struct snd_ctl_elem_value *ucontrol) | |
393 | { | |
394 | struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); | |
395 | struct usb_device *dev = mixer->chip->dev; | |
396 | u8 bRequest = (kcontrol->private_value >> 16) & 0xff; | |
397 | u16 wIndex = kcontrol->private_value & 0xffff; | |
398 | u16 wValue = ucontrol->value.integer.value[0]; | |
399 | ||
400 | int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), bRequest, | |
401 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, | |
402 | cpu_to_le16(wValue), cpu_to_le16(wIndex), | |
403 | NULL, 0, 1000); | |
404 | ||
405 | if (ret < 0) { | |
406 | snd_printk(KERN_ERR | |
407 | "unable to issue vendor write request (ret = %d)", ret); | |
408 | return ret; | |
409 | } | |
410 | ||
411 | return 0; | |
412 | } | |
413 | ||
414 | static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { | |
415 | { | |
416 | .name = "Direct Thru Channel A", | |
417 | .private_value = _MAKE_NI_CONTROL(0x01, 0x03), | |
418 | }, | |
419 | { | |
420 | .name = "Direct Thru Channel B", | |
421 | .private_value = _MAKE_NI_CONTROL(0x01, 0x05), | |
422 | }, | |
423 | { | |
424 | .name = "Phono Input Channel A", | |
425 | .private_value = _MAKE_NI_CONTROL(0x02, 0x03), | |
426 | }, | |
427 | { | |
428 | .name = "Phono Input Channel B", | |
429 | .private_value = _MAKE_NI_CONTROL(0x02, 0x05), | |
430 | }, | |
431 | }; | |
432 | ||
433 | static struct snd_kcontrol_new snd_nativeinstruments_ta10_mixers[] = { | |
434 | { | |
435 | .name = "Direct Thru Channel A", | |
436 | .private_value = _MAKE_NI_CONTROL(0x01, 0x03), | |
437 | }, | |
438 | { | |
439 | .name = "Direct Thru Channel B", | |
440 | .private_value = _MAKE_NI_CONTROL(0x01, 0x05), | |
441 | }, | |
442 | { | |
443 | .name = "Direct Thru Channel C", | |
444 | .private_value = _MAKE_NI_CONTROL(0x01, 0x07), | |
445 | }, | |
446 | { | |
447 | .name = "Direct Thru Channel D", | |
448 | .private_value = _MAKE_NI_CONTROL(0x01, 0x09), | |
449 | }, | |
450 | { | |
451 | .name = "Phono Input Channel A", | |
452 | .private_value = _MAKE_NI_CONTROL(0x02, 0x03), | |
453 | }, | |
454 | { | |
455 | .name = "Phono Input Channel B", | |
456 | .private_value = _MAKE_NI_CONTROL(0x02, 0x05), | |
457 | }, | |
458 | { | |
459 | .name = "Phono Input Channel C", | |
460 | .private_value = _MAKE_NI_CONTROL(0x02, 0x07), | |
461 | }, | |
462 | { | |
463 | .name = "Phono Input Channel D", | |
464 | .private_value = _MAKE_NI_CONTROL(0x02, 0x09), | |
465 | }, | |
466 | }; | |
467 | ||
468 | static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, | |
469 | const struct snd_kcontrol_new *kc, | |
470 | unsigned int count) | |
471 | { | |
472 | int i, err = 0; | |
473 | struct snd_kcontrol_new template = { | |
474 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
475 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
476 | .get = snd_nativeinstruments_control_get, | |
477 | .put = snd_nativeinstruments_control_put, | |
478 | .info = snd_ctl_boolean_mono_info, | |
479 | }; | |
480 | ||
481 | for (i = 0; i < count; i++) { | |
482 | struct snd_kcontrol *c; | |
483 | ||
484 | template.name = kc[i].name; | |
485 | template.private_value = kc[i].private_value; | |
486 | ||
487 | c = snd_ctl_new1(&template, mixer); | |
488 | err = snd_ctl_add(mixer->chip->card, c); | |
489 | ||
490 | if (err < 0) | |
491 | break; | |
492 | } | |
493 | ||
494 | return err; | |
495 | } | |
496 | ||
d5a0bf6c DM |
497 | /* M-Audio FastTrack Ultra quirks */ |
498 | ||
499 | /* private_free callback */ | |
500 | static void usb_mixer_elem_free(struct snd_kcontrol *kctl) | |
501 | { | |
502 | kfree(kctl->private_data); | |
503 | kctl->private_data = NULL; | |
504 | } | |
505 | ||
506 | static int snd_maudio_ftu_create_ctl(struct usb_mixer_interface *mixer, | |
507 | int in, int out, const char *name) | |
508 | { | |
509 | struct usb_mixer_elem_info *cval; | |
510 | struct snd_kcontrol *kctl; | |
511 | ||
512 | cval = kzalloc(sizeof(*cval), GFP_KERNEL); | |
513 | if (!cval) | |
514 | return -ENOMEM; | |
515 | ||
516 | cval->id = 5; | |
517 | cval->mixer = mixer; | |
518 | cval->val_type = USB_MIXER_S16; | |
519 | cval->channels = 1; | |
520 | cval->control = out + 1; | |
521 | cval->cmask = 1 << in; | |
522 | ||
523 | kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval); | |
524 | if (!kctl) { | |
525 | kfree(cval); | |
526 | return -ENOMEM; | |
527 | } | |
528 | ||
529 | snprintf(kctl->id.name, sizeof(kctl->id.name), name); | |
530 | kctl->private_free = usb_mixer_elem_free; | |
531 | return snd_usb_mixer_add_control(mixer, kctl); | |
532 | } | |
533 | ||
534 | static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer) | |
535 | { | |
536 | char name[64]; | |
537 | int in, out, err; | |
538 | ||
539 | for (out = 0; out < 8; out++) { | |
540 | for (in = 0; in < 8; in++) { | |
541 | snprintf(name, sizeof(name), | |
542 | "AIn%d - Out%d Capture Volume", in + 1, out + 1); | |
543 | err = snd_maudio_ftu_create_ctl(mixer, in, out, name); | |
544 | if (err < 0) | |
545 | return err; | |
546 | } | |
547 | ||
548 | for (in = 8; in < 16; in++) { | |
549 | snprintf(name, sizeof(name), | |
550 | "DIn%d - Out%d Playback Volume", in - 7, out + 1); | |
551 | err = snd_maudio_ftu_create_ctl(mixer, in, out, name); | |
552 | if (err < 0) | |
553 | return err; | |
554 | } | |
555 | } | |
556 | ||
557 | return 0; | |
558 | } | |
559 | ||
7b1eda22 DM |
560 | void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, |
561 | unsigned char samplerate_id) | |
562 | { | |
563 | struct usb_mixer_interface *mixer; | |
564 | struct usb_mixer_elem_info *cval; | |
565 | int unitid = 12; /* SamleRate ExtensionUnit ID */ | |
566 | ||
567 | list_for_each_entry(mixer, &chip->mixer_list, list) { | |
568 | cval = mixer->id_elems[unitid]; | |
569 | if (cval) { | |
570 | snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, | |
571 | cval->control << 8, | |
572 | samplerate_id); | |
573 | snd_usb_mixer_notify_id(mixer, unitid); | |
574 | } | |
575 | break; | |
576 | } | |
577 | } | |
578 | ||
579 | int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) | |
580 | { | |
3347b26c | 581 | int err = 0; |
7b1eda22 DM |
582 | struct snd_info_entry *entry; |
583 | ||
584 | if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0) | |
585 | return err; | |
586 | ||
3347b26c DM |
587 | switch (mixer->chip->usb_id) { |
588 | case USB_ID(0x041e, 0x3020): | |
589 | case USB_ID(0x041e, 0x3040): | |
590 | case USB_ID(0x041e, 0x3042): | |
7cdd8d73 | 591 | case USB_ID(0x041e, 0x30df): |
3347b26c DM |
592 | case USB_ID(0x041e, 0x3048): |
593 | err = snd_audigy2nx_controls_create(mixer); | |
594 | if (err < 0) | |
595 | break; | |
7b1eda22 DM |
596 | if (!snd_card_proc_new(mixer->chip->card, "audigy2nx", &entry)) |
597 | snd_info_set_text_ops(entry, mixer, | |
598 | snd_audigy2nx_proc_read); | |
3347b26c | 599 | break; |
7b1eda22 | 600 | |
d5a0bf6c DM |
601 | case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */ |
602 | case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ | |
603 | err = snd_maudio_ftu_create_mixer(mixer); | |
604 | break; | |
605 | ||
3347b26c DM |
606 | case USB_ID(0x0b05, 0x1739): |
607 | case USB_ID(0x0b05, 0x1743): | |
7b1eda22 | 608 | err = snd_xonar_u1_controls_create(mixer); |
3347b26c | 609 | break; |
7b1eda22 | 610 | |
3347b26c | 611 | case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ |
54a8c500 DM |
612 | err = snd_nativeinstruments_create_mixer(mixer, |
613 | snd_nativeinstruments_ta6_mixers, | |
614 | ARRAY_SIZE(snd_nativeinstruments_ta6_mixers)); | |
3347b26c | 615 | break; |
54a8c500 | 616 | |
3347b26c | 617 | case USB_ID(0x17cc, 0x1021): /* Traktor Audio 10 */ |
54a8c500 DM |
618 | err = snd_nativeinstruments_create_mixer(mixer, |
619 | snd_nativeinstruments_ta10_mixers, | |
620 | ARRAY_SIZE(snd_nativeinstruments_ta10_mixers)); | |
3347b26c | 621 | break; |
54a8c500 DM |
622 | } |
623 | ||
3347b26c | 624 | return err; |
7b1eda22 DM |
625 | } |
626 | ||
627 | void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer, | |
628 | int unitid) | |
629 | { | |
630 | if (!mixer->rc_cfg) | |
631 | return; | |
632 | /* unit ids specific to Extigy/Audigy 2 NX: */ | |
633 | switch (unitid) { | |
634 | case 0: /* remote control */ | |
635 | mixer->rc_urb->dev = mixer->chip->dev; | |
636 | usb_submit_urb(mixer->rc_urb, GFP_ATOMIC); | |
637 | break; | |
638 | case 4: /* digital in jack */ | |
639 | case 7: /* line in jacks */ | |
640 | case 19: /* speaker out jacks */ | |
641 | case 20: /* headphones out jack */ | |
642 | break; | |
643 | /* live24ext: 4 = line-in jack */ | |
644 | case 3: /* hp-out jack (may actuate Mute) */ | |
645 | if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || | |
646 | mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) | |
647 | snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id); | |
648 | break; | |
649 | default: | |
650 | snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid); | |
651 | break; | |
652 | } | |
653 | } | |
654 |