xref: /openbmc/linux/sound/pci/ca0106/ca0106_mixer.c (revision 5fe619f983d6c7c0a578fcaabf80edd30e7ce46c)
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
4b18cd538STrent Piepho  *  Version: 0.0.18
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.
42b18cd538STrent Piepho  *  0.0.18
43b18cd538STrent Piepho  *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
441da177e4SLinus Torvalds  *
451da177e4SLinus Torvalds  *  This code was initally based on code from ALSA's emu10k1x.c which is:
461da177e4SLinus Torvalds  *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
471da177e4SLinus Torvalds  *
481da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
491da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
501da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
511da177e4SLinus Torvalds  *   (at your option) any later version.
521da177e4SLinus Torvalds  *
531da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
541da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
551da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
561da177e4SLinus Torvalds  *   GNU General Public License for more details.
571da177e4SLinus Torvalds  *
581da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
591da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
601da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
611da177e4SLinus Torvalds  *
621da177e4SLinus Torvalds  */
631da177e4SLinus Torvalds #include <sound/driver.h>
641da177e4SLinus Torvalds #include <linux/delay.h>
651da177e4SLinus Torvalds #include <linux/init.h>
661da177e4SLinus Torvalds #include <linux/interrupt.h>
671da177e4SLinus Torvalds #include <linux/slab.h>
681da177e4SLinus Torvalds #include <linux/moduleparam.h>
691da177e4SLinus Torvalds #include <sound/core.h>
701da177e4SLinus Torvalds #include <sound/initval.h>
711da177e4SLinus Torvalds #include <sound/pcm.h>
721da177e4SLinus Torvalds #include <sound/ac97_codec.h>
731da177e4SLinus Torvalds #include <sound/info.h>
7442750b04SJaroslav Kysela #include <sound/tlv.h>
756473d160SJean Delvare #include <asm/io.h>
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds #include "ca0106.h"
781da177e4SLinus Torvalds 
790cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
800cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
8142750b04SJaroslav Kysela 
82a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info	snd_ctl_boolean_mono_info
831da177e4SLinus Torvalds 
84e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
85e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
861da177e4SLinus Torvalds {
87e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
881da177e4SLinus Torvalds 
89*5fe619f9STakashi Iwai 	ucontrol->value.integer.value[0] = emu->spdif_enable;
901da177e4SLinus Torvalds 	return 0;
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds 
93e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
94e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
951da177e4SLinus Torvalds {
96e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
971da177e4SLinus Torvalds 	unsigned int val;
981da177e4SLinus Torvalds 	int change = 0;
991da177e4SLinus Torvalds 	u32 mask;
1001da177e4SLinus Torvalds 
101*5fe619f9STakashi Iwai 	val = !!ucontrol->value.integer.value[0];
1021da177e4SLinus Torvalds 	change = (emu->spdif_enable != val);
1031da177e4SLinus Torvalds 	if (change) {
1041da177e4SLinus Torvalds 		emu->spdif_enable = val;
105*5fe619f9STakashi Iwai 		if (val) {
1061da177e4SLinus Torvalds 			/* Digital */
1071da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
1081da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
1091da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
1101da177e4SLinus Torvalds 				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
1111da177e4SLinus Torvalds 			mask = inl(emu->port + GPIO) & ~0x101;
1121da177e4SLinus Torvalds 			outl(mask, emu->port + GPIO);
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 		} else {
1151da177e4SLinus Torvalds 			/* Analog */
1161da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
1171f82941eSJames Courtier-Dutton 			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
1181da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
1191da177e4SLinus Torvalds 				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
1201da177e4SLinus Torvalds 			mask = inl(emu->port + GPIO) | 0x101;
1211da177e4SLinus Torvalds 			outl(mask, emu->port + GPIO);
1221da177e4SLinus Torvalds 		}
1231da177e4SLinus Torvalds 	}
1241da177e4SLinus Torvalds         return change;
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds 
127e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
128e4a3d145STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
1291da177e4SLinus Torvalds {
13095a98265STakashi Iwai 	static char *texts[6] = {
13139596dc8SJames Courtier-Dutton 		"IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
13295a98265STakashi Iwai 	};
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1351da177e4SLinus Torvalds 	uinfo->count = 1;
1361da177e4SLinus Torvalds 	uinfo->value.enumerated.items = 6;
1371da177e4SLinus Torvalds 	if (uinfo->value.enumerated.item > 5)
1381da177e4SLinus Torvalds                 uinfo->value.enumerated.item = 5;
1391da177e4SLinus Torvalds 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1401da177e4SLinus Torvalds 	return 0;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
143e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
144e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1451da177e4SLinus Torvalds {
146e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds 	ucontrol->value.enumerated.item[0] = emu->capture_source;
1491da177e4SLinus Torvalds 	return 0;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds 
152e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
153e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1541da177e4SLinus Torvalds {
155e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1561da177e4SLinus Torvalds 	unsigned int val;
1571da177e4SLinus Torvalds 	int change = 0;
1581da177e4SLinus Torvalds 	u32 mask;
1591da177e4SLinus Torvalds 	u32 source;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	val = ucontrol->value.enumerated.item[0] ;
162*5fe619f9STakashi Iwai 	if (val >= 6)
163*5fe619f9STakashi Iwai 		return -EINVAL;
1641da177e4SLinus Torvalds 	change = (emu->capture_source != val);
1651da177e4SLinus Torvalds 	if (change) {
1661da177e4SLinus Torvalds 		emu->capture_source = val;
1671da177e4SLinus Torvalds 		source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
1681da177e4SLinus Torvalds 		mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
1691da177e4SLinus Torvalds 		snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
1701da177e4SLinus Torvalds 	}
1711da177e4SLinus Torvalds         return change;
1721da177e4SLinus Torvalds }
1731da177e4SLinus Torvalds 
1746129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
1756129daaaSJames Courtier-Dutton 					  struct snd_ctl_elem_info *uinfo)
1766129daaaSJames Courtier-Dutton {
1776129daaaSJames Courtier-Dutton 	static char *texts[6] = {
1786129daaaSJames Courtier-Dutton 		"Phone", "Mic", "Line in", "Aux"
1796129daaaSJames Courtier-Dutton 	};
1806129daaaSJames Courtier-Dutton 
1816129daaaSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1826129daaaSJames Courtier-Dutton 	uinfo->count = 1;
1836129daaaSJames Courtier-Dutton 	uinfo->value.enumerated.items = 4;
1846129daaaSJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 3)
1856129daaaSJames Courtier-Dutton                 uinfo->value.enumerated.item = 3;
1866129daaaSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1876129daaaSJames Courtier-Dutton 	return 0;
1886129daaaSJames Courtier-Dutton }
1896129daaaSJames Courtier-Dutton 
1906129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
1916129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
1926129daaaSJames Courtier-Dutton {
1936129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1946129daaaSJames Courtier-Dutton 
1956129daaaSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
1966129daaaSJames Courtier-Dutton 	return 0;
1976129daaaSJames Courtier-Dutton }
1986129daaaSJames Courtier-Dutton 
1996129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
2006129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
2016129daaaSJames Courtier-Dutton {
2026129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2036129daaaSJames Courtier-Dutton 	unsigned int source_id;
2046129daaaSJames Courtier-Dutton 	unsigned int ngain, ogain;
2056129daaaSJames Courtier-Dutton 	int change = 0;
2066129daaaSJames Courtier-Dutton 	u32 source;
2076129daaaSJames Courtier-Dutton 	/* If the capture source has changed,
2086129daaaSJames Courtier-Dutton 	 * update the capture volume from the cached value
2096129daaaSJames Courtier-Dutton 	 * for the particular source.
2106129daaaSJames Courtier-Dutton 	 */
2116129daaaSJames Courtier-Dutton 	source_id = ucontrol->value.enumerated.item[0] ;
212*5fe619f9STakashi Iwai 	if (source_id >= 4)
213*5fe619f9STakashi Iwai 		return -EINVAL;
2146129daaaSJames Courtier-Dutton 	change = (emu->i2c_capture_source != source_id);
2156129daaaSJames Courtier-Dutton 	if (change) {
2166129daaaSJames Courtier-Dutton 		snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
2176129daaaSJames Courtier-Dutton 		ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
2186129daaaSJames Courtier-Dutton 		ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
2196129daaaSJames Courtier-Dutton 		if (ngain != ogain)
2206129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
2216129daaaSJames Courtier-Dutton 		ngain = emu->i2c_capture_volume[source_id][1]; /* Left */
2226129daaaSJames Courtier-Dutton 		ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */
2236129daaaSJames Courtier-Dutton 		if (ngain != ogain)
2246129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
2256129daaaSJames Courtier-Dutton 		source = 1 << source_id;
2266129daaaSJames Courtier-Dutton 		snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
2276129daaaSJames Courtier-Dutton 		emu->i2c_capture_source = source_id;
2286129daaaSJames Courtier-Dutton 	}
2296129daaaSJames Courtier-Dutton         return change;
2306129daaaSJames Courtier-Dutton }
2316129daaaSJames Courtier-Dutton 
232be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
233be0b7b01SJames Courtier-Dutton 					       struct snd_ctl_elem_info *uinfo)
234be0b7b01SJames Courtier-Dutton {
235be0b7b01SJames Courtier-Dutton 	static char *texts[2] = { "Side out", "Line in" };
236be0b7b01SJames Courtier-Dutton 
237be0b7b01SJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
238be0b7b01SJames Courtier-Dutton 	uinfo->count = 1;
239be0b7b01SJames Courtier-Dutton 	uinfo->value.enumerated.items = 2;
240be0b7b01SJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 1)
241be0b7b01SJames Courtier-Dutton                 uinfo->value.enumerated.item = 1;
242be0b7b01SJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
243be0b7b01SJames Courtier-Dutton 	return 0;
244be0b7b01SJames Courtier-Dutton }
245be0b7b01SJames Courtier-Dutton 
246e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
247e4a3d145STakashi Iwai 					       struct snd_ctl_elem_info *uinfo)
248ed144f3cSJames Courtier-Dutton {
249ed144f3cSJames Courtier-Dutton 	static char *texts[2] = { "Line in", "Mic in" };
250ed144f3cSJames Courtier-Dutton 
251ed144f3cSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
252ed144f3cSJames Courtier-Dutton 	uinfo->count = 1;
253ed144f3cSJames Courtier-Dutton 	uinfo->value.enumerated.items = 2;
254ed144f3cSJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 1)
255ed144f3cSJames Courtier-Dutton                 uinfo->value.enumerated.item = 1;
256ed144f3cSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
257ed144f3cSJames Courtier-Dutton 	return 0;
258ed144f3cSJames Courtier-Dutton }
259ed144f3cSJames Courtier-Dutton 
260e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
261e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
262ed144f3cSJames Courtier-Dutton {
263e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
264ed144f3cSJames Courtier-Dutton 
265ed144f3cSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
266ed144f3cSJames Courtier-Dutton 	return 0;
267ed144f3cSJames Courtier-Dutton }
268ed144f3cSJames Courtier-Dutton 
269e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
270e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
271ed144f3cSJames Courtier-Dutton {
272e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
273ed144f3cSJames Courtier-Dutton 	unsigned int val;
274ed144f3cSJames Courtier-Dutton 	int change = 0;
275ed144f3cSJames Courtier-Dutton 	u32 tmp;
276ed144f3cSJames Courtier-Dutton 
277ed144f3cSJames Courtier-Dutton 	val = ucontrol->value.enumerated.item[0] ;
278*5fe619f9STakashi Iwai 	if (val > 1)
279*5fe619f9STakashi Iwai 		return -EINVAL;
280ed144f3cSJames Courtier-Dutton 	change = (emu->capture_mic_line_in != val);
281ed144f3cSJames Courtier-Dutton 	if (change) {
282ed144f3cSJames Courtier-Dutton 		emu->capture_mic_line_in = val;
283ed144f3cSJames Courtier-Dutton 		if (val) {
2846129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
285ed144f3cSJames Courtier-Dutton 			tmp = inl(emu->port+GPIO) & ~0x400;
286ed144f3cSJames Courtier-Dutton 			tmp = tmp | 0x400;
287ed144f3cSJames Courtier-Dutton 			outl(tmp, emu->port+GPIO);
2886129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
289ed144f3cSJames Courtier-Dutton 		} else {
2906129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
291ed144f3cSJames Courtier-Dutton 			tmp = inl(emu->port+GPIO) & ~0x400;
292ed144f3cSJames Courtier-Dutton 			outl(tmp, emu->port+GPIO);
2936129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
294ed144f3cSJames Courtier-Dutton 		}
295ed144f3cSJames Courtier-Dutton 	}
296ed144f3cSJames Courtier-Dutton         return change;
297ed144f3cSJames Courtier-Dutton }
298ed144f3cSJames Courtier-Dutton 
299e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
300ed144f3cSJames Courtier-Dutton {
301ed144f3cSJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
3026129daaaSJames Courtier-Dutton 	.name =		"Shared Mic/Line in Capture Switch",
303ed144f3cSJames Courtier-Dutton 	.info =		snd_ca0106_capture_mic_line_in_info,
304ed144f3cSJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
305ed144f3cSJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
306ed144f3cSJames Courtier-Dutton };
307ed144f3cSJames Courtier-Dutton 
308be0b7b01SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
309be0b7b01SJames Courtier-Dutton {
310be0b7b01SJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
311be0b7b01SJames Courtier-Dutton 	.name =		"Shared Line in/Side out Capture Switch",
312be0b7b01SJames Courtier-Dutton 	.info =		snd_ca0106_capture_line_in_side_out_info,
313be0b7b01SJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
314be0b7b01SJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
315be0b7b01SJames Courtier-Dutton };
316be0b7b01SJames Courtier-Dutton 
317be0b7b01SJames Courtier-Dutton 
318e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
319e4a3d145STakashi Iwai 				 struct snd_ctl_elem_info *uinfo)
3201da177e4SLinus Torvalds {
3211da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
3221da177e4SLinus Torvalds 	uinfo->count = 1;
3231da177e4SLinus Torvalds 	return 0;
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds 
326e4a3d145STakashi Iwai static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
327e4a3d145STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3281da177e4SLinus Torvalds {
329e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3301da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
3331da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
3341da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
3351da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
3361da177e4SLinus Torvalds         return 0;
3371da177e4SLinus Torvalds }
3381da177e4SLinus Torvalds 
339e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
340e4a3d145STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3411da177e4SLinus Torvalds {
3421da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = 0xff;
3431da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = 0xff;
3441da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = 0xff;
3451da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = 0xff;
3461da177e4SLinus Torvalds         return 0;
3471da177e4SLinus Torvalds }
3481da177e4SLinus Torvalds 
349e4a3d145STakashi Iwai static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
350e4a3d145STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3511da177e4SLinus Torvalds {
352e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3531da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3541da177e4SLinus Torvalds 	int change;
3551da177e4SLinus Torvalds 	unsigned int val;
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 	val = (ucontrol->value.iec958.status[0] << 0) |
3581da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[1] << 8) |
3591da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[2] << 16) |
3601da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[3] << 24);
3611da177e4SLinus Torvalds 	change = val != emu->spdif_bits[idx];
3621da177e4SLinus Torvalds 	if (change) {
3631da177e4SLinus Torvalds 		snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
3641da177e4SLinus Torvalds 		emu->spdif_bits[idx] = val;
3651da177e4SLinus Torvalds 	}
3661da177e4SLinus Torvalds         return change;
3671da177e4SLinus Torvalds }
3681da177e4SLinus Torvalds 
369e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
370e4a3d145STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
3711da177e4SLinus Torvalds {
3721da177e4SLinus Torvalds         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3731da177e4SLinus Torvalds         uinfo->count = 2;
3741da177e4SLinus Torvalds         uinfo->value.integer.min = 0;
3751da177e4SLinus Torvalds         uinfo->value.integer.max = 255;
3761da177e4SLinus Torvalds         return 0;
3771da177e4SLinus Torvalds }
3781da177e4SLinus Torvalds 
379e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
380e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
3811da177e4SLinus Torvalds {
382e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3831da177e4SLinus Torvalds         unsigned int value;
38495a98265STakashi Iwai 	int channel_id, reg;
38595a98265STakashi Iwai 
38695a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
38795a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds         value = snd_ca0106_ptr_read(emu, reg, channel_id);
3901da177e4SLinus Torvalds         ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
3911da177e4SLinus Torvalds         ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
3921da177e4SLinus Torvalds         return 0;
3931da177e4SLinus Torvalds }
3941da177e4SLinus Torvalds 
395e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
396e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
3971da177e4SLinus Torvalds {
398e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
39995a98265STakashi Iwai         unsigned int oval, nval;
40095a98265STakashi Iwai 	int channel_id, reg;
40195a98265STakashi Iwai 
40295a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
40395a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
40495a98265STakashi Iwai 
40595a98265STakashi Iwai 	oval = snd_ca0106_ptr_read(emu, reg, channel_id);
40695a98265STakashi Iwai 	nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
40795a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) << 16);
40895a98265STakashi Iwai         nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
40995a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) );
41095a98265STakashi Iwai 	if (oval == nval)
41195a98265STakashi Iwai 		return 0;
41295a98265STakashi Iwai 	snd_ca0106_ptr_write(emu, reg, channel_id, nval);
4131da177e4SLinus Torvalds 	return 1;
4141da177e4SLinus Torvalds }
41595a98265STakashi Iwai 
4166129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
4176129daaaSJames Courtier-Dutton 				  struct snd_ctl_elem_info *uinfo)
4186129daaaSJames Courtier-Dutton {
4196129daaaSJames Courtier-Dutton         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4206129daaaSJames Courtier-Dutton         uinfo->count = 2;
4216129daaaSJames Courtier-Dutton         uinfo->value.integer.min = 0;
4226129daaaSJames Courtier-Dutton         uinfo->value.integer.max = 255;
4236129daaaSJames Courtier-Dutton         return 0;
4246129daaaSJames Courtier-Dutton }
4256129daaaSJames Courtier-Dutton 
4266129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
4276129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4286129daaaSJames Courtier-Dutton {
4296129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4306129daaaSJames Courtier-Dutton 	int source_id;
4316129daaaSJames Courtier-Dutton 
4326129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
4336129daaaSJames Courtier-Dutton 
4346129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
4356129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
4366129daaaSJames Courtier-Dutton         return 0;
4376129daaaSJames Courtier-Dutton }
4386129daaaSJames Courtier-Dutton 
4396129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
4406129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4416129daaaSJames Courtier-Dutton {
4426129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4436129daaaSJames Courtier-Dutton         unsigned int ogain;
4446129daaaSJames Courtier-Dutton         unsigned int ngain;
4456129daaaSJames Courtier-Dutton 	int source_id;
4466129daaaSJames Courtier-Dutton 	int change = 0;
4476129daaaSJames Courtier-Dutton 
4486129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
4496129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
4506129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[0];
4516129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
452*5fe619f9STakashi Iwai 		return -EINVAL;
4536129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
4546129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
4556129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
4566129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
4576129daaaSJames Courtier-Dutton 		change = 1;
4586129daaaSJames Courtier-Dutton 	}
4596129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
4606129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[1];
4616129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
462*5fe619f9STakashi Iwai 		return -EINVAL;
4636129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
4646129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
4656129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
4666129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
4676129daaaSJames Courtier-Dutton 		change = 1;
4686129daaaSJames Courtier-Dutton 	}
4696129daaaSJames Courtier-Dutton 
4706129daaaSJames Courtier-Dutton 	return change;
4716129daaaSJames Courtier-Dutton }
4726129daaaSJames Courtier-Dutton 
473b18cd538STrent Piepho #define spi_mute_info	snd_ctl_boolean_mono_info
474b18cd538STrent Piepho 
475b18cd538STrent Piepho static int spi_mute_get(struct snd_kcontrol *kcontrol,
476b18cd538STrent Piepho 			struct snd_ctl_elem_value *ucontrol)
477b18cd538STrent Piepho {
478b18cd538STrent Piepho 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
479b18cd538STrent Piepho 	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
480b18cd538STrent Piepho 	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
481b18cd538STrent Piepho 
482b18cd538STrent Piepho 	ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit);
483b18cd538STrent Piepho 	return 0;
484b18cd538STrent Piepho }
485b18cd538STrent Piepho 
486b18cd538STrent Piepho static int spi_mute_put(struct snd_kcontrol *kcontrol,
487b18cd538STrent Piepho 			struct snd_ctl_elem_value *ucontrol)
488b18cd538STrent Piepho {
489b18cd538STrent Piepho 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
490b18cd538STrent Piepho 	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
491b18cd538STrent Piepho 	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
492b18cd538STrent Piepho 	int ret;
493b18cd538STrent Piepho 
494b18cd538STrent Piepho 	ret = emu->spi_dac_reg[reg] & bit;
495b18cd538STrent Piepho 	if (ucontrol->value.integer.value[0]) {
496b18cd538STrent Piepho 		if (!ret)	/* bit already cleared, do nothing */
497b18cd538STrent Piepho 			return 0;
498b18cd538STrent Piepho 		emu->spi_dac_reg[reg] &= ~bit;
499b18cd538STrent Piepho 	} else {
500b18cd538STrent Piepho 		if (ret)	/* bit already set, do nothing */
501b18cd538STrent Piepho 			return 0;
502b18cd538STrent Piepho 		emu->spi_dac_reg[reg] |= bit;
503b18cd538STrent Piepho 	}
504b18cd538STrent Piepho 
505b18cd538STrent Piepho 	ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]);
506*5fe619f9STakashi Iwai 	return ret ? -EINVAL : 1;
507b18cd538STrent Piepho }
508b18cd538STrent Piepho 
50995a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \
51095a98265STakashi Iwai {								\
51195a98265STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
512302e9c5aSJaroslav Kysela 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
513302e9c5aSJaroslav Kysela 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
51495a98265STakashi Iwai 	.info =	 snd_ca0106_volume_info,			\
51595a98265STakashi Iwai 	.get =   snd_ca0106_volume_get,				\
51695a98265STakashi Iwai 	.put =   snd_ca0106_volume_put,				\
5177cf0a953STakashi Iwai 	.tlv = { .p = snd_ca0106_db_scale1 },			\
51895a98265STakashi Iwai 	.private_value = ((chid) << 8) | (reg)			\
5191da177e4SLinus Torvalds }
5201da177e4SLinus Torvalds 
521e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
52295a98265STakashi Iwai 	CA_VOLUME("Analog Front Playback Volume",
52395a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
52495a98265STakashi Iwai         CA_VOLUME("Analog Rear Playback Volume",
52595a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
52695a98265STakashi Iwai 	CA_VOLUME("Analog Center/LFE Playback Volume",
52795a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
52895a98265STakashi Iwai         CA_VOLUME("Analog Side Playback Volume",
52995a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
53095a98265STakashi Iwai 
53139596dc8SJames Courtier-Dutton         CA_VOLUME("IEC958 Front Playback Volume",
53295a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
53339596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Rear Playback Volume",
53495a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
53539596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Center/LFE Playback Volume",
53695a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
53739596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Unknown Playback Volume",
53895a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
53995a98265STakashi Iwai 
54095a98265STakashi Iwai         CA_VOLUME("CAPTURE feedback Playback Volume",
54195a98265STakashi Iwai 		  1, CAPTURE_CONTROL),
54295a98265STakashi Iwai 
54395a98265STakashi Iwai 	{
54495a98265STakashi Iwai 		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
54595a98265STakashi Iwai 		.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
54695a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
54795a98265STakashi Iwai 		.count =	4,
54895a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
54995a98265STakashi Iwai 		.get =          snd_ca0106_spdif_get_mask
55095a98265STakashi Iwai 	},
5511da177e4SLinus Torvalds 	{
5521da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
55339596dc8SJames Courtier-Dutton 		.name =		"IEC958 Playback Switch",
55495a98265STakashi Iwai 		.info =		snd_ca0106_shared_spdif_info,
55595a98265STakashi Iwai 		.get =		snd_ca0106_shared_spdif_get,
55695a98265STakashi Iwai 		.put =		snd_ca0106_shared_spdif_put
55795a98265STakashi Iwai 	},
5581da177e4SLinus Torvalds 	{
5591da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
560e6327cf9SJames Courtier-Dutton 		.name =		"Digital Source Capture Enum",
56195a98265STakashi Iwai 		.info =		snd_ca0106_capture_source_info,
56295a98265STakashi Iwai 		.get =		snd_ca0106_capture_source_get,
56395a98265STakashi Iwai 		.put =		snd_ca0106_capture_source_put
56495a98265STakashi Iwai 	},
5651da177e4SLinus Torvalds 	{
5666129daaaSJames Courtier-Dutton 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
567e6327cf9SJames Courtier-Dutton 		.name =		"Analog Source Capture Enum",
5686129daaaSJames Courtier-Dutton 		.info =		snd_ca0106_i2c_capture_source_info,
5696129daaaSJames Courtier-Dutton 		.get =		snd_ca0106_i2c_capture_source_get,
5706129daaaSJames Courtier-Dutton 		.put =		snd_ca0106_i2c_capture_source_put
5716129daaaSJames Courtier-Dutton 	},
5726129daaaSJames Courtier-Dutton 	{
57395a98265STakashi Iwai 		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
57495a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
57595a98265STakashi Iwai 		.count =	4,
57695a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
57795a98265STakashi Iwai 		.get =          snd_ca0106_spdif_get,
57895a98265STakashi Iwai 		.put =          snd_ca0106_spdif_put
57995a98265STakashi Iwai 	},
5801da177e4SLinus Torvalds };
5811da177e4SLinus Torvalds 
5827c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \
5837c157069SJames Courtier-Dutton {								\
5847c157069SJames Courtier-Dutton 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
5857c157069SJames Courtier-Dutton 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
5867c157069SJames Courtier-Dutton 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
5877c157069SJames Courtier-Dutton 	.info =  snd_ca0106_i2c_volume_info,			\
5887c157069SJames Courtier-Dutton 	.get =   snd_ca0106_i2c_volume_get,			\
5897c157069SJames Courtier-Dutton 	.put =   snd_ca0106_i2c_volume_put,			\
5907c157069SJames Courtier-Dutton 	.tlv = { .p = snd_ca0106_db_scale2 },			\
5917c157069SJames Courtier-Dutton 	.private_value = chid					\
5927c157069SJames Courtier-Dutton }
5937c157069SJames Courtier-Dutton 
5947c157069SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
5957c157069SJames Courtier-Dutton         I2C_VOLUME("Phone Capture Volume", 0),
5967c157069SJames Courtier-Dutton         I2C_VOLUME("Mic Capture Volume", 1),
5977c157069SJames Courtier-Dutton         I2C_VOLUME("Line in Capture Volume", 2),
5987c157069SJames Courtier-Dutton         I2C_VOLUME("Aux Capture Volume", 3),
5997c157069SJames Courtier-Dutton };
6007c157069SJames Courtier-Dutton 
601b18cd538STrent Piepho #define SPI_SWITCH(xname,reg,bit) \
602b18cd538STrent Piepho {								\
603b18cd538STrent Piepho 	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
604b18cd538STrent Piepho 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
605b18cd538STrent Piepho 	.info	= spi_mute_info,				\
606b18cd538STrent Piepho 	.get	= spi_mute_get,					\
607b18cd538STrent Piepho 	.put	= spi_mute_put,					\
60818b5d32fSTrent Piepho 	.private_value = (reg<<SPI_REG_SHIFT) | (bit)		\
609b18cd538STrent Piepho }
610b18cd538STrent Piepho 
611b18cd538STrent Piepho static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[]
612b18cd538STrent Piepho __devinitdata = {
613b18cd538STrent Piepho 	SPI_SWITCH("Analog Front Playback Switch",
614b18cd538STrent Piepho 		   SPI_DMUTE4_REG, SPI_DMUTE4_BIT),
615b18cd538STrent Piepho 	SPI_SWITCH("Analog Rear Playback Switch",
616b18cd538STrent Piepho 		   SPI_DMUTE0_REG, SPI_DMUTE0_BIT),
617b18cd538STrent Piepho 	SPI_SWITCH("Analog Center/LFE Playback Switch",
618b18cd538STrent Piepho 		   SPI_DMUTE2_REG, SPI_DMUTE2_BIT),
619b18cd538STrent Piepho 	SPI_SWITCH("Analog Side Playback Switch",
620b18cd538STrent Piepho 		   SPI_DMUTE1_REG, SPI_DMUTE1_BIT),
621b18cd538STrent Piepho };
622b18cd538STrent Piepho 
623e4a3d145STakashi Iwai static int __devinit remove_ctl(struct snd_card *card, const char *name)
6241da177e4SLinus Torvalds {
625e4a3d145STakashi Iwai 	struct snd_ctl_elem_id id;
6261da177e4SLinus Torvalds 	memset(&id, 0, sizeof(id));
6271da177e4SLinus Torvalds 	strcpy(id.name, name);
6281da177e4SLinus Torvalds 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
6291da177e4SLinus Torvalds 	return snd_ctl_remove_id(card, &id);
6301da177e4SLinus Torvalds }
6311da177e4SLinus Torvalds 
632e4a3d145STakashi Iwai static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
6331da177e4SLinus Torvalds {
634e4a3d145STakashi Iwai 	struct snd_ctl_elem_id sid;
6351da177e4SLinus Torvalds 	memset(&sid, 0, sizeof(sid));
6361da177e4SLinus Torvalds 	/* FIXME: strcpy is bad. */
6371da177e4SLinus Torvalds 	strcpy(sid.name, name);
6381da177e4SLinus Torvalds 	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
6391da177e4SLinus Torvalds 	return snd_ctl_find_id(card, &sid);
6401da177e4SLinus Torvalds }
6411da177e4SLinus Torvalds 
642e4a3d145STakashi Iwai static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
6431da177e4SLinus Torvalds {
644e4a3d145STakashi Iwai 	struct snd_kcontrol *kctl = ctl_find(card, src);
6451da177e4SLinus Torvalds 	if (kctl) {
6461da177e4SLinus Torvalds 		strcpy(kctl->id.name, dst);
6471da177e4SLinus Torvalds 		return 0;
6481da177e4SLinus Torvalds 	}
6491da177e4SLinus Torvalds 	return -ENOENT;
6501da177e4SLinus Torvalds }
6511da177e4SLinus Torvalds 
652fca7f388STrent Piepho #define ADD_CTLS(emu, ctls)						\
653fca7f388STrent Piepho 	do {								\
654fca7f388STrent Piepho 		int i, err;						\
655fca7f388STrent Piepho 		for (i = 0; i < ARRAY_SIZE(ctls); i++) {		\
656fca7f388STrent Piepho 			err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
657fca7f388STrent Piepho 			if (err < 0)					\
658fca7f388STrent Piepho 				return err;				\
659fca7f388STrent Piepho 		}							\
660fca7f388STrent Piepho 	} while (0)
661fca7f388STrent Piepho 
662e4a3d145STakashi Iwai int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
6631da177e4SLinus Torvalds {
664fca7f388STrent Piepho 	int err;
665e4a3d145STakashi Iwai         struct snd_card *card = emu->card;
6661da177e4SLinus Torvalds 	char **c;
6671da177e4SLinus Torvalds 	static char *ca0106_remove_ctls[] = {
6681da177e4SLinus Torvalds 		"Master Mono Playback Switch",
6691da177e4SLinus Torvalds 		"Master Mono Playback Volume",
6701da177e4SLinus Torvalds 		"3D Control - Switch",
6711da177e4SLinus Torvalds 		"3D Control Sigmatel - Depth",
6721da177e4SLinus Torvalds 		"PCM Playback Switch",
6731da177e4SLinus Torvalds 		"PCM Playback Volume",
6741da177e4SLinus Torvalds 		"CD Playback Switch",
6751da177e4SLinus Torvalds 		"CD Playback Volume",
6761da177e4SLinus Torvalds 		"Phone Playback Switch",
6771da177e4SLinus Torvalds 		"Phone Playback Volume",
6781da177e4SLinus Torvalds 		"Video Playback Switch",
6791da177e4SLinus Torvalds 		"Video Playback Volume",
6801da177e4SLinus Torvalds 		"PC Speaker Playback Switch",
6811da177e4SLinus Torvalds 		"PC Speaker Playback Volume",
6821da177e4SLinus Torvalds 		"Mono Output Select",
6831da177e4SLinus Torvalds 		"Capture Source",
6841da177e4SLinus Torvalds 		"Capture Switch",
6851da177e4SLinus Torvalds 		"Capture Volume",
6861da177e4SLinus Torvalds 		"External Amplifier",
6871da177e4SLinus Torvalds 		"Sigmatel 4-Speaker Stereo Playback Switch",
6881da177e4SLinus Torvalds 		"Sigmatel Surround Phase Inversion Playback ",
6891da177e4SLinus Torvalds 		NULL
6901da177e4SLinus Torvalds 	};
6911da177e4SLinus Torvalds 	static char *ca0106_rename_ctls[] = {
6921da177e4SLinus Torvalds 		"Master Playback Switch", "Capture Switch",
6931da177e4SLinus Torvalds 		"Master Playback Volume", "Capture Volume",
6941da177e4SLinus Torvalds 		"Line Playback Switch", "AC97 Line Capture Switch",
6951da177e4SLinus Torvalds 		"Line Playback Volume", "AC97 Line Capture Volume",
6961da177e4SLinus Torvalds 		"Aux Playback Switch", "AC97 Aux Capture Switch",
6971da177e4SLinus Torvalds 		"Aux Playback Volume", "AC97 Aux Capture Volume",
6981da177e4SLinus Torvalds 		"Mic Playback Switch", "AC97 Mic Capture Switch",
6991da177e4SLinus Torvalds 		"Mic Playback Volume", "AC97 Mic Capture Volume",
7001da177e4SLinus Torvalds 		"Mic Select", "AC97 Mic Select",
7011da177e4SLinus Torvalds 		"Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
7021da177e4SLinus Torvalds 		NULL
7031da177e4SLinus Torvalds 	};
7041da177e4SLinus Torvalds #if 1
7051da177e4SLinus Torvalds 	for (c = ca0106_remove_ctls; *c; c++)
7061da177e4SLinus Torvalds 		remove_ctl(card, *c);
7071da177e4SLinus Torvalds 	for (c = ca0106_rename_ctls; *c; c += 2)
7081da177e4SLinus Torvalds 		rename_ctl(card, c[0], c[1]);
7091da177e4SLinus Torvalds #endif
7101da177e4SLinus Torvalds 
711fca7f388STrent Piepho 	ADD_CTLS(emu, snd_ca0106_volume_ctls);
71295a98265STakashi Iwai 	if (emu->details->i2c_adc == 1) {
713fca7f388STrent Piepho 		ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
714be0b7b01SJames Courtier-Dutton 		if (emu->details->gpio_type == 1)
71595a98265STakashi Iwai 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
716be0b7b01SJames Courtier-Dutton 		else  /* gpio_type == 2 */
717be0b7b01SJames Courtier-Dutton 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
71895a98265STakashi Iwai 		if (err < 0)
7191da177e4SLinus Torvalds 			return err;
72095a98265STakashi Iwai 	}
721fca7f388STrent Piepho 	if (emu->details->spi_dac == 1)
722fca7f388STrent Piepho 		ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls);
7231da177e4SLinus Torvalds         return 0;
7241da177e4SLinus Torvalds }
7251da177e4SLinus Torvalds 
726