xref: /openbmc/linux/sound/pci/ca0106/ca0106_mixer.c (revision a5ce88909d3007caa7b65996a8f6784350beb2a6)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
31da177e4SLinus Torvalds  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
4ed144f3cSJames Courtier-Dutton  *  Version: 0.0.17
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *  FEATURES currently supported:
71da177e4SLinus Torvalds  *    See ca0106_main.c for features.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  Changelog:
101da177e4SLinus Torvalds  *    Support interrupts per period.
111da177e4SLinus Torvalds  *    Removed noise from Center/LFE channel when in Analog mode.
121da177e4SLinus Torvalds  *    Rename and remove mixer controls.
131da177e4SLinus Torvalds  *  0.0.6
141da177e4SLinus Torvalds  *    Use separate card based DMA buffer for periods table list.
151da177e4SLinus Torvalds  *  0.0.7
161da177e4SLinus Torvalds  *    Change remove and rename ctrls into lists.
171da177e4SLinus Torvalds  *  0.0.8
181da177e4SLinus Torvalds  *    Try to fix capture sources.
191da177e4SLinus Torvalds  *  0.0.9
201da177e4SLinus Torvalds  *    Fix AC3 output.
211da177e4SLinus Torvalds  *    Enable S32_LE format support.
221da177e4SLinus Torvalds  *  0.0.10
231da177e4SLinus Torvalds  *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
241da177e4SLinus Torvalds  *  0.0.11
251da177e4SLinus Torvalds  *    Add Model name recognition.
261da177e4SLinus Torvalds  *  0.0.12
271da177e4SLinus Torvalds  *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
281da177e4SLinus Torvalds  *    Remove redundent "voice" handling.
291da177e4SLinus Torvalds  *  0.0.13
301da177e4SLinus Torvalds  *    Single trigger call for multi channels.
311da177e4SLinus Torvalds  *  0.0.14
321da177e4SLinus Torvalds  *    Set limits based on what the sound card hardware can do.
331da177e4SLinus Torvalds  *    playback periods_min=2, periods_max=8
341da177e4SLinus Torvalds  *    capture hw constraints require period_size = n * 64 bytes.
351da177e4SLinus Torvalds  *    playback hw constraints require period_size = n * 64 bytes.
361da177e4SLinus Torvalds  *  0.0.15
371da177e4SLinus Torvalds  *    Separated ca0106.c into separate functional .c files.
381da177e4SLinus Torvalds  *  0.0.16
391da177e4SLinus Torvalds  *    Modified Copyright message.
40ed144f3cSJames Courtier-Dutton  *  0.0.17
41ed144f3cSJames Courtier-Dutton  *    Implement Mic and Line in Capture.
421da177e4SLinus Torvalds  *
431da177e4SLinus Torvalds  *  This code was initally based on code from ALSA's emu10k1x.c which is:
441da177e4SLinus Torvalds  *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
451da177e4SLinus Torvalds  *
461da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
471da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
481da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
491da177e4SLinus Torvalds  *   (at your option) any later version.
501da177e4SLinus Torvalds  *
511da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
521da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
531da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
541da177e4SLinus Torvalds  *   GNU General Public License for more details.
551da177e4SLinus Torvalds  *
561da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
571da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
581da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
591da177e4SLinus Torvalds  *
601da177e4SLinus Torvalds  */
611da177e4SLinus Torvalds #include <sound/driver.h>
621da177e4SLinus Torvalds #include <linux/delay.h>
631da177e4SLinus Torvalds #include <linux/init.h>
641da177e4SLinus Torvalds #include <linux/interrupt.h>
651da177e4SLinus Torvalds #include <linux/slab.h>
661da177e4SLinus Torvalds #include <linux/moduleparam.h>
671da177e4SLinus Torvalds #include <sound/core.h>
681da177e4SLinus Torvalds #include <sound/initval.h>
691da177e4SLinus Torvalds #include <sound/pcm.h>
701da177e4SLinus Torvalds #include <sound/ac97_codec.h>
711da177e4SLinus Torvalds #include <sound/info.h>
7242750b04SJaroslav Kysela #include <sound/tlv.h>
736473d160SJean Delvare #include <asm/io.h>
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds #include "ca0106.h"
761da177e4SLinus Torvalds 
770cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
780cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
7942750b04SJaroslav Kysela 
80*a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info	snd_ctl_boolean_mono_info
811da177e4SLinus Torvalds 
82e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
83e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
841da177e4SLinus Torvalds {
85e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	ucontrol->value.enumerated.item[0] = emu->spdif_enable;
881da177e4SLinus Torvalds 	return 0;
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
91e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
92e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
931da177e4SLinus Torvalds {
94e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
951da177e4SLinus Torvalds 	unsigned int val;
961da177e4SLinus Torvalds 	int change = 0;
971da177e4SLinus Torvalds 	u32 mask;
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 	val = ucontrol->value.enumerated.item[0] ;
1001da177e4SLinus Torvalds 	change = (emu->spdif_enable != val);
1011da177e4SLinus Torvalds 	if (change) {
1021da177e4SLinus Torvalds 		emu->spdif_enable = val;
1031da177e4SLinus Torvalds 		if (val == 1) {
1041da177e4SLinus Torvalds 			/* Digital */
1051da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
1061da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
1071da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
1081da177e4SLinus Torvalds 				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
1091da177e4SLinus Torvalds 			mask = inl(emu->port + GPIO) & ~0x101;
1101da177e4SLinus Torvalds 			outl(mask, emu->port + GPIO);
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 		} else {
1131da177e4SLinus Torvalds 			/* Analog */
1141da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
1151f82941eSJames Courtier-Dutton 			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
1161da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
1171da177e4SLinus Torvalds 				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
1181da177e4SLinus Torvalds 			mask = inl(emu->port + GPIO) | 0x101;
1191da177e4SLinus Torvalds 			outl(mask, emu->port + GPIO);
1201da177e4SLinus Torvalds 		}
1211da177e4SLinus Torvalds 	}
1221da177e4SLinus Torvalds         return change;
1231da177e4SLinus Torvalds }
1241da177e4SLinus Torvalds 
125e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
126e4a3d145STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
1271da177e4SLinus Torvalds {
12895a98265STakashi Iwai 	static char *texts[6] = {
12939596dc8SJames Courtier-Dutton 		"IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
13095a98265STakashi Iwai 	};
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1331da177e4SLinus Torvalds 	uinfo->count = 1;
1341da177e4SLinus Torvalds 	uinfo->value.enumerated.items = 6;
1351da177e4SLinus Torvalds 	if (uinfo->value.enumerated.item > 5)
1361da177e4SLinus Torvalds                 uinfo->value.enumerated.item = 5;
1371da177e4SLinus Torvalds 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1381da177e4SLinus Torvalds 	return 0;
1391da177e4SLinus Torvalds }
1401da177e4SLinus Torvalds 
141e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
142e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1431da177e4SLinus Torvalds {
144e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	ucontrol->value.enumerated.item[0] = emu->capture_source;
1471da177e4SLinus Torvalds 	return 0;
1481da177e4SLinus Torvalds }
1491da177e4SLinus Torvalds 
150e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
151e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1521da177e4SLinus Torvalds {
153e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1541da177e4SLinus Torvalds 	unsigned int val;
1551da177e4SLinus Torvalds 	int change = 0;
1561da177e4SLinus Torvalds 	u32 mask;
1571da177e4SLinus Torvalds 	u32 source;
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	val = ucontrol->value.enumerated.item[0] ;
1601da177e4SLinus Torvalds 	change = (emu->capture_source != val);
1611da177e4SLinus Torvalds 	if (change) {
1621da177e4SLinus Torvalds 		emu->capture_source = val;
1631da177e4SLinus Torvalds 		source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
1641da177e4SLinus Torvalds 		mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
1651da177e4SLinus Torvalds 		snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds         return change;
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds 
1706129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
1716129daaaSJames Courtier-Dutton 					  struct snd_ctl_elem_info *uinfo)
1726129daaaSJames Courtier-Dutton {
1736129daaaSJames Courtier-Dutton 	static char *texts[6] = {
1746129daaaSJames Courtier-Dutton 		"Phone", "Mic", "Line in", "Aux"
1756129daaaSJames Courtier-Dutton 	};
1766129daaaSJames Courtier-Dutton 
1776129daaaSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1786129daaaSJames Courtier-Dutton 	uinfo->count = 1;
1796129daaaSJames Courtier-Dutton 	uinfo->value.enumerated.items = 4;
1806129daaaSJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 3)
1816129daaaSJames Courtier-Dutton                 uinfo->value.enumerated.item = 3;
1826129daaaSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1836129daaaSJames Courtier-Dutton 	return 0;
1846129daaaSJames Courtier-Dutton }
1856129daaaSJames Courtier-Dutton 
1866129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
1876129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
1886129daaaSJames Courtier-Dutton {
1896129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1906129daaaSJames Courtier-Dutton 
1916129daaaSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
1926129daaaSJames Courtier-Dutton 	return 0;
1936129daaaSJames Courtier-Dutton }
1946129daaaSJames Courtier-Dutton 
1956129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
1966129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
1976129daaaSJames Courtier-Dutton {
1986129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1996129daaaSJames Courtier-Dutton 	unsigned int source_id;
2006129daaaSJames Courtier-Dutton 	unsigned int ngain, ogain;
2016129daaaSJames Courtier-Dutton 	int change = 0;
2026129daaaSJames Courtier-Dutton 	u32 source;
2036129daaaSJames Courtier-Dutton 	/* If the capture source has changed,
2046129daaaSJames Courtier-Dutton 	 * update the capture volume from the cached value
2056129daaaSJames Courtier-Dutton 	 * for the particular source.
2066129daaaSJames Courtier-Dutton 	 */
2076129daaaSJames Courtier-Dutton 	source_id = ucontrol->value.enumerated.item[0] ;
2086129daaaSJames Courtier-Dutton 	change = (emu->i2c_capture_source != source_id);
2096129daaaSJames Courtier-Dutton 	if (change) {
2106129daaaSJames Courtier-Dutton 		snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
2116129daaaSJames Courtier-Dutton 		ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
2126129daaaSJames Courtier-Dutton 		ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
2136129daaaSJames Courtier-Dutton 		if (ngain != ogain)
2146129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
2156129daaaSJames Courtier-Dutton 		ngain = emu->i2c_capture_volume[source_id][1]; /* Left */
2166129daaaSJames Courtier-Dutton 		ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */
2176129daaaSJames Courtier-Dutton 		if (ngain != ogain)
2186129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
2196129daaaSJames Courtier-Dutton 		source = 1 << source_id;
2206129daaaSJames Courtier-Dutton 		snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
2216129daaaSJames Courtier-Dutton 		emu->i2c_capture_source = source_id;
2226129daaaSJames Courtier-Dutton 	}
2236129daaaSJames Courtier-Dutton         return change;
2246129daaaSJames Courtier-Dutton }
2256129daaaSJames Courtier-Dutton 
226be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
227be0b7b01SJames Courtier-Dutton 					       struct snd_ctl_elem_info *uinfo)
228be0b7b01SJames Courtier-Dutton {
229be0b7b01SJames Courtier-Dutton 	static char *texts[2] = { "Side out", "Line in" };
230be0b7b01SJames Courtier-Dutton 
231be0b7b01SJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
232be0b7b01SJames Courtier-Dutton 	uinfo->count = 1;
233be0b7b01SJames Courtier-Dutton 	uinfo->value.enumerated.items = 2;
234be0b7b01SJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 1)
235be0b7b01SJames Courtier-Dutton                 uinfo->value.enumerated.item = 1;
236be0b7b01SJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
237be0b7b01SJames Courtier-Dutton 	return 0;
238be0b7b01SJames Courtier-Dutton }
239be0b7b01SJames Courtier-Dutton 
240e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
241e4a3d145STakashi Iwai 					       struct snd_ctl_elem_info *uinfo)
242ed144f3cSJames Courtier-Dutton {
243ed144f3cSJames Courtier-Dutton 	static char *texts[2] = { "Line in", "Mic in" };
244ed144f3cSJames Courtier-Dutton 
245ed144f3cSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
246ed144f3cSJames Courtier-Dutton 	uinfo->count = 1;
247ed144f3cSJames Courtier-Dutton 	uinfo->value.enumerated.items = 2;
248ed144f3cSJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 1)
249ed144f3cSJames Courtier-Dutton                 uinfo->value.enumerated.item = 1;
250ed144f3cSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
251ed144f3cSJames Courtier-Dutton 	return 0;
252ed144f3cSJames Courtier-Dutton }
253ed144f3cSJames Courtier-Dutton 
254e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
255e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
256ed144f3cSJames Courtier-Dutton {
257e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
258ed144f3cSJames Courtier-Dutton 
259ed144f3cSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
260ed144f3cSJames Courtier-Dutton 	return 0;
261ed144f3cSJames Courtier-Dutton }
262ed144f3cSJames Courtier-Dutton 
263e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
264e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
265ed144f3cSJames Courtier-Dutton {
266e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
267ed144f3cSJames Courtier-Dutton 	unsigned int val;
268ed144f3cSJames Courtier-Dutton 	int change = 0;
269ed144f3cSJames Courtier-Dutton 	u32 tmp;
270ed144f3cSJames Courtier-Dutton 
271ed144f3cSJames Courtier-Dutton 	val = ucontrol->value.enumerated.item[0] ;
272ed144f3cSJames Courtier-Dutton 	change = (emu->capture_mic_line_in != val);
273ed144f3cSJames Courtier-Dutton 	if (change) {
274ed144f3cSJames Courtier-Dutton 		emu->capture_mic_line_in = val;
275ed144f3cSJames Courtier-Dutton 		if (val) {
2766129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
277ed144f3cSJames Courtier-Dutton 			tmp = inl(emu->port+GPIO) & ~0x400;
278ed144f3cSJames Courtier-Dutton 			tmp = tmp | 0x400;
279ed144f3cSJames Courtier-Dutton 			outl(tmp, emu->port+GPIO);
2806129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
281ed144f3cSJames Courtier-Dutton 		} else {
2826129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
283ed144f3cSJames Courtier-Dutton 			tmp = inl(emu->port+GPIO) & ~0x400;
284ed144f3cSJames Courtier-Dutton 			outl(tmp, emu->port+GPIO);
2856129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
286ed144f3cSJames Courtier-Dutton 		}
287ed144f3cSJames Courtier-Dutton 	}
288ed144f3cSJames Courtier-Dutton         return change;
289ed144f3cSJames Courtier-Dutton }
290ed144f3cSJames Courtier-Dutton 
291e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
292ed144f3cSJames Courtier-Dutton {
293ed144f3cSJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
2946129daaaSJames Courtier-Dutton 	.name =		"Shared Mic/Line in Capture Switch",
295ed144f3cSJames Courtier-Dutton 	.info =		snd_ca0106_capture_mic_line_in_info,
296ed144f3cSJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
297ed144f3cSJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
298ed144f3cSJames Courtier-Dutton };
299ed144f3cSJames Courtier-Dutton 
300be0b7b01SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
301be0b7b01SJames Courtier-Dutton {
302be0b7b01SJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
303be0b7b01SJames Courtier-Dutton 	.name =		"Shared Line in/Side out Capture Switch",
304be0b7b01SJames Courtier-Dutton 	.info =		snd_ca0106_capture_line_in_side_out_info,
305be0b7b01SJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
306be0b7b01SJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
307be0b7b01SJames Courtier-Dutton };
308be0b7b01SJames Courtier-Dutton 
309be0b7b01SJames Courtier-Dutton 
310e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
311e4a3d145STakashi Iwai 				 struct snd_ctl_elem_info *uinfo)
3121da177e4SLinus Torvalds {
3131da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
3141da177e4SLinus Torvalds 	uinfo->count = 1;
3151da177e4SLinus Torvalds 	return 0;
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds 
318e4a3d145STakashi Iwai static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
319e4a3d145STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3201da177e4SLinus Torvalds {
321e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3221da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
3251da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
3261da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
3271da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
3281da177e4SLinus Torvalds         return 0;
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds 
331e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
332e4a3d145STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = 0xff;
3351da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = 0xff;
3361da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = 0xff;
3371da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = 0xff;
3381da177e4SLinus Torvalds         return 0;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds 
341e4a3d145STakashi Iwai static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
342e4a3d145STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3431da177e4SLinus Torvalds {
344e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3451da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3461da177e4SLinus Torvalds 	int change;
3471da177e4SLinus Torvalds 	unsigned int val;
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds 	val = (ucontrol->value.iec958.status[0] << 0) |
3501da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[1] << 8) |
3511da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[2] << 16) |
3521da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[3] << 24);
3531da177e4SLinus Torvalds 	change = val != emu->spdif_bits[idx];
3541da177e4SLinus Torvalds 	if (change) {
3551da177e4SLinus Torvalds 		snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
3561da177e4SLinus Torvalds 		emu->spdif_bits[idx] = val;
3571da177e4SLinus Torvalds 	}
3581da177e4SLinus Torvalds         return change;
3591da177e4SLinus Torvalds }
3601da177e4SLinus Torvalds 
361e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
362e4a3d145STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
3631da177e4SLinus Torvalds {
3641da177e4SLinus Torvalds         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3651da177e4SLinus Torvalds         uinfo->count = 2;
3661da177e4SLinus Torvalds         uinfo->value.integer.min = 0;
3671da177e4SLinus Torvalds         uinfo->value.integer.max = 255;
3681da177e4SLinus Torvalds         return 0;
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds 
371e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
372e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
3731da177e4SLinus Torvalds {
374e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3751da177e4SLinus Torvalds         unsigned int value;
37695a98265STakashi Iwai 	int channel_id, reg;
37795a98265STakashi Iwai 
37895a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
37995a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds         value = snd_ca0106_ptr_read(emu, reg, channel_id);
3821da177e4SLinus Torvalds         ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
3831da177e4SLinus Torvalds         ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
3841da177e4SLinus Torvalds         return 0;
3851da177e4SLinus Torvalds }
3861da177e4SLinus Torvalds 
387e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
388e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
3891da177e4SLinus Torvalds {
390e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
39195a98265STakashi Iwai         unsigned int oval, nval;
39295a98265STakashi Iwai 	int channel_id, reg;
39395a98265STakashi Iwai 
39495a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
39595a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
39695a98265STakashi Iwai 
39795a98265STakashi Iwai 	oval = snd_ca0106_ptr_read(emu, reg, channel_id);
39895a98265STakashi Iwai 	nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
39995a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) << 16);
40095a98265STakashi Iwai         nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
40195a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) );
40295a98265STakashi Iwai 	if (oval == nval)
40395a98265STakashi Iwai 		return 0;
40495a98265STakashi Iwai 	snd_ca0106_ptr_write(emu, reg, channel_id, nval);
4051da177e4SLinus Torvalds 	return 1;
4061da177e4SLinus Torvalds }
40795a98265STakashi Iwai 
4086129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
4096129daaaSJames Courtier-Dutton 				  struct snd_ctl_elem_info *uinfo)
4106129daaaSJames Courtier-Dutton {
4116129daaaSJames Courtier-Dutton         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4126129daaaSJames Courtier-Dutton         uinfo->count = 2;
4136129daaaSJames Courtier-Dutton         uinfo->value.integer.min = 0;
4146129daaaSJames Courtier-Dutton         uinfo->value.integer.max = 255;
4156129daaaSJames Courtier-Dutton         return 0;
4166129daaaSJames Courtier-Dutton }
4176129daaaSJames Courtier-Dutton 
4186129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
4196129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4206129daaaSJames Courtier-Dutton {
4216129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4226129daaaSJames Courtier-Dutton 	int source_id;
4236129daaaSJames Courtier-Dutton 
4246129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
4256129daaaSJames Courtier-Dutton 
4266129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
4276129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
4286129daaaSJames Courtier-Dutton         return 0;
4296129daaaSJames Courtier-Dutton }
4306129daaaSJames Courtier-Dutton 
4316129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
4326129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4336129daaaSJames Courtier-Dutton {
4346129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4356129daaaSJames Courtier-Dutton         unsigned int ogain;
4366129daaaSJames Courtier-Dutton         unsigned int ngain;
4376129daaaSJames Courtier-Dutton 	int source_id;
4386129daaaSJames Courtier-Dutton 	int change = 0;
4396129daaaSJames Courtier-Dutton 
4406129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
4416129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
4426129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[0];
4436129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
4446129daaaSJames Courtier-Dutton 		return 0;
4456129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
4466129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
4476129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
4486129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
4496129daaaSJames Courtier-Dutton 		change = 1;
4506129daaaSJames Courtier-Dutton 	}
4516129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
4526129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[1];
4536129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
4546129daaaSJames Courtier-Dutton 		return 0;
4556129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
4566129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
4576129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
4586129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
4596129daaaSJames Courtier-Dutton 		change = 1;
4606129daaaSJames Courtier-Dutton 	}
4616129daaaSJames Courtier-Dutton 
4626129daaaSJames Courtier-Dutton 	return change;
4636129daaaSJames Courtier-Dutton }
4646129daaaSJames Courtier-Dutton 
46595a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \
46695a98265STakashi Iwai {								\
46795a98265STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
468302e9c5aSJaroslav Kysela 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
469302e9c5aSJaroslav Kysela 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
47095a98265STakashi Iwai 	.info =	 snd_ca0106_volume_info,			\
47195a98265STakashi Iwai 	.get =   snd_ca0106_volume_get,				\
47295a98265STakashi Iwai 	.put =   snd_ca0106_volume_put,				\
4737cf0a953STakashi Iwai 	.tlv = { .p = snd_ca0106_db_scale1 },			\
47495a98265STakashi Iwai 	.private_value = ((chid) << 8) | (reg)			\
4751da177e4SLinus Torvalds }
4761da177e4SLinus Torvalds 
477e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
47895a98265STakashi Iwai 	CA_VOLUME("Analog Front Playback Volume",
47995a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
48095a98265STakashi Iwai         CA_VOLUME("Analog Rear Playback Volume",
48195a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
48295a98265STakashi Iwai 	CA_VOLUME("Analog Center/LFE Playback Volume",
48395a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
48495a98265STakashi Iwai         CA_VOLUME("Analog Side Playback Volume",
48595a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
48695a98265STakashi Iwai 
48739596dc8SJames Courtier-Dutton         CA_VOLUME("IEC958 Front Playback Volume",
48895a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
48939596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Rear Playback Volume",
49095a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
49139596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Center/LFE Playback Volume",
49295a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
49339596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Unknown Playback Volume",
49495a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
49595a98265STakashi Iwai 
49695a98265STakashi Iwai         CA_VOLUME("CAPTURE feedback Playback Volume",
49795a98265STakashi Iwai 		  1, CAPTURE_CONTROL),
49895a98265STakashi Iwai 
49995a98265STakashi Iwai 	{
50095a98265STakashi Iwai 		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
50195a98265STakashi Iwai 		.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
50295a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
50395a98265STakashi Iwai 		.count =	4,
50495a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
50595a98265STakashi Iwai 		.get =          snd_ca0106_spdif_get_mask
50695a98265STakashi Iwai 	},
5071da177e4SLinus Torvalds 	{
5081da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
50939596dc8SJames Courtier-Dutton 		.name =		"IEC958 Playback Switch",
51095a98265STakashi Iwai 		.info =		snd_ca0106_shared_spdif_info,
51195a98265STakashi Iwai 		.get =		snd_ca0106_shared_spdif_get,
51295a98265STakashi Iwai 		.put =		snd_ca0106_shared_spdif_put
51395a98265STakashi Iwai 	},
5141da177e4SLinus Torvalds 	{
5151da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
516e6327cf9SJames Courtier-Dutton 		.name =		"Digital Source Capture Enum",
51795a98265STakashi Iwai 		.info =		snd_ca0106_capture_source_info,
51895a98265STakashi Iwai 		.get =		snd_ca0106_capture_source_get,
51995a98265STakashi Iwai 		.put =		snd_ca0106_capture_source_put
52095a98265STakashi Iwai 	},
5211da177e4SLinus Torvalds 	{
5226129daaaSJames Courtier-Dutton 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
523e6327cf9SJames Courtier-Dutton 		.name =		"Analog Source Capture Enum",
5246129daaaSJames Courtier-Dutton 		.info =		snd_ca0106_i2c_capture_source_info,
5256129daaaSJames Courtier-Dutton 		.get =		snd_ca0106_i2c_capture_source_get,
5266129daaaSJames Courtier-Dutton 		.put =		snd_ca0106_i2c_capture_source_put
5276129daaaSJames Courtier-Dutton 	},
5286129daaaSJames Courtier-Dutton 	{
52995a98265STakashi Iwai 		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
53095a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
53195a98265STakashi Iwai 		.count =	4,
53295a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
53395a98265STakashi Iwai 		.get =          snd_ca0106_spdif_get,
53495a98265STakashi Iwai 		.put =          snd_ca0106_spdif_put
53595a98265STakashi Iwai 	},
5361da177e4SLinus Torvalds };
5371da177e4SLinus Torvalds 
5387c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \
5397c157069SJames Courtier-Dutton {								\
5407c157069SJames Courtier-Dutton 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
5417c157069SJames Courtier-Dutton 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
5427c157069SJames Courtier-Dutton 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
5437c157069SJames Courtier-Dutton 	.info =  snd_ca0106_i2c_volume_info,			\
5447c157069SJames Courtier-Dutton 	.get =   snd_ca0106_i2c_volume_get,			\
5457c157069SJames Courtier-Dutton 	.put =   snd_ca0106_i2c_volume_put,			\
5467c157069SJames Courtier-Dutton 	.tlv = { .p = snd_ca0106_db_scale2 },			\
5477c157069SJames Courtier-Dutton 	.private_value = chid					\
5487c157069SJames Courtier-Dutton }
5497c157069SJames Courtier-Dutton 
5507c157069SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
5517c157069SJames Courtier-Dutton         I2C_VOLUME("Phone Capture Volume", 0),
5527c157069SJames Courtier-Dutton         I2C_VOLUME("Mic Capture Volume", 1),
5537c157069SJames Courtier-Dutton         I2C_VOLUME("Line in Capture Volume", 2),
5547c157069SJames Courtier-Dutton         I2C_VOLUME("Aux Capture Volume", 3),
5557c157069SJames Courtier-Dutton };
5567c157069SJames Courtier-Dutton 
557e4a3d145STakashi Iwai static int __devinit remove_ctl(struct snd_card *card, const char *name)
5581da177e4SLinus Torvalds {
559e4a3d145STakashi Iwai 	struct snd_ctl_elem_id id;
5601da177e4SLinus Torvalds 	memset(&id, 0, sizeof(id));
5611da177e4SLinus Torvalds 	strcpy(id.name, name);
5621da177e4SLinus Torvalds 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
5631da177e4SLinus Torvalds 	return snd_ctl_remove_id(card, &id);
5641da177e4SLinus Torvalds }
5651da177e4SLinus Torvalds 
566e4a3d145STakashi Iwai static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
5671da177e4SLinus Torvalds {
568e4a3d145STakashi Iwai 	struct snd_ctl_elem_id sid;
5691da177e4SLinus Torvalds 	memset(&sid, 0, sizeof(sid));
5701da177e4SLinus Torvalds 	/* FIXME: strcpy is bad. */
5711da177e4SLinus Torvalds 	strcpy(sid.name, name);
5721da177e4SLinus Torvalds 	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
5731da177e4SLinus Torvalds 	return snd_ctl_find_id(card, &sid);
5741da177e4SLinus Torvalds }
5751da177e4SLinus Torvalds 
576e4a3d145STakashi Iwai static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
5771da177e4SLinus Torvalds {
578e4a3d145STakashi Iwai 	struct snd_kcontrol *kctl = ctl_find(card, src);
5791da177e4SLinus Torvalds 	if (kctl) {
5801da177e4SLinus Torvalds 		strcpy(kctl->id.name, dst);
5811da177e4SLinus Torvalds 		return 0;
5821da177e4SLinus Torvalds 	}
5831da177e4SLinus Torvalds 	return -ENOENT;
5841da177e4SLinus Torvalds }
5851da177e4SLinus Torvalds 
586e4a3d145STakashi Iwai int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
5871da177e4SLinus Torvalds {
58895a98265STakashi Iwai 	int i, err;
589e4a3d145STakashi Iwai         struct snd_card *card = emu->card;
5901da177e4SLinus Torvalds 	char **c;
5911da177e4SLinus Torvalds 	static char *ca0106_remove_ctls[] = {
5921da177e4SLinus Torvalds 		"Master Mono Playback Switch",
5931da177e4SLinus Torvalds 		"Master Mono Playback Volume",
5941da177e4SLinus Torvalds 		"3D Control - Switch",
5951da177e4SLinus Torvalds 		"3D Control Sigmatel - Depth",
5961da177e4SLinus Torvalds 		"PCM Playback Switch",
5971da177e4SLinus Torvalds 		"PCM Playback Volume",
5981da177e4SLinus Torvalds 		"CD Playback Switch",
5991da177e4SLinus Torvalds 		"CD Playback Volume",
6001da177e4SLinus Torvalds 		"Phone Playback Switch",
6011da177e4SLinus Torvalds 		"Phone Playback Volume",
6021da177e4SLinus Torvalds 		"Video Playback Switch",
6031da177e4SLinus Torvalds 		"Video Playback Volume",
6041da177e4SLinus Torvalds 		"PC Speaker Playback Switch",
6051da177e4SLinus Torvalds 		"PC Speaker Playback Volume",
6061da177e4SLinus Torvalds 		"Mono Output Select",
6071da177e4SLinus Torvalds 		"Capture Source",
6081da177e4SLinus Torvalds 		"Capture Switch",
6091da177e4SLinus Torvalds 		"Capture Volume",
6101da177e4SLinus Torvalds 		"External Amplifier",
6111da177e4SLinus Torvalds 		"Sigmatel 4-Speaker Stereo Playback Switch",
6121da177e4SLinus Torvalds 		"Sigmatel Surround Phase Inversion Playback ",
6131da177e4SLinus Torvalds 		NULL
6141da177e4SLinus Torvalds 	};
6151da177e4SLinus Torvalds 	static char *ca0106_rename_ctls[] = {
6161da177e4SLinus Torvalds 		"Master Playback Switch", "Capture Switch",
6171da177e4SLinus Torvalds 		"Master Playback Volume", "Capture Volume",
6181da177e4SLinus Torvalds 		"Line Playback Switch", "AC97 Line Capture Switch",
6191da177e4SLinus Torvalds 		"Line Playback Volume", "AC97 Line Capture Volume",
6201da177e4SLinus Torvalds 		"Aux Playback Switch", "AC97 Aux Capture Switch",
6211da177e4SLinus Torvalds 		"Aux Playback Volume", "AC97 Aux Capture Volume",
6221da177e4SLinus Torvalds 		"Mic Playback Switch", "AC97 Mic Capture Switch",
6231da177e4SLinus Torvalds 		"Mic Playback Volume", "AC97 Mic Capture Volume",
6241da177e4SLinus Torvalds 		"Mic Select", "AC97 Mic Select",
6251da177e4SLinus Torvalds 		"Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
6261da177e4SLinus Torvalds 		NULL
6271da177e4SLinus Torvalds 	};
6281da177e4SLinus Torvalds #if 1
6291da177e4SLinus Torvalds 	for (c = ca0106_remove_ctls; *c; c++)
6301da177e4SLinus Torvalds 		remove_ctl(card, *c);
6311da177e4SLinus Torvalds 	for (c = ca0106_rename_ctls; *c; c += 2)
6321da177e4SLinus Torvalds 		rename_ctl(card, c[0], c[1]);
6331da177e4SLinus Torvalds #endif
6341da177e4SLinus Torvalds 
63595a98265STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_ctls); i++) {
63695a98265STakashi Iwai 		err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_ctls[i], emu));
63795a98265STakashi Iwai 		if (err < 0)
638ed144f3cSJames Courtier-Dutton 			return err;
639ed144f3cSJames Courtier-Dutton 	}
64095a98265STakashi Iwai 	if (emu->details->i2c_adc == 1) {
6417c157069SJames Courtier-Dutton 		for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_i2c_adc_ctls); i++) {
6427c157069SJames Courtier-Dutton 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_i2c_adc_ctls[i], emu));
6437c157069SJames Courtier-Dutton 			if (err < 0)
6447c157069SJames Courtier-Dutton 				return err;
6457c157069SJames Courtier-Dutton 		}
646be0b7b01SJames Courtier-Dutton 		if (emu->details->gpio_type == 1)
64795a98265STakashi Iwai 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
648be0b7b01SJames Courtier-Dutton 		else  /* gpio_type == 2 */
649be0b7b01SJames Courtier-Dutton 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
65095a98265STakashi Iwai 		if (err < 0)
6511da177e4SLinus Torvalds 			return err;
65295a98265STakashi Iwai 	}
6531da177e4SLinus Torvalds         return 0;
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds 
656