xref: /openbmc/linux/sound/pci/ca0106/ca0106_mixer.c (revision 8df0f70751dc0e51d0550caee3416339183c5767)
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 <linux/delay.h>
641da177e4SLinus Torvalds #include <linux/init.h>
651da177e4SLinus Torvalds #include <linux/interrupt.h>
661da177e4SLinus Torvalds #include <linux/slab.h>
671da177e4SLinus Torvalds #include <linux/moduleparam.h>
681da177e4SLinus Torvalds #include <sound/core.h>
691da177e4SLinus Torvalds #include <sound/initval.h>
701da177e4SLinus Torvalds #include <sound/pcm.h>
711da177e4SLinus Torvalds #include <sound/ac97_codec.h>
721da177e4SLinus Torvalds #include <sound/info.h>
7342750b04SJaroslav Kysela #include <sound/tlv.h>
746473d160SJean Delvare #include <asm/io.h>
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds #include "ca0106.h"
771da177e4SLinus Torvalds 
785da95273STakashi Iwai static void ca0106_spdif_enable(struct snd_ca0106 *emu)
795da95273STakashi Iwai {
805da95273STakashi Iwai 	unsigned int val;
815da95273STakashi Iwai 
825da95273STakashi Iwai 	if (emu->spdif_enable) {
835da95273STakashi Iwai 		/* Digital */
845da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
855da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
865da95273STakashi Iwai 		val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
875da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
885da95273STakashi Iwai 		val = inl(emu->port + GPIO) & ~0x101;
895da95273STakashi Iwai 		outl(val, emu->port + GPIO);
905da95273STakashi Iwai 
915da95273STakashi Iwai 	} else {
925da95273STakashi Iwai 		/* Analog */
935da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
945da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
955da95273STakashi Iwai 		val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
965da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
975da95273STakashi Iwai 		val = inl(emu->port + GPIO) | 0x101;
985da95273STakashi Iwai 		outl(val, emu->port + GPIO);
995da95273STakashi Iwai 	}
1005da95273STakashi Iwai }
1015da95273STakashi Iwai 
1025da95273STakashi Iwai static void ca0106_set_capture_source(struct snd_ca0106 *emu)
1035da95273STakashi Iwai {
1045da95273STakashi Iwai 	unsigned int val = emu->capture_source;
1055da95273STakashi Iwai 	unsigned int source, mask;
1065da95273STakashi Iwai 	source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
1075da95273STakashi Iwai 	mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
1085da95273STakashi Iwai 	snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
1095da95273STakashi Iwai }
1105da95273STakashi Iwai 
1115da95273STakashi Iwai static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu,
1125da95273STakashi Iwai 					  unsigned int val, int force)
1135da95273STakashi Iwai {
1145da95273STakashi Iwai 	unsigned int ngain, ogain;
1155da95273STakashi Iwai 	u32 source;
1165da95273STakashi Iwai 
1175da95273STakashi Iwai 	snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
1185da95273STakashi Iwai 	ngain = emu->i2c_capture_volume[val][0]; /* Left */
1195da95273STakashi Iwai 	ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
1205da95273STakashi Iwai 	if (force || ngain != ogain)
1215da95273STakashi Iwai 		snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff);
1225da95273STakashi Iwai 	ngain = emu->i2c_capture_volume[val][1]; /* Right */
1235da95273STakashi Iwai 	ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
1245da95273STakashi Iwai 	if (force || ngain != ogain)
1255da95273STakashi Iwai 		snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff);
1265da95273STakashi Iwai 	source = 1 << val;
1275da95273STakashi Iwai 	snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
1285da95273STakashi Iwai 	emu->i2c_capture_source = val;
1295da95273STakashi Iwai }
1305da95273STakashi Iwai 
1315da95273STakashi Iwai static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
1325da95273STakashi Iwai {
1335da95273STakashi Iwai 	u32 tmp;
1345da95273STakashi Iwai 
1355da95273STakashi Iwai 	if (emu->capture_mic_line_in) {
1365da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
1375da95273STakashi Iwai 		tmp = inl(emu->port+GPIO) & ~0x400;
1385da95273STakashi Iwai 		tmp = tmp | 0x400;
1395da95273STakashi Iwai 		outl(tmp, emu->port+GPIO);
1405da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
1415da95273STakashi Iwai 	} else {
1425da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
1435da95273STakashi Iwai 		tmp = inl(emu->port+GPIO) & ~0x400;
1445da95273STakashi Iwai 		outl(tmp, emu->port+GPIO);
1455da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
1465da95273STakashi Iwai 	}
1475da95273STakashi Iwai }
1485da95273STakashi Iwai 
1495da95273STakashi Iwai static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx)
1505da95273STakashi Iwai {
1515da95273STakashi Iwai 	snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_bits[idx]);
1525da95273STakashi Iwai }
1535da95273STakashi Iwai 
1545da95273STakashi Iwai /*
1555da95273STakashi Iwai  */
1560cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
1570cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
15842750b04SJaroslav Kysela 
159a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info	snd_ctl_boolean_mono_info
1601da177e4SLinus Torvalds 
161e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
162e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1631da177e4SLinus Torvalds {
164e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1651da177e4SLinus Torvalds 
1665fe619f9STakashi Iwai 	ucontrol->value.integer.value[0] = emu->spdif_enable;
1671da177e4SLinus Torvalds 	return 0;
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds 
170e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
171e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1721da177e4SLinus Torvalds {
173e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1741da177e4SLinus Torvalds 	unsigned int val;
1751da177e4SLinus Torvalds 	int change = 0;
1761da177e4SLinus Torvalds 
1775fe619f9STakashi Iwai 	val = !!ucontrol->value.integer.value[0];
1781da177e4SLinus Torvalds 	change = (emu->spdif_enable != val);
1791da177e4SLinus Torvalds 	if (change) {
1801da177e4SLinus Torvalds 		emu->spdif_enable = val;
1815da95273STakashi Iwai 		ca0106_spdif_enable(emu);
1821da177e4SLinus Torvalds 	}
1831da177e4SLinus Torvalds         return change;
1841da177e4SLinus Torvalds }
1851da177e4SLinus Torvalds 
186e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
187e4a3d145STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
1881da177e4SLinus Torvalds {
18995a98265STakashi Iwai 	static char *texts[6] = {
19039596dc8SJames Courtier-Dutton 		"IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
19195a98265STakashi Iwai 	};
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1941da177e4SLinus Torvalds 	uinfo->count = 1;
1951da177e4SLinus Torvalds 	uinfo->value.enumerated.items = 6;
1961da177e4SLinus Torvalds 	if (uinfo->value.enumerated.item > 5)
1971da177e4SLinus Torvalds                 uinfo->value.enumerated.item = 5;
1981da177e4SLinus Torvalds 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1991da177e4SLinus Torvalds 	return 0;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds 
202e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
203e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
2041da177e4SLinus Torvalds {
205e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	ucontrol->value.enumerated.item[0] = emu->capture_source;
2081da177e4SLinus Torvalds 	return 0;
2091da177e4SLinus Torvalds }
2101da177e4SLinus Torvalds 
211e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
212e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
2131da177e4SLinus Torvalds {
214e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2151da177e4SLinus Torvalds 	unsigned int val;
2161da177e4SLinus Torvalds 	int change = 0;
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds 	val = ucontrol->value.enumerated.item[0] ;
2195fe619f9STakashi Iwai 	if (val >= 6)
2205fe619f9STakashi Iwai 		return -EINVAL;
2211da177e4SLinus Torvalds 	change = (emu->capture_source != val);
2221da177e4SLinus Torvalds 	if (change) {
2231da177e4SLinus Torvalds 		emu->capture_source = val;
2245da95273STakashi Iwai 		ca0106_set_capture_source(emu);
2251da177e4SLinus Torvalds 	}
2261da177e4SLinus Torvalds         return change;
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds 
2296129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
2306129daaaSJames Courtier-Dutton 					  struct snd_ctl_elem_info *uinfo)
2316129daaaSJames Courtier-Dutton {
2326129daaaSJames Courtier-Dutton 	static char *texts[6] = {
2336129daaaSJames Courtier-Dutton 		"Phone", "Mic", "Line in", "Aux"
2346129daaaSJames Courtier-Dutton 	};
2356129daaaSJames Courtier-Dutton 
2366129daaaSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2376129daaaSJames Courtier-Dutton 	uinfo->count = 1;
2386129daaaSJames Courtier-Dutton 	uinfo->value.enumerated.items = 4;
2396129daaaSJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 3)
2406129daaaSJames Courtier-Dutton                 uinfo->value.enumerated.item = 3;
2416129daaaSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2426129daaaSJames Courtier-Dutton 	return 0;
2436129daaaSJames Courtier-Dutton }
2446129daaaSJames Courtier-Dutton 
2456129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
2466129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
2476129daaaSJames Courtier-Dutton {
2486129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2496129daaaSJames Courtier-Dutton 
2506129daaaSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
2516129daaaSJames Courtier-Dutton 	return 0;
2526129daaaSJames Courtier-Dutton }
2536129daaaSJames Courtier-Dutton 
2546129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
2556129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
2566129daaaSJames Courtier-Dutton {
2576129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2586129daaaSJames Courtier-Dutton 	unsigned int source_id;
2596129daaaSJames Courtier-Dutton 	int change = 0;
2606129daaaSJames Courtier-Dutton 	/* If the capture source has changed,
2616129daaaSJames Courtier-Dutton 	 * update the capture volume from the cached value
2626129daaaSJames Courtier-Dutton 	 * for the particular source.
2636129daaaSJames Courtier-Dutton 	 */
2646129daaaSJames Courtier-Dutton 	source_id = ucontrol->value.enumerated.item[0] ;
2655fe619f9STakashi Iwai 	if (source_id >= 4)
2665fe619f9STakashi Iwai 		return -EINVAL;
2676129daaaSJames Courtier-Dutton 	change = (emu->i2c_capture_source != source_id);
2686129daaaSJames Courtier-Dutton 	if (change) {
2695da95273STakashi Iwai 		ca0106_set_i2c_capture_source(emu, source_id, 0);
2706129daaaSJames Courtier-Dutton 	}
2716129daaaSJames Courtier-Dutton         return change;
2726129daaaSJames Courtier-Dutton }
2736129daaaSJames Courtier-Dutton 
274be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
275be0b7b01SJames Courtier-Dutton 					       struct snd_ctl_elem_info *uinfo)
276be0b7b01SJames Courtier-Dutton {
277be0b7b01SJames Courtier-Dutton 	static char *texts[2] = { "Side out", "Line in" };
278be0b7b01SJames Courtier-Dutton 
279be0b7b01SJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
280be0b7b01SJames Courtier-Dutton 	uinfo->count = 1;
281be0b7b01SJames Courtier-Dutton 	uinfo->value.enumerated.items = 2;
282be0b7b01SJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 1)
283be0b7b01SJames Courtier-Dutton                 uinfo->value.enumerated.item = 1;
284be0b7b01SJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
285be0b7b01SJames Courtier-Dutton 	return 0;
286be0b7b01SJames Courtier-Dutton }
287be0b7b01SJames Courtier-Dutton 
288e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
289e4a3d145STakashi Iwai 					       struct snd_ctl_elem_info *uinfo)
290ed144f3cSJames Courtier-Dutton {
291ed144f3cSJames Courtier-Dutton 	static char *texts[2] = { "Line in", "Mic in" };
292ed144f3cSJames Courtier-Dutton 
293ed144f3cSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
294ed144f3cSJames Courtier-Dutton 	uinfo->count = 1;
295ed144f3cSJames Courtier-Dutton 	uinfo->value.enumerated.items = 2;
296ed144f3cSJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 1)
297ed144f3cSJames Courtier-Dutton                 uinfo->value.enumerated.item = 1;
298ed144f3cSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
299ed144f3cSJames Courtier-Dutton 	return 0;
300ed144f3cSJames Courtier-Dutton }
301ed144f3cSJames Courtier-Dutton 
302e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
303e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
304ed144f3cSJames Courtier-Dutton {
305e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
306ed144f3cSJames Courtier-Dutton 
307ed144f3cSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
308ed144f3cSJames Courtier-Dutton 	return 0;
309ed144f3cSJames Courtier-Dutton }
310ed144f3cSJames Courtier-Dutton 
311e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
312e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
313ed144f3cSJames Courtier-Dutton {
314e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
315ed144f3cSJames Courtier-Dutton 	unsigned int val;
316ed144f3cSJames Courtier-Dutton 	int change = 0;
317ed144f3cSJames Courtier-Dutton 
318ed144f3cSJames Courtier-Dutton 	val = ucontrol->value.enumerated.item[0] ;
3195fe619f9STakashi Iwai 	if (val > 1)
3205fe619f9STakashi Iwai 		return -EINVAL;
321ed144f3cSJames Courtier-Dutton 	change = (emu->capture_mic_line_in != val);
322ed144f3cSJames Courtier-Dutton 	if (change) {
323ed144f3cSJames Courtier-Dutton 		emu->capture_mic_line_in = val;
3245da95273STakashi Iwai 		ca0106_set_capture_mic_line_in(emu);
325ed144f3cSJames Courtier-Dutton 	}
326ed144f3cSJames Courtier-Dutton         return change;
327ed144f3cSJames Courtier-Dutton }
328ed144f3cSJames Courtier-Dutton 
329e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
330ed144f3cSJames Courtier-Dutton {
331ed144f3cSJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
3326129daaaSJames Courtier-Dutton 	.name =		"Shared Mic/Line in Capture Switch",
333ed144f3cSJames Courtier-Dutton 	.info =		snd_ca0106_capture_mic_line_in_info,
334ed144f3cSJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
335ed144f3cSJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
336ed144f3cSJames Courtier-Dutton };
337ed144f3cSJames Courtier-Dutton 
338be0b7b01SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
339be0b7b01SJames Courtier-Dutton {
340be0b7b01SJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
341be0b7b01SJames Courtier-Dutton 	.name =		"Shared Line in/Side out Capture Switch",
342be0b7b01SJames Courtier-Dutton 	.info =		snd_ca0106_capture_line_in_side_out_info,
343be0b7b01SJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
344be0b7b01SJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
345be0b7b01SJames Courtier-Dutton };
346be0b7b01SJames Courtier-Dutton 
347be0b7b01SJames Courtier-Dutton 
348e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
349e4a3d145STakashi Iwai 				 struct snd_ctl_elem_info *uinfo)
3501da177e4SLinus Torvalds {
3511da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
3521da177e4SLinus Torvalds 	uinfo->count = 1;
3531da177e4SLinus Torvalds 	return 0;
3541da177e4SLinus Torvalds }
3551da177e4SLinus Torvalds 
356e4a3d145STakashi Iwai static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
357e4a3d145STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3581da177e4SLinus Torvalds {
359e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3601da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
3631da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
3641da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
3651da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
3661da177e4SLinus Torvalds         return 0;
3671da177e4SLinus Torvalds }
3681da177e4SLinus Torvalds 
369e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
370e4a3d145STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3711da177e4SLinus Torvalds {
3721da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = 0xff;
3731da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = 0xff;
3741da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = 0xff;
3751da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = 0xff;
3761da177e4SLinus Torvalds         return 0;
3771da177e4SLinus Torvalds }
3781da177e4SLinus Torvalds 
379e4a3d145STakashi Iwai static int snd_ca0106_spdif_put(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 idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3841da177e4SLinus Torvalds 	int change;
3851da177e4SLinus Torvalds 	unsigned int val;
3861da177e4SLinus Torvalds 
3871da177e4SLinus Torvalds 	val = (ucontrol->value.iec958.status[0] << 0) |
3881da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[1] << 8) |
3891da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[2] << 16) |
3901da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[3] << 24);
3911da177e4SLinus Torvalds 	change = val != emu->spdif_bits[idx];
3921da177e4SLinus Torvalds 	if (change) {
3931da177e4SLinus Torvalds 		emu->spdif_bits[idx] = val;
3945da95273STakashi Iwai 		ca0106_set_spdif_bits(emu, idx);
3951da177e4SLinus Torvalds 	}
3961da177e4SLinus Torvalds         return change;
3971da177e4SLinus Torvalds }
3981da177e4SLinus Torvalds 
399e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
400e4a3d145STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
4011da177e4SLinus Torvalds {
4021da177e4SLinus Torvalds         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4031da177e4SLinus Torvalds         uinfo->count = 2;
4041da177e4SLinus Torvalds         uinfo->value.integer.min = 0;
4051da177e4SLinus Torvalds         uinfo->value.integer.max = 255;
4061da177e4SLinus Torvalds         return 0;
4071da177e4SLinus Torvalds }
4081da177e4SLinus Torvalds 
409e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
410e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
4111da177e4SLinus Torvalds {
412e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4131da177e4SLinus Torvalds         unsigned int value;
41495a98265STakashi Iwai 	int channel_id, reg;
41595a98265STakashi Iwai 
41695a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
41795a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds         value = snd_ca0106_ptr_read(emu, reg, channel_id);
4201da177e4SLinus Torvalds         ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
4211da177e4SLinus Torvalds         ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
4221da177e4SLinus Torvalds         return 0;
4231da177e4SLinus Torvalds }
4241da177e4SLinus Torvalds 
425e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
426e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
4271da177e4SLinus Torvalds {
428e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
42995a98265STakashi Iwai         unsigned int oval, nval;
43095a98265STakashi Iwai 	int channel_id, reg;
43195a98265STakashi Iwai 
43295a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
43395a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
43495a98265STakashi Iwai 
43595a98265STakashi Iwai 	oval = snd_ca0106_ptr_read(emu, reg, channel_id);
43695a98265STakashi Iwai 	nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
43795a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) << 16);
43895a98265STakashi Iwai         nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
43995a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) );
44095a98265STakashi Iwai 	if (oval == nval)
44195a98265STakashi Iwai 		return 0;
44295a98265STakashi Iwai 	snd_ca0106_ptr_write(emu, reg, channel_id, nval);
4431da177e4SLinus Torvalds 	return 1;
4441da177e4SLinus Torvalds }
44595a98265STakashi Iwai 
4466129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
4476129daaaSJames Courtier-Dutton 				  struct snd_ctl_elem_info *uinfo)
4486129daaaSJames Courtier-Dutton {
4496129daaaSJames Courtier-Dutton         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4506129daaaSJames Courtier-Dutton         uinfo->count = 2;
4516129daaaSJames Courtier-Dutton         uinfo->value.integer.min = 0;
4526129daaaSJames Courtier-Dutton         uinfo->value.integer.max = 255;
4536129daaaSJames Courtier-Dutton         return 0;
4546129daaaSJames Courtier-Dutton }
4556129daaaSJames Courtier-Dutton 
4566129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
4576129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4586129daaaSJames Courtier-Dutton {
4596129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4606129daaaSJames Courtier-Dutton 	int source_id;
4616129daaaSJames Courtier-Dutton 
4626129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
4636129daaaSJames Courtier-Dutton 
4646129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
4656129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
4666129daaaSJames Courtier-Dutton         return 0;
4676129daaaSJames Courtier-Dutton }
4686129daaaSJames Courtier-Dutton 
4696129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
4706129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4716129daaaSJames Courtier-Dutton {
4726129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4736129daaaSJames Courtier-Dutton         unsigned int ogain;
4746129daaaSJames Courtier-Dutton         unsigned int ngain;
4756129daaaSJames Courtier-Dutton 	int source_id;
4766129daaaSJames Courtier-Dutton 	int change = 0;
4776129daaaSJames Courtier-Dutton 
4786129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
4796129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
4806129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[0];
4816129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
4825fe619f9STakashi Iwai 		return -EINVAL;
4836129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
4846129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
4856129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
4866129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
4876129daaaSJames Courtier-Dutton 		change = 1;
4886129daaaSJames Courtier-Dutton 	}
4896129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
4906129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[1];
4916129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
4925fe619f9STakashi Iwai 		return -EINVAL;
4936129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
4946129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
4956129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
4966129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
4976129daaaSJames Courtier-Dutton 		change = 1;
4986129daaaSJames Courtier-Dutton 	}
4996129daaaSJames Courtier-Dutton 
5006129daaaSJames Courtier-Dutton 	return change;
5016129daaaSJames Courtier-Dutton }
5026129daaaSJames Courtier-Dutton 
503b18cd538STrent Piepho #define spi_mute_info	snd_ctl_boolean_mono_info
504b18cd538STrent Piepho 
505b18cd538STrent Piepho static int spi_mute_get(struct snd_kcontrol *kcontrol,
506b18cd538STrent Piepho 			struct snd_ctl_elem_value *ucontrol)
507b18cd538STrent Piepho {
508b18cd538STrent Piepho 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
509b18cd538STrent Piepho 	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
510b18cd538STrent Piepho 	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
511b18cd538STrent Piepho 
512b18cd538STrent Piepho 	ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit);
513b18cd538STrent Piepho 	return 0;
514b18cd538STrent Piepho }
515b18cd538STrent Piepho 
516b18cd538STrent Piepho static int spi_mute_put(struct snd_kcontrol *kcontrol,
517b18cd538STrent Piepho 			struct snd_ctl_elem_value *ucontrol)
518b18cd538STrent Piepho {
519b18cd538STrent Piepho 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
520b18cd538STrent Piepho 	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
521b18cd538STrent Piepho 	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
522b18cd538STrent Piepho 	int ret;
523b18cd538STrent Piepho 
524b18cd538STrent Piepho 	ret = emu->spi_dac_reg[reg] & bit;
525b18cd538STrent Piepho 	if (ucontrol->value.integer.value[0]) {
526b18cd538STrent Piepho 		if (!ret)	/* bit already cleared, do nothing */
527b18cd538STrent Piepho 			return 0;
528b18cd538STrent Piepho 		emu->spi_dac_reg[reg] &= ~bit;
529b18cd538STrent Piepho 	} else {
530b18cd538STrent Piepho 		if (ret)	/* bit already set, do nothing */
531b18cd538STrent Piepho 			return 0;
532b18cd538STrent Piepho 		emu->spi_dac_reg[reg] |= bit;
533b18cd538STrent Piepho 	}
534b18cd538STrent Piepho 
535b18cd538STrent Piepho 	ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]);
5365fe619f9STakashi Iwai 	return ret ? -EINVAL : 1;
537b18cd538STrent Piepho }
538b18cd538STrent Piepho 
53995a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \
54095a98265STakashi Iwai {								\
54195a98265STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
542302e9c5aSJaroslav Kysela 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
543302e9c5aSJaroslav Kysela 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
54495a98265STakashi Iwai 	.info =	 snd_ca0106_volume_info,			\
54595a98265STakashi Iwai 	.get =   snd_ca0106_volume_get,				\
54695a98265STakashi Iwai 	.put =   snd_ca0106_volume_put,				\
5477cf0a953STakashi Iwai 	.tlv = { .p = snd_ca0106_db_scale1 },			\
54895a98265STakashi Iwai 	.private_value = ((chid) << 8) | (reg)			\
5491da177e4SLinus Torvalds }
5501da177e4SLinus Torvalds 
551e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
55295a98265STakashi Iwai 	CA_VOLUME("Analog Front Playback Volume",
55395a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
55495a98265STakashi Iwai         CA_VOLUME("Analog Rear Playback Volume",
55595a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
55695a98265STakashi Iwai 	CA_VOLUME("Analog Center/LFE Playback Volume",
55795a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
55895a98265STakashi Iwai         CA_VOLUME("Analog Side Playback Volume",
55995a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
56095a98265STakashi Iwai 
56139596dc8SJames Courtier-Dutton         CA_VOLUME("IEC958 Front Playback Volume",
56295a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
56339596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Rear Playback Volume",
56495a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
56539596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Center/LFE Playback Volume",
56695a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
56739596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Unknown Playback Volume",
56895a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
56995a98265STakashi Iwai 
57095a98265STakashi Iwai         CA_VOLUME("CAPTURE feedback Playback Volume",
57195a98265STakashi Iwai 		  1, CAPTURE_CONTROL),
57295a98265STakashi Iwai 
57395a98265STakashi Iwai 	{
57495a98265STakashi Iwai 		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
57595a98265STakashi Iwai 		.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
57695a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
57795a98265STakashi Iwai 		.count =	4,
57895a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
57995a98265STakashi Iwai 		.get =          snd_ca0106_spdif_get_mask
58095a98265STakashi Iwai 	},
5811da177e4SLinus Torvalds 	{
5821da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
58339596dc8SJames Courtier-Dutton 		.name =		"IEC958 Playback Switch",
58495a98265STakashi Iwai 		.info =		snd_ca0106_shared_spdif_info,
58595a98265STakashi Iwai 		.get =		snd_ca0106_shared_spdif_get,
58695a98265STakashi Iwai 		.put =		snd_ca0106_shared_spdif_put
58795a98265STakashi Iwai 	},
5881da177e4SLinus Torvalds 	{
5891da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
590e6327cf9SJames Courtier-Dutton 		.name =		"Digital Source Capture Enum",
59195a98265STakashi Iwai 		.info =		snd_ca0106_capture_source_info,
59295a98265STakashi Iwai 		.get =		snd_ca0106_capture_source_get,
59395a98265STakashi Iwai 		.put =		snd_ca0106_capture_source_put
59495a98265STakashi Iwai 	},
5951da177e4SLinus Torvalds 	{
5966129daaaSJames Courtier-Dutton 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
597e6327cf9SJames Courtier-Dutton 		.name =		"Analog Source Capture Enum",
5986129daaaSJames Courtier-Dutton 		.info =		snd_ca0106_i2c_capture_source_info,
5996129daaaSJames Courtier-Dutton 		.get =		snd_ca0106_i2c_capture_source_get,
6006129daaaSJames Courtier-Dutton 		.put =		snd_ca0106_i2c_capture_source_put
6016129daaaSJames Courtier-Dutton 	},
6026129daaaSJames Courtier-Dutton 	{
60395a98265STakashi Iwai 		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
60495a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
60595a98265STakashi Iwai 		.count =	4,
60695a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
60795a98265STakashi Iwai 		.get =          snd_ca0106_spdif_get,
60895a98265STakashi Iwai 		.put =          snd_ca0106_spdif_put
60995a98265STakashi Iwai 	},
6101da177e4SLinus Torvalds };
6111da177e4SLinus Torvalds 
6127c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \
6137c157069SJames Courtier-Dutton {								\
6147c157069SJames Courtier-Dutton 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
6157c157069SJames Courtier-Dutton 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
6167c157069SJames Courtier-Dutton 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
6177c157069SJames Courtier-Dutton 	.info =  snd_ca0106_i2c_volume_info,			\
6187c157069SJames Courtier-Dutton 	.get =   snd_ca0106_i2c_volume_get,			\
6197c157069SJames Courtier-Dutton 	.put =   snd_ca0106_i2c_volume_put,			\
6207c157069SJames Courtier-Dutton 	.tlv = { .p = snd_ca0106_db_scale2 },			\
6217c157069SJames Courtier-Dutton 	.private_value = chid					\
6227c157069SJames Courtier-Dutton }
6237c157069SJames Courtier-Dutton 
6247c157069SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
6257c157069SJames Courtier-Dutton         I2C_VOLUME("Phone Capture Volume", 0),
6267c157069SJames Courtier-Dutton         I2C_VOLUME("Mic Capture Volume", 1),
6277c157069SJames Courtier-Dutton         I2C_VOLUME("Line in Capture Volume", 2),
6287c157069SJames Courtier-Dutton         I2C_VOLUME("Aux Capture Volume", 3),
6297c157069SJames Courtier-Dutton };
6307c157069SJames Courtier-Dutton 
631b18cd538STrent Piepho #define SPI_SWITCH(xname,reg,bit) \
632b18cd538STrent Piepho {								\
633b18cd538STrent Piepho 	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
634b18cd538STrent Piepho 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
635b18cd538STrent Piepho 	.info	= spi_mute_info,				\
636b18cd538STrent Piepho 	.get	= spi_mute_get,					\
637b18cd538STrent Piepho 	.put	= spi_mute_put,					\
63818b5d32fSTrent Piepho 	.private_value = (reg<<SPI_REG_SHIFT) | (bit)		\
639b18cd538STrent Piepho }
640b18cd538STrent Piepho 
641b18cd538STrent Piepho static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[]
642b18cd538STrent Piepho __devinitdata = {
643b18cd538STrent Piepho 	SPI_SWITCH("Analog Front Playback Switch",
644b18cd538STrent Piepho 		   SPI_DMUTE4_REG, SPI_DMUTE4_BIT),
645b18cd538STrent Piepho 	SPI_SWITCH("Analog Rear Playback Switch",
646b18cd538STrent Piepho 		   SPI_DMUTE0_REG, SPI_DMUTE0_BIT),
647b18cd538STrent Piepho 	SPI_SWITCH("Analog Center/LFE Playback Switch",
648b18cd538STrent Piepho 		   SPI_DMUTE2_REG, SPI_DMUTE2_BIT),
649b18cd538STrent Piepho 	SPI_SWITCH("Analog Side Playback Switch",
650b18cd538STrent Piepho 		   SPI_DMUTE1_REG, SPI_DMUTE1_BIT),
651b18cd538STrent Piepho };
652b18cd538STrent Piepho 
653e4a3d145STakashi Iwai static int __devinit remove_ctl(struct snd_card *card, const char *name)
6541da177e4SLinus Torvalds {
655e4a3d145STakashi Iwai 	struct snd_ctl_elem_id id;
6561da177e4SLinus Torvalds 	memset(&id, 0, sizeof(id));
6571da177e4SLinus Torvalds 	strcpy(id.name, name);
6581da177e4SLinus Torvalds 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
6591da177e4SLinus Torvalds 	return snd_ctl_remove_id(card, &id);
6601da177e4SLinus Torvalds }
6611da177e4SLinus Torvalds 
662e4a3d145STakashi Iwai static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
6631da177e4SLinus Torvalds {
664e4a3d145STakashi Iwai 	struct snd_ctl_elem_id sid;
6651da177e4SLinus Torvalds 	memset(&sid, 0, sizeof(sid));
6661da177e4SLinus Torvalds 	/* FIXME: strcpy is bad. */
6671da177e4SLinus Torvalds 	strcpy(sid.name, name);
6681da177e4SLinus Torvalds 	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
6691da177e4SLinus Torvalds 	return snd_ctl_find_id(card, &sid);
6701da177e4SLinus Torvalds }
6711da177e4SLinus Torvalds 
672e4a3d145STakashi Iwai static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
6731da177e4SLinus Torvalds {
674e4a3d145STakashi Iwai 	struct snd_kcontrol *kctl = ctl_find(card, src);
6751da177e4SLinus Torvalds 	if (kctl) {
6761da177e4SLinus Torvalds 		strcpy(kctl->id.name, dst);
6771da177e4SLinus Torvalds 		return 0;
6781da177e4SLinus Torvalds 	}
6791da177e4SLinus Torvalds 	return -ENOENT;
6801da177e4SLinus Torvalds }
6811da177e4SLinus Torvalds 
682fca7f388STrent Piepho #define ADD_CTLS(emu, ctls)						\
683fca7f388STrent Piepho 	do {								\
684bed515b0SHarvey Harrison 		int i, _err;						\
685fca7f388STrent Piepho 		for (i = 0; i < ARRAY_SIZE(ctls); i++) {		\
686bed515b0SHarvey Harrison 			_err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
687bed515b0SHarvey Harrison 			if (_err < 0)					\
688bed515b0SHarvey Harrison 				return _err;				\
689fca7f388STrent Piepho 		}							\
690fca7f388STrent Piepho 	} while (0)
691fca7f388STrent Piepho 
69249c88b85STakashi Iwai static __devinitdata
69349c88b85STakashi Iwai DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1);
69449c88b85STakashi Iwai 
69549c88b85STakashi Iwai static char *slave_vols[] __devinitdata = {
69649c88b85STakashi Iwai 	"Analog Front Playback Volume",
69749c88b85STakashi Iwai         "Analog Rear Playback Volume",
69849c88b85STakashi Iwai 	"Analog Center/LFE Playback Volume",
69949c88b85STakashi Iwai         "Analog Side Playback Volume",
70049c88b85STakashi Iwai         "IEC958 Front Playback Volume",
70149c88b85STakashi Iwai 	"IEC958 Rear Playback Volume",
70249c88b85STakashi Iwai 	"IEC958 Center/LFE Playback Volume",
70349c88b85STakashi Iwai 	"IEC958 Unknown Playback Volume",
70449c88b85STakashi Iwai         "CAPTURE feedback Playback Volume",
70549c88b85STakashi Iwai 	NULL
70649c88b85STakashi Iwai };
70749c88b85STakashi Iwai 
70849c88b85STakashi Iwai static char *slave_sws[] __devinitdata = {
70949c88b85STakashi Iwai 	"Analog Front Playback Switch",
71049c88b85STakashi Iwai 	"Analog Rear Playback Switch",
71149c88b85STakashi Iwai 	"Analog Center/LFE Playback Switch",
71249c88b85STakashi Iwai 	"Analog Side Playback Switch",
71349c88b85STakashi Iwai 	"IEC958 Playback Switch",
71449c88b85STakashi Iwai 	NULL
71549c88b85STakashi Iwai };
71649c88b85STakashi Iwai 
71749c88b85STakashi Iwai static void __devinit add_slaves(struct snd_card *card,
71849c88b85STakashi Iwai 				 struct snd_kcontrol *master, char **list)
71949c88b85STakashi Iwai {
72049c88b85STakashi Iwai 	for (; *list; list++) {
72149c88b85STakashi Iwai 		struct snd_kcontrol *slave = ctl_find(card, *list);
72249c88b85STakashi Iwai 		if (slave)
72349c88b85STakashi Iwai 			snd_ctl_add_slave(master, slave);
72449c88b85STakashi Iwai 	}
72549c88b85STakashi Iwai }
72649c88b85STakashi Iwai 
727e4a3d145STakashi Iwai int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
7281da177e4SLinus Torvalds {
729fca7f388STrent Piepho 	int err;
730e4a3d145STakashi Iwai         struct snd_card *card = emu->card;
7311da177e4SLinus Torvalds 	char **c;
73249c88b85STakashi Iwai 	struct snd_kcontrol *vmaster;
7331da177e4SLinus Torvalds 	static char *ca0106_remove_ctls[] = {
7341da177e4SLinus Torvalds 		"Master Mono Playback Switch",
7351da177e4SLinus Torvalds 		"Master Mono Playback Volume",
7361da177e4SLinus Torvalds 		"3D Control - Switch",
7371da177e4SLinus Torvalds 		"3D Control Sigmatel - Depth",
7381da177e4SLinus Torvalds 		"PCM Playback Switch",
7391da177e4SLinus Torvalds 		"PCM Playback Volume",
7401da177e4SLinus Torvalds 		"CD Playback Switch",
7411da177e4SLinus Torvalds 		"CD Playback Volume",
7421da177e4SLinus Torvalds 		"Phone Playback Switch",
7431da177e4SLinus Torvalds 		"Phone Playback Volume",
7441da177e4SLinus Torvalds 		"Video Playback Switch",
7451da177e4SLinus Torvalds 		"Video Playback Volume",
7461da177e4SLinus Torvalds 		"PC Speaker Playback Switch",
7471da177e4SLinus Torvalds 		"PC Speaker Playback Volume",
7481da177e4SLinus Torvalds 		"Mono Output Select",
7491da177e4SLinus Torvalds 		"Capture Source",
7501da177e4SLinus Torvalds 		"Capture Switch",
7511da177e4SLinus Torvalds 		"Capture Volume",
7521da177e4SLinus Torvalds 		"External Amplifier",
7531da177e4SLinus Torvalds 		"Sigmatel 4-Speaker Stereo Playback Switch",
7541da177e4SLinus Torvalds 		"Sigmatel Surround Phase Inversion Playback ",
7551da177e4SLinus Torvalds 		NULL
7561da177e4SLinus Torvalds 	};
7571da177e4SLinus Torvalds 	static char *ca0106_rename_ctls[] = {
7581da177e4SLinus Torvalds 		"Master Playback Switch", "Capture Switch",
7591da177e4SLinus Torvalds 		"Master Playback Volume", "Capture Volume",
7601da177e4SLinus Torvalds 		"Line Playback Switch", "AC97 Line Capture Switch",
7611da177e4SLinus Torvalds 		"Line Playback Volume", "AC97 Line Capture Volume",
7621da177e4SLinus Torvalds 		"Aux Playback Switch", "AC97 Aux Capture Switch",
7631da177e4SLinus Torvalds 		"Aux Playback Volume", "AC97 Aux Capture Volume",
7641da177e4SLinus Torvalds 		"Mic Playback Switch", "AC97 Mic Capture Switch",
7651da177e4SLinus Torvalds 		"Mic Playback Volume", "AC97 Mic Capture Volume",
7661da177e4SLinus Torvalds 		"Mic Select", "AC97 Mic Select",
7671da177e4SLinus Torvalds 		"Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
7681da177e4SLinus Torvalds 		NULL
7691da177e4SLinus Torvalds 	};
7701da177e4SLinus Torvalds #if 1
7711da177e4SLinus Torvalds 	for (c = ca0106_remove_ctls; *c; c++)
7721da177e4SLinus Torvalds 		remove_ctl(card, *c);
7731da177e4SLinus Torvalds 	for (c = ca0106_rename_ctls; *c; c += 2)
7741da177e4SLinus Torvalds 		rename_ctl(card, c[0], c[1]);
7751da177e4SLinus Torvalds #endif
7761da177e4SLinus Torvalds 
777fca7f388STrent Piepho 	ADD_CTLS(emu, snd_ca0106_volume_ctls);
77895a98265STakashi Iwai 	if (emu->details->i2c_adc == 1) {
779fca7f388STrent Piepho 		ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
780be0b7b01SJames Courtier-Dutton 		if (emu->details->gpio_type == 1)
78195a98265STakashi Iwai 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
782be0b7b01SJames Courtier-Dutton 		else  /* gpio_type == 2 */
783be0b7b01SJames Courtier-Dutton 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
78495a98265STakashi Iwai 		if (err < 0)
7851da177e4SLinus Torvalds 			return err;
78695a98265STakashi Iwai 	}
787fca7f388STrent Piepho 	if (emu->details->spi_dac == 1)
788fca7f388STrent Piepho 		ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls);
78949c88b85STakashi Iwai 
79049c88b85STakashi Iwai 	/* Create virtual master controls */
79149c88b85STakashi Iwai 	vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
79249c88b85STakashi Iwai 					      snd_ca0106_master_db_scale);
79349c88b85STakashi Iwai 	if (!vmaster)
79449c88b85STakashi Iwai 		return -ENOMEM;
79549c88b85STakashi Iwai 	add_slaves(card, vmaster, slave_vols);
79649c88b85STakashi Iwai 
79749c88b85STakashi Iwai 	if (emu->details->spi_dac == 1) {
79849c88b85STakashi Iwai 		vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
79949c88b85STakashi Iwai 						      NULL);
80049c88b85STakashi Iwai 		if (!vmaster)
80149c88b85STakashi Iwai 			return -ENOMEM;
80249c88b85STakashi Iwai 		add_slaves(card, vmaster, slave_sws);
80349c88b85STakashi Iwai 	}
8041da177e4SLinus Torvalds         return 0;
8051da177e4SLinus Torvalds }
8061da177e4SLinus Torvalds 
8075da95273STakashi Iwai #ifdef CONFIG_PM
8085da95273STakashi Iwai struct ca0106_vol_tbl {
8095da95273STakashi Iwai 	unsigned int channel_id;
810*8df0f707STakashi Iwai 	unsigned int reg;
8115da95273STakashi Iwai };
8125da95273STakashi Iwai 
8135da95273STakashi Iwai static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
8145da95273STakashi Iwai 	{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
8155da95273STakashi Iwai 	{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
8165da95273STakashi Iwai 	{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
8175da95273STakashi Iwai 	{ CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 },
8185da95273STakashi Iwai 	{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 },
8195da95273STakashi Iwai 	{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 },
8205da95273STakashi Iwai 	{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 },
8215da95273STakashi Iwai 	{ CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 },
8225da95273STakashi Iwai 	{ 1, CAPTURE_CONTROL },
8235da95273STakashi Iwai };
8245da95273STakashi Iwai 
8255da95273STakashi Iwai void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip)
8265da95273STakashi Iwai {
8275da95273STakashi Iwai 	int i;
8285da95273STakashi Iwai 
8295da95273STakashi Iwai 	/* save volumes */
8305da95273STakashi Iwai 	for (i = 0; i < NUM_SAVED_VOLUMES; i++)
8315da95273STakashi Iwai 		chip->saved_vol[i] =
8325da95273STakashi Iwai 			snd_ca0106_ptr_read(chip, saved_volumes[i].reg,
8335da95273STakashi Iwai 					    saved_volumes[i].channel_id);
8345da95273STakashi Iwai }
8355da95273STakashi Iwai 
8365da95273STakashi Iwai void snd_ca0106_mixer_resume(struct snd_ca0106  *chip)
8375da95273STakashi Iwai {
8385da95273STakashi Iwai 	int i;
8395da95273STakashi Iwai 
8405da95273STakashi Iwai 	for (i = 0; i < NUM_SAVED_VOLUMES; i++)
8415da95273STakashi Iwai 		snd_ca0106_ptr_write(chip, saved_volumes[i].reg,
8425da95273STakashi Iwai 				     saved_volumes[i].channel_id,
8435da95273STakashi Iwai 				     chip->saved_vol[i]);
8445da95273STakashi Iwai 
8455da95273STakashi Iwai 	ca0106_spdif_enable(chip);
8465da95273STakashi Iwai 	ca0106_set_capture_source(chip);
8475da95273STakashi Iwai 	ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1);
8485da95273STakashi Iwai 	for (i = 0; i < 4; i++)
8495da95273STakashi Iwai 		ca0106_set_spdif_bits(chip, i);
8505da95273STakashi Iwai 	if (chip->details->i2c_adc)
8515da95273STakashi Iwai 		ca0106_set_capture_mic_line_in(chip);
8525da95273STakashi Iwai }
8535da95273STakashi Iwai #endif /* CONFIG_PM */
854