[ALSA] snd-ca0106: Tidy up volume controls
[linux-2.6.git] / sound / pci / ca0106 / ca0106_mixer.c
1 /*
2  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
3  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
4  *  Version: 0.0.17
5  *
6  *  FEATURES currently supported:
7  *    See ca0106_main.c for features.
8  * 
9  *  Changelog:
10  *    Support interrupts per period.
11  *    Removed noise from Center/LFE channel when in Analog mode.
12  *    Rename and remove mixer controls.
13  *  0.0.6
14  *    Use separate card based DMA buffer for periods table list.
15  *  0.0.7
16  *    Change remove and rename ctrls into lists.
17  *  0.0.8
18  *    Try to fix capture sources.
19  *  0.0.9
20  *    Fix AC3 output.
21  *    Enable S32_LE format support.
22  *  0.0.10
23  *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
24  *  0.0.11
25  *    Add Model name recognition.
26  *  0.0.12
27  *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
28  *    Remove redundent "voice" handling.
29  *  0.0.13
30  *    Single trigger call for multi channels.
31  *  0.0.14
32  *    Set limits based on what the sound card hardware can do.
33  *    playback periods_min=2, periods_max=8
34  *    capture hw constraints require period_size = n * 64 bytes.
35  *    playback hw constraints require period_size = n * 64 bytes.
36  *  0.0.15
37  *    Separated ca0106.c into separate functional .c files.
38  *  0.0.16
39  *    Modified Copyright message.
40  *  0.0.17
41  *    Implement Mic and Line in Capture.
42  *
43  *  This code was initally based on code from ALSA's emu10k1x.c which is:
44  *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
45  *
46  *   This program is free software; you can redistribute it and/or modify
47  *   it under the terms of the GNU General Public License as published by
48  *   the Free Software Foundation; either version 2 of the License, or
49  *   (at your option) any later version.
50  *
51  *   This program is distributed in the hope that it will be useful,
52  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
53  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
54  *   GNU General Public License for more details.
55  *
56  *   You should have received a copy of the GNU General Public License
57  *   along with this program; if not, write to the Free Software
58  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
59  *
60  */
61 #include <sound/driver.h>
62 #include <linux/delay.h>
63 #include <linux/init.h>
64 #include <linux/interrupt.h>
65 #include <linux/pci.h>
66 #include <linux/slab.h>
67 #include <linux/moduleparam.h>
68 #include <sound/core.h>
69 #include <sound/initval.h>
70 #include <sound/pcm.h>
71 #include <sound/ac97_codec.h>
72 #include <sound/info.h>
73
74 #include "ca0106.h"
75
76 static int snd_ca0106_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
77 {
78         uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
79         uinfo->count = 1;
80         uinfo->value.integer.min = 0;
81         uinfo->value.integer.max = 1;
82         return 0;
83 }
84
85 static int snd_ca0106_shared_spdif_get(snd_kcontrol_t * kcontrol,
86                                         snd_ctl_elem_value_t * ucontrol)
87 {
88         ca0106_t *emu = snd_kcontrol_chip(kcontrol);
89
90         ucontrol->value.enumerated.item[0] = emu->spdif_enable;
91         return 0;
92 }
93
94 static int snd_ca0106_shared_spdif_put(snd_kcontrol_t * kcontrol,
95                                         snd_ctl_elem_value_t * ucontrol)
96 {
97         ca0106_t *emu = snd_kcontrol_chip(kcontrol);
98         unsigned int val;
99         int change = 0;
100         u32 mask;
101
102         val = ucontrol->value.enumerated.item[0] ;
103         change = (emu->spdif_enable != val);
104         if (change) {
105                 emu->spdif_enable = val;
106                 if (val == 1) {
107                         /* Digital */
108                         snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
109                         snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
110                         snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
111                                 snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
112                         mask = inl(emu->port + GPIO) & ~0x101;
113                         outl(mask, emu->port + GPIO);
114
115                 } else {
116                         /* Analog */
117                         snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
118                         snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
119                         snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
120                                 snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
121                         mask = inl(emu->port + GPIO) | 0x101;
122                         outl(mask, emu->port + GPIO);
123                 }
124         }
125         return change;
126 }
127
128 static snd_kcontrol_new_t snd_ca0106_shared_spdif __devinitdata =
129 {
130         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
131         .name =         "SPDIF Out",
132         .info =         snd_ca0106_shared_spdif_info,
133         .get =          snd_ca0106_shared_spdif_get,
134         .put =          snd_ca0106_shared_spdif_put
135 };
136
137 static int snd_ca0106_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
138 {
139         static char *texts[6] = { "SPDIF out", "i2s mixer out", "SPDIF in", "i2s in", "AC97 in", "SRC out" };
140
141         uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
142         uinfo->count = 1;
143         uinfo->value.enumerated.items = 6;
144         if (uinfo->value.enumerated.item > 5)
145                 uinfo->value.enumerated.item = 5;
146         strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
147         return 0;
148 }
149
150 static int snd_ca0106_capture_source_get(snd_kcontrol_t * kcontrol,
151                                         snd_ctl_elem_value_t * ucontrol)
152 {
153         ca0106_t *emu = snd_kcontrol_chip(kcontrol);
154
155         ucontrol->value.enumerated.item[0] = emu->capture_source;
156         return 0;
157 }
158
159 static int snd_ca0106_capture_source_put(snd_kcontrol_t * kcontrol,
160                                         snd_ctl_elem_value_t * ucontrol)
161 {
162         ca0106_t *emu = snd_kcontrol_chip(kcontrol);
163         unsigned int val;
164         int change = 0;
165         u32 mask;
166         u32 source;
167
168         val = ucontrol->value.enumerated.item[0] ;
169         change = (emu->capture_source != val);
170         if (change) {
171                 emu->capture_source = val;
172                 source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
173                 mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
174                 snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
175         }
176         return change;
177 }
178
179 static snd_kcontrol_new_t snd_ca0106_capture_source __devinitdata =
180 {
181         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
182         .name =         "Capture Source",
183         .info =         snd_ca0106_capture_source_info,
184         .get =          snd_ca0106_capture_source_get,
185         .put =          snd_ca0106_capture_source_put
186 };
187
188 static int snd_ca0106_capture_mic_line_in_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
189 {
190         static char *texts[2] = { "Line in", "Mic in" };
191
192         uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
193         uinfo->count = 1;
194         uinfo->value.enumerated.items = 2;
195         if (uinfo->value.enumerated.item > 1)
196                 uinfo->value.enumerated.item = 1;
197         strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
198         return 0;
199 }
200
201 static int snd_ca0106_capture_mic_line_in_get(snd_kcontrol_t * kcontrol,
202                                         snd_ctl_elem_value_t * ucontrol)
203 {
204         ca0106_t *emu = snd_kcontrol_chip(kcontrol);
205
206         ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
207         return 0;
208 }
209
210 static int snd_ca0106_capture_mic_line_in_put(snd_kcontrol_t * kcontrol,
211                                         snd_ctl_elem_value_t * ucontrol)
212 {
213         ca0106_t *emu = snd_kcontrol_chip(kcontrol);
214         unsigned int val;
215         int change = 0;
216         u32 tmp;
217
218         val = ucontrol->value.enumerated.item[0] ;
219         change = (emu->capture_mic_line_in != val);
220         if (change) {
221                 emu->capture_mic_line_in = val;
222                 if (val) {
223                         snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */
224                         tmp = inl(emu->port+GPIO) & ~0x400;
225                         tmp = tmp | 0x400;
226                         outl(tmp, emu->port+GPIO);
227                         snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
228                 } else {
229                         snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */
230                         tmp = inl(emu->port+GPIO) & ~0x400;
231                         outl(tmp, emu->port+GPIO);
232                         snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
233                 }
234         }
235         return change;
236 }
237
238 static snd_kcontrol_new_t snd_ca0106_capture_mic_line_in __devinitdata =
239 {
240         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
241         .name =         "Mic/Line in Capture",
242         .info =         snd_ca0106_capture_mic_line_in_info,
243         .get =          snd_ca0106_capture_mic_line_in_get,
244         .put =          snd_ca0106_capture_mic_line_in_put
245 };
246
247 static int snd_ca0106_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
248 {
249         uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
250         uinfo->count = 1;
251         return 0;
252 }
253
254 static int snd_ca0106_spdif_get(snd_kcontrol_t * kcontrol,
255                                  snd_ctl_elem_value_t * ucontrol)
256 {
257         ca0106_t *emu = snd_kcontrol_chip(kcontrol);
258         unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
259
260         ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
261         ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
262         ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
263         ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
264         return 0;
265 }
266
267 static int snd_ca0106_spdif_get_mask(snd_kcontrol_t * kcontrol,
268                                       snd_ctl_elem_value_t * ucontrol)
269 {
270         ucontrol->value.iec958.status[0] = 0xff;
271         ucontrol->value.iec958.status[1] = 0xff;
272         ucontrol->value.iec958.status[2] = 0xff;
273         ucontrol->value.iec958.status[3] = 0xff;
274         return 0;
275 }
276
277 static int snd_ca0106_spdif_put(snd_kcontrol_t * kcontrol,
278                                  snd_ctl_elem_value_t * ucontrol)
279 {
280         ca0106_t *emu = snd_kcontrol_chip(kcontrol);
281         unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
282         int change;
283         unsigned int val;
284
285         val = (ucontrol->value.iec958.status[0] << 0) |
286               (ucontrol->value.iec958.status[1] << 8) |
287               (ucontrol->value.iec958.status[2] << 16) |
288               (ucontrol->value.iec958.status[3] << 24);
289         change = val != emu->spdif_bits[idx];
290         if (change) {
291                 snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
292                 emu->spdif_bits[idx] = val;
293         }
294         return change;
295 }
296
297 static snd_kcontrol_new_t snd_ca0106_spdif_mask_control =
298 {
299         .access =       SNDRV_CTL_ELEM_ACCESS_READ,
300         .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
301         .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
302         .count =        4,
303         .info =         snd_ca0106_spdif_info,
304         .get =          snd_ca0106_spdif_get_mask
305 };
306
307 static snd_kcontrol_new_t snd_ca0106_spdif_control =
308 {
309         .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
310         .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
311         .count =        4,
312         .info =         snd_ca0106_spdif_info,
313         .get =          snd_ca0106_spdif_get,
314         .put =          snd_ca0106_spdif_put
315 };
316
317 static int snd_ca0106_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
318 {
319         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
320         uinfo->count = 2;
321         uinfo->value.integer.min = 0;
322         uinfo->value.integer.max = 255;
323         return 0;
324 }
325
326 static int snd_ca0106_volume_get(snd_kcontrol_t * kcontrol,
327                                        snd_ctl_elem_value_t * ucontrol, int reg, int channel_id)
328 {
329         ca0106_t *emu = snd_kcontrol_chip(kcontrol);
330         unsigned int value;
331
332         value = snd_ca0106_ptr_read(emu, reg, channel_id);
333         ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
334         ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
335         return 0;
336 }
337
338 static int snd_ca0106_volume_get_spdif_front(snd_kcontrol_t * kcontrol,
339                                        snd_ctl_elem_value_t * ucontrol)
340 {
341         int channel_id = CONTROL_FRONT_CHANNEL;
342         int reg = PLAYBACK_VOLUME1;
343         return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
344 }
345
346 static int snd_ca0106_volume_get_spdif_center_lfe(snd_kcontrol_t * kcontrol,
347                                        snd_ctl_elem_value_t * ucontrol)
348 {
349         int channel_id = CONTROL_CENTER_LFE_CHANNEL;
350         int reg = PLAYBACK_VOLUME1;
351         return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
352 }
353 static int snd_ca0106_volume_get_spdif_unknown(snd_kcontrol_t * kcontrol,
354                                        snd_ctl_elem_value_t * ucontrol)
355 {
356         int channel_id = CONTROL_UNKNOWN_CHANNEL;
357         int reg = PLAYBACK_VOLUME1;
358         return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
359 }
360 static int snd_ca0106_volume_get_spdif_rear(snd_kcontrol_t * kcontrol,
361                                        snd_ctl_elem_value_t * ucontrol)
362 {
363         int channel_id = CONTROL_REAR_CHANNEL;
364         int reg = PLAYBACK_VOLUME1;
365         return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
366 }
367 static int snd_ca0106_volume_get_analog_front(snd_kcontrol_t * kcontrol,
368                                        snd_ctl_elem_value_t * ucontrol)
369 {
370         int channel_id = CONTROL_FRONT_CHANNEL;
371         int reg = PLAYBACK_VOLUME2;
372         return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
373 }
374
375 static int snd_ca0106_volume_get_analog_center_lfe(snd_kcontrol_t * kcontrol,
376                                        snd_ctl_elem_value_t * ucontrol)
377 {
378         int channel_id = CONTROL_CENTER_LFE_CHANNEL;
379         int reg = PLAYBACK_VOLUME2;
380         return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
381 }
382 static int snd_ca0106_volume_get_analog_unknown(snd_kcontrol_t * kcontrol,
383                                        snd_ctl_elem_value_t * ucontrol)
384 {
385         int channel_id = CONTROL_UNKNOWN_CHANNEL;
386         int reg = PLAYBACK_VOLUME2;
387         return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
388 }
389 static int snd_ca0106_volume_get_analog_rear(snd_kcontrol_t * kcontrol,
390                                        snd_ctl_elem_value_t * ucontrol)
391 {
392         int channel_id = CONTROL_REAR_CHANNEL;
393         int reg = PLAYBACK_VOLUME2;
394         return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
395 }
396
397 static int snd_ca0106_volume_get_feedback(snd_kcontrol_t * kcontrol,
398                                        snd_ctl_elem_value_t * ucontrol)
399 {
400         int channel_id = 1;
401         int reg = CAPTURE_CONTROL;
402         return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
403 }
404                                                                                                                            
405 static int snd_ca0106_volume_put(snd_kcontrol_t * kcontrol,
406                                        snd_ctl_elem_value_t * ucontrol, int reg, int channel_id)
407 {
408         ca0106_t *emu = snd_kcontrol_chip(kcontrol);
409         unsigned int value;
410         //value = snd_ca0106_ptr_read(emu, reg, channel_id);
411         //value = value & 0xffff;
412         value = ((0xff - ucontrol->value.integer.value[0]) << 24) | ((0xff - ucontrol->value.integer.value[1]) << 16);
413         value = value | ((0xff - ucontrol->value.integer.value[0]) << 8) | ((0xff - ucontrol->value.integer.value[1]) );
414         snd_ca0106_ptr_write(emu, reg, channel_id, value);
415         return 1;
416 }
417 static int snd_ca0106_volume_put_spdif_front(snd_kcontrol_t * kcontrol,
418                                        snd_ctl_elem_value_t * ucontrol)
419 {
420         int channel_id = CONTROL_FRONT_CHANNEL;
421         int reg = PLAYBACK_VOLUME1;
422         return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
423 }
424 static int snd_ca0106_volume_put_spdif_center_lfe(snd_kcontrol_t * kcontrol,
425                                        snd_ctl_elem_value_t * ucontrol)
426 {
427         int channel_id = CONTROL_CENTER_LFE_CHANNEL;
428         int reg = PLAYBACK_VOLUME1;
429         return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
430 }
431 static int snd_ca0106_volume_put_spdif_unknown(snd_kcontrol_t * kcontrol,
432                                        snd_ctl_elem_value_t * ucontrol)
433 {
434         int channel_id = CONTROL_UNKNOWN_CHANNEL;
435         int reg = PLAYBACK_VOLUME1;
436         return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
437 }
438 static int snd_ca0106_volume_put_spdif_rear(snd_kcontrol_t * kcontrol,
439                                        snd_ctl_elem_value_t * ucontrol)
440 {
441         int channel_id = CONTROL_REAR_CHANNEL;
442         int reg = PLAYBACK_VOLUME1;
443         return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
444 }
445 static int snd_ca0106_volume_put_analog_front(snd_kcontrol_t * kcontrol,
446                                        snd_ctl_elem_value_t * ucontrol)
447 {
448         int channel_id = CONTROL_FRONT_CHANNEL;
449         int reg = PLAYBACK_VOLUME2;
450         return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
451 }
452 static int snd_ca0106_volume_put_analog_center_lfe(snd_kcontrol_t * kcontrol,
453                                        snd_ctl_elem_value_t * ucontrol)
454 {
455         int channel_id = CONTROL_CENTER_LFE_CHANNEL;
456         int reg = PLAYBACK_VOLUME2;
457         return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
458 }
459 static int snd_ca0106_volume_put_analog_unknown(snd_kcontrol_t * kcontrol,
460                                        snd_ctl_elem_value_t * ucontrol)
461 {
462         int channel_id = CONTROL_UNKNOWN_CHANNEL;
463         int reg = PLAYBACK_VOLUME2;
464         return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
465 }
466 static int snd_ca0106_volume_put_analog_rear(snd_kcontrol_t * kcontrol,
467                                        snd_ctl_elem_value_t * ucontrol)
468 {
469         int channel_id = CONTROL_REAR_CHANNEL;
470         int reg = PLAYBACK_VOLUME2;
471         return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
472 }
473
474 static int snd_ca0106_volume_put_feedback(snd_kcontrol_t * kcontrol,
475                                        snd_ctl_elem_value_t * ucontrol)
476 {
477         int channel_id = 1;
478         int reg = CAPTURE_CONTROL;
479         return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
480 }
481
482 static snd_kcontrol_new_t snd_ca0106_volume_control_analog_front =
483 {
484         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
485         .name =         "Analog Front Playback Volume",
486         .info =         snd_ca0106_volume_info,
487         .get =          snd_ca0106_volume_get_analog_front,
488         .put =          snd_ca0106_volume_put_analog_front
489 };
490 static snd_kcontrol_new_t snd_ca0106_volume_control_analog_center_lfe =
491 {
492         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
493         .name =         "Analog Center/LFE Playback Volume",
494         .info =         snd_ca0106_volume_info,
495         .get =          snd_ca0106_volume_get_analog_center_lfe,
496         .put =          snd_ca0106_volume_put_analog_center_lfe
497 };
498 static snd_kcontrol_new_t snd_ca0106_volume_control_analog_unknown =
499 {
500         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
501         .name =         "Analog Side Playback Volume",
502         .info =         snd_ca0106_volume_info,
503         .get =          snd_ca0106_volume_get_analog_unknown,
504         .put =          snd_ca0106_volume_put_analog_unknown
505 };
506 static snd_kcontrol_new_t snd_ca0106_volume_control_analog_rear =
507 {
508         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
509         .name =         "Analog Rear Playback Volume",
510         .info =         snd_ca0106_volume_info,
511         .get =          snd_ca0106_volume_get_analog_rear,
512         .put =          snd_ca0106_volume_put_analog_rear
513 };
514 static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_front =
515 {
516         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
517         .name =         "SPDIF Front Playback Volume",
518         .info =         snd_ca0106_volume_info,
519         .get =          snd_ca0106_volume_get_spdif_front,
520         .put =          snd_ca0106_volume_put_spdif_front
521 };
522 static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_center_lfe =
523 {
524         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
525         .name =         "SPDIF Center/LFE Playback Volume",
526         .info =         snd_ca0106_volume_info,
527         .get =          snd_ca0106_volume_get_spdif_center_lfe,
528         .put =          snd_ca0106_volume_put_spdif_center_lfe
529 };
530 static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_unknown =
531 {
532         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
533         .name =         "SPDIF Unknown Playback Volume",
534         .info =         snd_ca0106_volume_info,
535         .get =          snd_ca0106_volume_get_spdif_unknown,
536         .put =          snd_ca0106_volume_put_spdif_unknown
537 };
538 static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_rear =
539 {
540         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
541         .name =         "SPDIF Rear Playback Volume",
542         .info =         snd_ca0106_volume_info,
543         .get =          snd_ca0106_volume_get_spdif_rear,
544         .put =          snd_ca0106_volume_put_spdif_rear
545 };
546
547 static snd_kcontrol_new_t snd_ca0106_volume_control_feedback =
548 {
549         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
550         .name =         "CAPTURE feedback Playback Volume",
551         .info =         snd_ca0106_volume_info,
552         .get =          snd_ca0106_volume_get_feedback,
553         .put =          snd_ca0106_volume_put_feedback
554 };
555
556
557 static int remove_ctl(snd_card_t *card, const char *name)
558 {
559         snd_ctl_elem_id_t id;
560         memset(&id, 0, sizeof(id));
561         strcpy(id.name, name);
562         id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
563         return snd_ctl_remove_id(card, &id);
564 }
565
566 static snd_kcontrol_t *ctl_find(snd_card_t *card, const char *name)
567 {
568         snd_ctl_elem_id_t sid;
569         memset(&sid, 0, sizeof(sid));
570         /* FIXME: strcpy is bad. */
571         strcpy(sid.name, name);
572         sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
573         return snd_ctl_find_id(card, &sid);
574 }
575
576 static int rename_ctl(snd_card_t *card, const char *src, const char *dst)
577 {
578         snd_kcontrol_t *kctl = ctl_find(card, src);
579         if (kctl) {
580                 strcpy(kctl->id.name, dst);
581                 return 0;
582         }
583         return -ENOENT;
584 }
585
586 int __devinit snd_ca0106_mixer(ca0106_t *emu)
587 {
588         int err;
589         snd_kcontrol_t *kctl;
590         snd_card_t *card = emu->card;
591         char **c;
592         static char *ca0106_remove_ctls[] = {
593                 "Master Mono Playback Switch",
594                 "Master Mono Playback Volume",
595                 "3D Control - Switch",
596                 "3D Control Sigmatel - Depth",
597                 "PCM Playback Switch",
598                 "PCM Playback Volume",
599                 "CD Playback Switch",
600                 "CD Playback Volume",
601                 "Phone Playback Switch",
602                 "Phone Playback Volume",
603                 "Video Playback Switch",
604                 "Video Playback Volume",
605                 "PC Speaker Playback Switch",
606                 "PC Speaker Playback Volume",
607                 "Mono Output Select",
608                 "Capture Source",
609                 "Capture Switch",
610                 "Capture Volume",
611                 "External Amplifier",
612                 "Sigmatel 4-Speaker Stereo Playback Switch",
613                 "Sigmatel Surround Phase Inversion Playback ",
614                 NULL
615         };
616         static char *ca0106_rename_ctls[] = {
617                 "Master Playback Switch", "Capture Switch",
618                 "Master Playback Volume", "Capture Volume",
619                 "Line Playback Switch", "AC97 Line Capture Switch",
620                 "Line Playback Volume", "AC97 Line Capture Volume",
621                 "Aux Playback Switch", "AC97 Aux Capture Switch",
622                 "Aux Playback Volume", "AC97 Aux Capture Volume",
623                 "Mic Playback Switch", "AC97 Mic Capture Switch",
624                 "Mic Playback Volume", "AC97 Mic Capture Volume",
625                 "Mic Select", "AC97 Mic Select",
626                 "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
627                 NULL
628         };
629 #if 1
630         for (c=ca0106_remove_ctls; *c; c++)
631                 remove_ctl(card, *c);
632         for (c=ca0106_rename_ctls; *c; c += 2)
633                 rename_ctl(card, c[0], c[1]);
634 #endif
635
636         if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_front, emu)) == NULL)
637                 return -ENOMEM;
638         if ((err = snd_ctl_add(card, kctl)))
639                 return err;
640         if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_rear, emu)) == NULL)
641                 return -ENOMEM;
642         if ((err = snd_ctl_add(card, kctl)))
643                 return err;
644         if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_center_lfe, emu)) == NULL)
645                 return -ENOMEM;
646         if ((err = snd_ctl_add(card, kctl)))
647                 return err;
648         if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_unknown, emu)) == NULL)
649                 return -ENOMEM;
650         if ((err = snd_ctl_add(card, kctl)))
651                 return err;
652         if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_front, emu)) == NULL)
653                 return -ENOMEM;
654         if ((err = snd_ctl_add(card, kctl)))
655                 return err;
656         if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_rear, emu)) == NULL)
657                 return -ENOMEM;
658         if ((err = snd_ctl_add(card, kctl)))
659                 return err;
660         if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_center_lfe, emu)) == NULL)
661                 return -ENOMEM;
662         if ((err = snd_ctl_add(card, kctl)))
663                 return err;
664         if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_unknown, emu)) == NULL)
665                 return -ENOMEM;
666         if ((err = snd_ctl_add(card, kctl)))
667                 return err;
668         if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_feedback, emu)) == NULL)
669                 return -ENOMEM;
670         if ((err = snd_ctl_add(card, kctl)))
671                 return err;
672         if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_mask_control, emu)) == NULL)
673                 return -ENOMEM;
674         if ((err = snd_ctl_add(card, kctl)))
675                 return err;
676         if ((kctl = snd_ctl_new1(&snd_ca0106_shared_spdif, emu)) == NULL)
677                 return -ENOMEM;
678         if ((err = snd_ctl_add(card, kctl)))
679                 return err;
680         if ((kctl = snd_ctl_new1(&snd_ca0106_capture_source, emu)) == NULL)
681                 return -ENOMEM;
682         if ((err = snd_ctl_add(card, kctl)))
683                 return err;
684         if (emu->details->i2c_adc == 1) {
685                 if ((kctl = snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)) == NULL)
686                         return -ENOMEM;
687                 if ((err = snd_ctl_add(card, kctl)))
688                         return err;
689         }
690         if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_control, emu)) == NULL)
691                 return -ENOMEM;
692         if ((err = snd_ctl_add(card, kctl)))
693                 return err;
694         return 0;
695 }
696