xref: /openbmc/linux/sound/pci/ca0106/ca0106_mixer.c (revision c7561cd80469f2fe4a6be0984db57832ee7f2a3b)
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  *
4525985edcSLucas De Marchi  *  This code was initially 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/moduleparam.h>
671da177e4SLinus Torvalds #include <sound/core.h>
681da177e4SLinus Torvalds #include <sound/initval.h>
691da177e4SLinus Torvalds #include <sound/pcm.h>
701da177e4SLinus Torvalds #include <sound/ac97_codec.h>
711da177e4SLinus Torvalds #include <sound/info.h>
7242750b04SJaroslav Kysela #include <sound/tlv.h>
736473d160SJean Delvare #include <asm/io.h>
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds #include "ca0106.h"
761da177e4SLinus Torvalds 
775da95273STakashi Iwai static void ca0106_spdif_enable(struct snd_ca0106 *emu)
785da95273STakashi Iwai {
795da95273STakashi Iwai 	unsigned int val;
805da95273STakashi Iwai 
815da95273STakashi Iwai 	if (emu->spdif_enable) {
825da95273STakashi Iwai 		/* Digital */
835da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
845da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
855da95273STakashi Iwai 		val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
865da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
875da95273STakashi Iwai 		val = inl(emu->port + GPIO) & ~0x101;
885da95273STakashi Iwai 		outl(val, emu->port + GPIO);
895da95273STakashi Iwai 
905da95273STakashi Iwai 	} else {
915da95273STakashi Iwai 		/* Analog */
925da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
935da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
945da95273STakashi Iwai 		val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
955da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
965da95273STakashi Iwai 		val = inl(emu->port + GPIO) | 0x101;
975da95273STakashi Iwai 		outl(val, emu->port + GPIO);
985da95273STakashi Iwai 	}
995da95273STakashi Iwai }
1005da95273STakashi Iwai 
1015da95273STakashi Iwai static void ca0106_set_capture_source(struct snd_ca0106 *emu)
1025da95273STakashi Iwai {
1035da95273STakashi Iwai 	unsigned int val = emu->capture_source;
1045da95273STakashi Iwai 	unsigned int source, mask;
1055da95273STakashi Iwai 	source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
1065da95273STakashi Iwai 	mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
1075da95273STakashi Iwai 	snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
1085da95273STakashi Iwai }
1095da95273STakashi Iwai 
1105da95273STakashi Iwai static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu,
1115da95273STakashi Iwai 					  unsigned int val, int force)
1125da95273STakashi Iwai {
1135da95273STakashi Iwai 	unsigned int ngain, ogain;
1145da95273STakashi Iwai 	u32 source;
1155da95273STakashi Iwai 
1165da95273STakashi Iwai 	snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
1175da95273STakashi Iwai 	ngain = emu->i2c_capture_volume[val][0]; /* Left */
1185da95273STakashi Iwai 	ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
1195da95273STakashi Iwai 	if (force || ngain != ogain)
1205da95273STakashi Iwai 		snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff);
1215da95273STakashi Iwai 	ngain = emu->i2c_capture_volume[val][1]; /* Right */
1225da95273STakashi Iwai 	ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
1235da95273STakashi Iwai 	if (force || ngain != ogain)
1245da95273STakashi Iwai 		snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff);
1255da95273STakashi Iwai 	source = 1 << val;
1265da95273STakashi Iwai 	snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
1275da95273STakashi Iwai 	emu->i2c_capture_source = val;
1285da95273STakashi Iwai }
1295da95273STakashi Iwai 
1305da95273STakashi Iwai static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
1315da95273STakashi Iwai {
1325da95273STakashi Iwai 	u32 tmp;
1335da95273STakashi Iwai 
1345da95273STakashi Iwai 	if (emu->capture_mic_line_in) {
1355da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
1365da95273STakashi Iwai 		tmp = inl(emu->port+GPIO) & ~0x400;
1375da95273STakashi Iwai 		tmp = tmp | 0x400;
1385da95273STakashi Iwai 		outl(tmp, emu->port+GPIO);
1395da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
1405da95273STakashi Iwai 	} else {
1415da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
1425da95273STakashi Iwai 		tmp = inl(emu->port+GPIO) & ~0x400;
1435da95273STakashi Iwai 		outl(tmp, emu->port+GPIO);
1445da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
1455da95273STakashi Iwai 	}
1465da95273STakashi Iwai }
1475da95273STakashi Iwai 
1485da95273STakashi Iwai static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx)
1495da95273STakashi Iwai {
1503d475829STakashi Iwai 	snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]);
1515da95273STakashi Iwai }
1525da95273STakashi Iwai 
1535da95273STakashi Iwai /*
1545da95273STakashi Iwai  */
1550cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
1560cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
15742750b04SJaroslav Kysela 
158a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info	snd_ctl_boolean_mono_info
1591da177e4SLinus Torvalds 
160e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
161e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1621da177e4SLinus Torvalds {
163e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1641da177e4SLinus Torvalds 
1655fe619f9STakashi Iwai 	ucontrol->value.integer.value[0] = emu->spdif_enable;
1661da177e4SLinus Torvalds 	return 0;
1671da177e4SLinus Torvalds }
1681da177e4SLinus Torvalds 
169e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
170e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1711da177e4SLinus Torvalds {
172e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1731da177e4SLinus Torvalds 	unsigned int val;
1741da177e4SLinus Torvalds 	int change = 0;
1751da177e4SLinus Torvalds 
1765fe619f9STakashi Iwai 	val = !!ucontrol->value.integer.value[0];
1771da177e4SLinus Torvalds 	change = (emu->spdif_enable != val);
1781da177e4SLinus Torvalds 	if (change) {
1791da177e4SLinus Torvalds 		emu->spdif_enable = val;
1805da95273STakashi Iwai 		ca0106_spdif_enable(emu);
1811da177e4SLinus Torvalds 	}
1821da177e4SLinus Torvalds         return change;
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds 
185e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
186e4a3d145STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
1871da177e4SLinus Torvalds {
18895a98265STakashi Iwai 	static char *texts[6] = {
18939596dc8SJames Courtier-Dutton 		"IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
19095a98265STakashi Iwai 	};
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1931da177e4SLinus Torvalds 	uinfo->count = 1;
1941da177e4SLinus Torvalds 	uinfo->value.enumerated.items = 6;
1951da177e4SLinus Torvalds 	if (uinfo->value.enumerated.item > 5)
1961da177e4SLinus Torvalds                 uinfo->value.enumerated.item = 5;
1971da177e4SLinus Torvalds 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1981da177e4SLinus Torvalds 	return 0;
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
201e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
202e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
2031da177e4SLinus Torvalds {
204e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 	ucontrol->value.enumerated.item[0] = emu->capture_source;
2071da177e4SLinus Torvalds 	return 0;
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
210e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
211e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
2121da177e4SLinus Torvalds {
213e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2141da177e4SLinus Torvalds 	unsigned int val;
2151da177e4SLinus Torvalds 	int change = 0;
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 	val = ucontrol->value.enumerated.item[0] ;
2185fe619f9STakashi Iwai 	if (val >= 6)
2195fe619f9STakashi Iwai 		return -EINVAL;
2201da177e4SLinus Torvalds 	change = (emu->capture_source != val);
2211da177e4SLinus Torvalds 	if (change) {
2221da177e4SLinus Torvalds 		emu->capture_source = val;
2235da95273STakashi Iwai 		ca0106_set_capture_source(emu);
2241da177e4SLinus Torvalds 	}
2251da177e4SLinus Torvalds         return change;
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds 
2286129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
2296129daaaSJames Courtier-Dutton 					  struct snd_ctl_elem_info *uinfo)
2306129daaaSJames Courtier-Dutton {
2316129daaaSJames Courtier-Dutton 	static char *texts[6] = {
2326129daaaSJames Courtier-Dutton 		"Phone", "Mic", "Line in", "Aux"
2336129daaaSJames Courtier-Dutton 	};
2346129daaaSJames Courtier-Dutton 
2356129daaaSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2366129daaaSJames Courtier-Dutton 	uinfo->count = 1;
2376129daaaSJames Courtier-Dutton 	uinfo->value.enumerated.items = 4;
2386129daaaSJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 3)
2396129daaaSJames Courtier-Dutton                 uinfo->value.enumerated.item = 3;
2406129daaaSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
2416129daaaSJames Courtier-Dutton 	return 0;
2426129daaaSJames Courtier-Dutton }
2436129daaaSJames Courtier-Dutton 
2446129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
2456129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
2466129daaaSJames Courtier-Dutton {
2476129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2486129daaaSJames Courtier-Dutton 
2496129daaaSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
2506129daaaSJames Courtier-Dutton 	return 0;
2516129daaaSJames Courtier-Dutton }
2526129daaaSJames Courtier-Dutton 
2536129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
2546129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
2556129daaaSJames Courtier-Dutton {
2566129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2576129daaaSJames Courtier-Dutton 	unsigned int source_id;
2586129daaaSJames Courtier-Dutton 	int change = 0;
2596129daaaSJames Courtier-Dutton 	/* If the capture source has changed,
2606129daaaSJames Courtier-Dutton 	 * update the capture volume from the cached value
2616129daaaSJames Courtier-Dutton 	 * for the particular source.
2626129daaaSJames Courtier-Dutton 	 */
2636129daaaSJames Courtier-Dutton 	source_id = ucontrol->value.enumerated.item[0] ;
2645fe619f9STakashi Iwai 	if (source_id >= 4)
2655fe619f9STakashi Iwai 		return -EINVAL;
2666129daaaSJames Courtier-Dutton 	change = (emu->i2c_capture_source != source_id);
2676129daaaSJames Courtier-Dutton 	if (change) {
2685da95273STakashi Iwai 		ca0106_set_i2c_capture_source(emu, source_id, 0);
2696129daaaSJames Courtier-Dutton 	}
2706129daaaSJames Courtier-Dutton         return change;
2716129daaaSJames Courtier-Dutton }
2726129daaaSJames Courtier-Dutton 
273be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
274be0b7b01SJames Courtier-Dutton 					       struct snd_ctl_elem_info *uinfo)
275be0b7b01SJames Courtier-Dutton {
276be0b7b01SJames Courtier-Dutton 	static char *texts[2] = { "Side out", "Line in" };
277be0b7b01SJames Courtier-Dutton 
278be0b7b01SJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
279be0b7b01SJames Courtier-Dutton 	uinfo->count = 1;
280be0b7b01SJames Courtier-Dutton 	uinfo->value.enumerated.items = 2;
281be0b7b01SJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 1)
282be0b7b01SJames Courtier-Dutton                 uinfo->value.enumerated.item = 1;
283be0b7b01SJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
284be0b7b01SJames Courtier-Dutton 	return 0;
285be0b7b01SJames Courtier-Dutton }
286be0b7b01SJames Courtier-Dutton 
287e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
288e4a3d145STakashi Iwai 					       struct snd_ctl_elem_info *uinfo)
289ed144f3cSJames Courtier-Dutton {
290ed144f3cSJames Courtier-Dutton 	static char *texts[2] = { "Line in", "Mic in" };
291ed144f3cSJames Courtier-Dutton 
292ed144f3cSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
293ed144f3cSJames Courtier-Dutton 	uinfo->count = 1;
294ed144f3cSJames Courtier-Dutton 	uinfo->value.enumerated.items = 2;
295ed144f3cSJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 1)
296ed144f3cSJames Courtier-Dutton                 uinfo->value.enumerated.item = 1;
297ed144f3cSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
298ed144f3cSJames Courtier-Dutton 	return 0;
299ed144f3cSJames Courtier-Dutton }
300ed144f3cSJames Courtier-Dutton 
301e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
302e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
303ed144f3cSJames Courtier-Dutton {
304e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
305ed144f3cSJames Courtier-Dutton 
306ed144f3cSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
307ed144f3cSJames Courtier-Dutton 	return 0;
308ed144f3cSJames Courtier-Dutton }
309ed144f3cSJames Courtier-Dutton 
310e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
311e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
312ed144f3cSJames Courtier-Dutton {
313e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
314ed144f3cSJames Courtier-Dutton 	unsigned int val;
315ed144f3cSJames Courtier-Dutton 	int change = 0;
316ed144f3cSJames Courtier-Dutton 
317ed144f3cSJames Courtier-Dutton 	val = ucontrol->value.enumerated.item[0] ;
3185fe619f9STakashi Iwai 	if (val > 1)
3195fe619f9STakashi Iwai 		return -EINVAL;
320ed144f3cSJames Courtier-Dutton 	change = (emu->capture_mic_line_in != val);
321ed144f3cSJames Courtier-Dutton 	if (change) {
322ed144f3cSJames Courtier-Dutton 		emu->capture_mic_line_in = val;
3235da95273STakashi Iwai 		ca0106_set_capture_mic_line_in(emu);
324ed144f3cSJames Courtier-Dutton 	}
325ed144f3cSJames Courtier-Dutton         return change;
326ed144f3cSJames Courtier-Dutton }
327ed144f3cSJames Courtier-Dutton 
328e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
329ed144f3cSJames Courtier-Dutton {
330ed144f3cSJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
3316129daaaSJames Courtier-Dutton 	.name =		"Shared Mic/Line in Capture Switch",
332ed144f3cSJames Courtier-Dutton 	.info =		snd_ca0106_capture_mic_line_in_info,
333ed144f3cSJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
334ed144f3cSJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
335ed144f3cSJames Courtier-Dutton };
336ed144f3cSJames Courtier-Dutton 
337be0b7b01SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
338be0b7b01SJames Courtier-Dutton {
339be0b7b01SJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
340be0b7b01SJames Courtier-Dutton 	.name =		"Shared Line in/Side out Capture Switch",
341be0b7b01SJames Courtier-Dutton 	.info =		snd_ca0106_capture_line_in_side_out_info,
342be0b7b01SJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
343be0b7b01SJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
344be0b7b01SJames Courtier-Dutton };
345be0b7b01SJames Courtier-Dutton 
346be0b7b01SJames Courtier-Dutton 
347e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
348e4a3d145STakashi Iwai 				 struct snd_ctl_elem_info *uinfo)
3491da177e4SLinus Torvalds {
3501da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
3511da177e4SLinus Torvalds 	uinfo->count = 1;
3521da177e4SLinus Torvalds 	return 0;
3531da177e4SLinus Torvalds }
3541da177e4SLinus Torvalds 
3553d475829STakashi Iwai static void decode_spdif_bits(unsigned char *status, unsigned int bits)
3563d475829STakashi Iwai {
3573d475829STakashi Iwai 	status[0] = (bits >> 0) & 0xff;
3583d475829STakashi Iwai 	status[1] = (bits >> 8) & 0xff;
3593d475829STakashi Iwai 	status[2] = (bits >> 16) & 0xff;
3603d475829STakashi Iwai 	status[3] = (bits >> 24) & 0xff;
3613d475829STakashi Iwai }
3623d475829STakashi Iwai 
3633d475829STakashi Iwai static int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol,
364e4a3d145STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3651da177e4SLinus Torvalds {
366e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3671da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3681da177e4SLinus Torvalds 
3693d475829STakashi Iwai 	decode_spdif_bits(ucontrol->value.iec958.status,
3703d475829STakashi Iwai 			  emu->spdif_bits[idx]);
3713d475829STakashi Iwai         return 0;
3723d475829STakashi Iwai }
3733d475829STakashi Iwai 
3743d475829STakashi Iwai static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol,
3753d475829STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3763d475829STakashi Iwai {
3773d475829STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3783d475829STakashi Iwai 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3793d475829STakashi Iwai 
3803d475829STakashi Iwai 	decode_spdif_bits(ucontrol->value.iec958.status,
3813d475829STakashi Iwai 			  emu->spdif_str_bits[idx]);
3821da177e4SLinus Torvalds         return 0;
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds 
385e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
386e4a3d145STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3871da177e4SLinus Torvalds {
3881da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = 0xff;
3891da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = 0xff;
3901da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = 0xff;
3911da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = 0xff;
3921da177e4SLinus Torvalds         return 0;
3931da177e4SLinus Torvalds }
3941da177e4SLinus Torvalds 
3953d475829STakashi Iwai static unsigned int encode_spdif_bits(unsigned char *status)
3963d475829STakashi Iwai {
3973d475829STakashi Iwai 	return ((unsigned int)status[0] << 0) |
3983d475829STakashi Iwai 		((unsigned int)status[1] << 8) |
3993d475829STakashi Iwai 		((unsigned int)status[2] << 16) |
4003d475829STakashi Iwai 		((unsigned int)status[3] << 24);
4013d475829STakashi Iwai }
4023d475829STakashi Iwai 
4033d475829STakashi Iwai static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol,
404e4a3d145STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
4051da177e4SLinus Torvalds {
406e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4071da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
4081da177e4SLinus Torvalds 	unsigned int val;
4091da177e4SLinus Torvalds 
4103d475829STakashi Iwai 	val = encode_spdif_bits(ucontrol->value.iec958.status);
4113d475829STakashi Iwai 	if (val != emu->spdif_bits[idx]) {
4121da177e4SLinus Torvalds 		emu->spdif_bits[idx] = val;
4133d475829STakashi Iwai 		/* FIXME: this isn't safe, but needed to keep the compatibility
4143d475829STakashi Iwai 		 * with older alsa-lib config
4153d475829STakashi Iwai 		 */
4163d475829STakashi Iwai 		emu->spdif_str_bits[idx] = val;
4175da95273STakashi Iwai 		ca0106_set_spdif_bits(emu, idx);
4183d475829STakashi Iwai 		return 1;
4191da177e4SLinus Torvalds 	}
4203d475829STakashi Iwai 	return 0;
4213d475829STakashi Iwai }
4223d475829STakashi Iwai 
4233d475829STakashi Iwai static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol,
4243d475829STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
4253d475829STakashi Iwai {
4263d475829STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4273d475829STakashi Iwai 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
4283d475829STakashi Iwai 	unsigned int val;
4293d475829STakashi Iwai 
4303d475829STakashi Iwai 	val = encode_spdif_bits(ucontrol->value.iec958.status);
4313d475829STakashi Iwai 	if (val != emu->spdif_str_bits[idx]) {
4323d475829STakashi Iwai 		emu->spdif_str_bits[idx] = val;
4333d475829STakashi Iwai 		ca0106_set_spdif_bits(emu, idx);
4343d475829STakashi Iwai 		return 1;
4353d475829STakashi Iwai 	}
4363d475829STakashi Iwai         return 0;
4371da177e4SLinus Torvalds }
4381da177e4SLinus Torvalds 
439e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
440e4a3d145STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
4411da177e4SLinus Torvalds {
4421da177e4SLinus Torvalds         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4431da177e4SLinus Torvalds         uinfo->count = 2;
4441da177e4SLinus Torvalds         uinfo->value.integer.min = 0;
4451da177e4SLinus Torvalds         uinfo->value.integer.max = 255;
4461da177e4SLinus Torvalds         return 0;
4471da177e4SLinus Torvalds }
4481da177e4SLinus Torvalds 
449e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
450e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
4511da177e4SLinus Torvalds {
452e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4531da177e4SLinus Torvalds         unsigned int value;
45495a98265STakashi Iwai 	int channel_id, reg;
45595a98265STakashi Iwai 
45695a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
45795a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds         value = snd_ca0106_ptr_read(emu, reg, channel_id);
4601da177e4SLinus Torvalds         ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
4611da177e4SLinus Torvalds         ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
4621da177e4SLinus Torvalds         return 0;
4631da177e4SLinus Torvalds }
4641da177e4SLinus Torvalds 
465e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
466e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
4671da177e4SLinus Torvalds {
468e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
46995a98265STakashi Iwai         unsigned int oval, nval;
47095a98265STakashi Iwai 	int channel_id, reg;
47195a98265STakashi Iwai 
47295a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
47395a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
47495a98265STakashi Iwai 
47595a98265STakashi Iwai 	oval = snd_ca0106_ptr_read(emu, reg, channel_id);
47695a98265STakashi Iwai 	nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
47795a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) << 16);
47895a98265STakashi Iwai         nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
47995a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) );
48095a98265STakashi Iwai 	if (oval == nval)
48195a98265STakashi Iwai 		return 0;
48295a98265STakashi Iwai 	snd_ca0106_ptr_write(emu, reg, channel_id, nval);
4831da177e4SLinus Torvalds 	return 1;
4841da177e4SLinus Torvalds }
48595a98265STakashi Iwai 
4866129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
4876129daaaSJames Courtier-Dutton 				  struct snd_ctl_elem_info *uinfo)
4886129daaaSJames Courtier-Dutton {
4896129daaaSJames Courtier-Dutton         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4906129daaaSJames Courtier-Dutton         uinfo->count = 2;
4916129daaaSJames Courtier-Dutton         uinfo->value.integer.min = 0;
4926129daaaSJames Courtier-Dutton         uinfo->value.integer.max = 255;
4936129daaaSJames Courtier-Dutton         return 0;
4946129daaaSJames Courtier-Dutton }
4956129daaaSJames Courtier-Dutton 
4966129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
4976129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4986129daaaSJames Courtier-Dutton {
4996129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
5006129daaaSJames Courtier-Dutton 	int source_id;
5016129daaaSJames Courtier-Dutton 
5026129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
5036129daaaSJames Courtier-Dutton 
5046129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
5056129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
5066129daaaSJames Courtier-Dutton         return 0;
5076129daaaSJames Courtier-Dutton }
5086129daaaSJames Courtier-Dutton 
5096129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
5106129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
5116129daaaSJames Courtier-Dutton {
5126129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
5136129daaaSJames Courtier-Dutton         unsigned int ogain;
5146129daaaSJames Courtier-Dutton         unsigned int ngain;
5156129daaaSJames Courtier-Dutton 	int source_id;
5166129daaaSJames Courtier-Dutton 	int change = 0;
5176129daaaSJames Courtier-Dutton 
5186129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
5196129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
5206129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[0];
5216129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
5225fe619f9STakashi Iwai 		return -EINVAL;
5236129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
5246129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
5256129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
5266129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
5276129daaaSJames Courtier-Dutton 		change = 1;
5286129daaaSJames Courtier-Dutton 	}
5296129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
5306129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[1];
5316129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
5325fe619f9STakashi Iwai 		return -EINVAL;
5336129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
5346129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
5356129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
5366129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
5376129daaaSJames Courtier-Dutton 		change = 1;
5386129daaaSJames Courtier-Dutton 	}
5396129daaaSJames Courtier-Dutton 
5406129daaaSJames Courtier-Dutton 	return change;
5416129daaaSJames Courtier-Dutton }
5426129daaaSJames Courtier-Dutton 
543b18cd538STrent Piepho #define spi_mute_info	snd_ctl_boolean_mono_info
544b18cd538STrent Piepho 
545b18cd538STrent Piepho static int spi_mute_get(struct snd_kcontrol *kcontrol,
546b18cd538STrent Piepho 			struct snd_ctl_elem_value *ucontrol)
547b18cd538STrent Piepho {
548b18cd538STrent Piepho 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
549b18cd538STrent Piepho 	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
550b18cd538STrent Piepho 	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
551b18cd538STrent Piepho 
552b18cd538STrent Piepho 	ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit);
553b18cd538STrent Piepho 	return 0;
554b18cd538STrent Piepho }
555b18cd538STrent Piepho 
556b18cd538STrent Piepho static int spi_mute_put(struct snd_kcontrol *kcontrol,
557b18cd538STrent Piepho 			struct snd_ctl_elem_value *ucontrol)
558b18cd538STrent Piepho {
559b18cd538STrent Piepho 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
560b18cd538STrent Piepho 	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
561b18cd538STrent Piepho 	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
562b18cd538STrent Piepho 	int ret;
563b18cd538STrent Piepho 
564b18cd538STrent Piepho 	ret = emu->spi_dac_reg[reg] & bit;
565b18cd538STrent Piepho 	if (ucontrol->value.integer.value[0]) {
566b18cd538STrent Piepho 		if (!ret)	/* bit already cleared, do nothing */
567b18cd538STrent Piepho 			return 0;
568b18cd538STrent Piepho 		emu->spi_dac_reg[reg] &= ~bit;
569b18cd538STrent Piepho 	} else {
570b18cd538STrent Piepho 		if (ret)	/* bit already set, do nothing */
571b18cd538STrent Piepho 			return 0;
572b18cd538STrent Piepho 		emu->spi_dac_reg[reg] |= bit;
573b18cd538STrent Piepho 	}
574b18cd538STrent Piepho 
575b18cd538STrent Piepho 	ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]);
5765fe619f9STakashi Iwai 	return ret ? -EINVAL : 1;
577b18cd538STrent Piepho }
578b18cd538STrent Piepho 
57995a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \
58095a98265STakashi Iwai {								\
58195a98265STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
582302e9c5aSJaroslav Kysela 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
583302e9c5aSJaroslav Kysela 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
58495a98265STakashi Iwai 	.info =	 snd_ca0106_volume_info,			\
58595a98265STakashi Iwai 	.get =   snd_ca0106_volume_get,				\
58695a98265STakashi Iwai 	.put =   snd_ca0106_volume_put,				\
5877cf0a953STakashi Iwai 	.tlv = { .p = snd_ca0106_db_scale1 },			\
58895a98265STakashi Iwai 	.private_value = ((chid) << 8) | (reg)			\
5891da177e4SLinus Torvalds }
5901da177e4SLinus Torvalds 
591e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
59295a98265STakashi Iwai 	CA_VOLUME("Analog Front Playback Volume",
59395a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
59495a98265STakashi Iwai         CA_VOLUME("Analog Rear Playback Volume",
59595a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
59695a98265STakashi Iwai 	CA_VOLUME("Analog Center/LFE Playback Volume",
59795a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
59895a98265STakashi Iwai         CA_VOLUME("Analog Side Playback Volume",
59995a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
60095a98265STakashi Iwai 
60139596dc8SJames Courtier-Dutton         CA_VOLUME("IEC958 Front Playback Volume",
60295a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
60339596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Rear Playback Volume",
60495a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
60539596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Center/LFE Playback Volume",
60695a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
60739596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Unknown Playback Volume",
60895a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
60995a98265STakashi Iwai 
61095a98265STakashi Iwai         CA_VOLUME("CAPTURE feedback Playback Volume",
61195a98265STakashi Iwai 		  1, CAPTURE_CONTROL),
61295a98265STakashi Iwai 
61395a98265STakashi Iwai 	{
61495a98265STakashi Iwai 		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
61595a98265STakashi Iwai 		.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
61695a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
61795a98265STakashi Iwai 		.count =	4,
61895a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
61995a98265STakashi Iwai 		.get =          snd_ca0106_spdif_get_mask
62095a98265STakashi Iwai 	},
6211da177e4SLinus Torvalds 	{
6221da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
62339596dc8SJames Courtier-Dutton 		.name =		"IEC958 Playback Switch",
62495a98265STakashi Iwai 		.info =		snd_ca0106_shared_spdif_info,
62595a98265STakashi Iwai 		.get =		snd_ca0106_shared_spdif_get,
62695a98265STakashi Iwai 		.put =		snd_ca0106_shared_spdif_put
62795a98265STakashi Iwai 	},
6281da177e4SLinus Torvalds 	{
6291da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
630e6327cf9SJames Courtier-Dutton 		.name =		"Digital Source Capture Enum",
63195a98265STakashi Iwai 		.info =		snd_ca0106_capture_source_info,
63295a98265STakashi Iwai 		.get =		snd_ca0106_capture_source_get,
63395a98265STakashi Iwai 		.put =		snd_ca0106_capture_source_put
63495a98265STakashi Iwai 	},
6351da177e4SLinus Torvalds 	{
6366129daaaSJames Courtier-Dutton 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
637e6327cf9SJames Courtier-Dutton 		.name =		"Analog Source Capture Enum",
6386129daaaSJames Courtier-Dutton 		.info =		snd_ca0106_i2c_capture_source_info,
6396129daaaSJames Courtier-Dutton 		.get =		snd_ca0106_i2c_capture_source_get,
6406129daaaSJames Courtier-Dutton 		.put =		snd_ca0106_i2c_capture_source_put
6416129daaaSJames Courtier-Dutton 	},
6426129daaaSJames Courtier-Dutton 	{
64395a98265STakashi Iwai 		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
64495a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
64595a98265STakashi Iwai 		.count =	4,
64695a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
6473d475829STakashi Iwai 		.get =          snd_ca0106_spdif_get_default,
6483d475829STakashi Iwai 		.put =          snd_ca0106_spdif_put_default
6493d475829STakashi Iwai 	},
6503d475829STakashi Iwai 	{
6513d475829STakashi Iwai 		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
6523d475829STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
6533d475829STakashi Iwai 		.count =	4,
6543d475829STakashi Iwai 		.info =         snd_ca0106_spdif_info,
6553d475829STakashi Iwai 		.get =          snd_ca0106_spdif_get_stream,
6563d475829STakashi Iwai 		.put =          snd_ca0106_spdif_put_stream
65795a98265STakashi Iwai 	},
6581da177e4SLinus Torvalds };
6591da177e4SLinus Torvalds 
6607c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \
6617c157069SJames Courtier-Dutton {								\
6627c157069SJames Courtier-Dutton 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
6637c157069SJames Courtier-Dutton 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
6647c157069SJames Courtier-Dutton 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
6657c157069SJames Courtier-Dutton 	.info =  snd_ca0106_i2c_volume_info,			\
6667c157069SJames Courtier-Dutton 	.get =   snd_ca0106_i2c_volume_get,			\
6677c157069SJames Courtier-Dutton 	.put =   snd_ca0106_i2c_volume_put,			\
6687c157069SJames Courtier-Dutton 	.tlv = { .p = snd_ca0106_db_scale2 },			\
6697c157069SJames Courtier-Dutton 	.private_value = chid					\
6707c157069SJames Courtier-Dutton }
6717c157069SJames Courtier-Dutton 
6727c157069SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
6737c157069SJames Courtier-Dutton         I2C_VOLUME("Phone Capture Volume", 0),
6747c157069SJames Courtier-Dutton         I2C_VOLUME("Mic Capture Volume", 1),
6757c157069SJames Courtier-Dutton         I2C_VOLUME("Line in Capture Volume", 2),
6767c157069SJames Courtier-Dutton         I2C_VOLUME("Aux Capture Volume", 3),
6777c157069SJames Courtier-Dutton };
6787c157069SJames Courtier-Dutton 
67964e5310aSAndy Owen static const int spi_dmute_reg[] = {
68064e5310aSAndy Owen 	SPI_DMUTE0_REG,
68164e5310aSAndy Owen 	SPI_DMUTE1_REG,
68264e5310aSAndy Owen 	SPI_DMUTE2_REG,
68364e5310aSAndy Owen 	0,
68464e5310aSAndy Owen 	SPI_DMUTE4_REG,
685b18cd538STrent Piepho };
68664e5310aSAndy Owen static const int spi_dmute_bit[] = {
68764e5310aSAndy Owen 	SPI_DMUTE0_BIT,
68864e5310aSAndy Owen 	SPI_DMUTE1_BIT,
68964e5310aSAndy Owen 	SPI_DMUTE2_BIT,
69064e5310aSAndy Owen 	0,
69164e5310aSAndy Owen 	SPI_DMUTE4_BIT,
69264e5310aSAndy Owen };
69364e5310aSAndy Owen 
69464e5310aSAndy Owen static struct snd_kcontrol_new __devinit
69564e5310aSAndy Owen snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details,
69664e5310aSAndy Owen 			      int channel_id)
69764e5310aSAndy Owen {
69864e5310aSAndy Owen 	struct snd_kcontrol_new spi_switch = {0};
69964e5310aSAndy Owen 	int reg, bit;
70064e5310aSAndy Owen 	int dac_id;
70164e5310aSAndy Owen 
70264e5310aSAndy Owen 	spi_switch.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
70364e5310aSAndy Owen 	spi_switch.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
70464e5310aSAndy Owen 	spi_switch.info = spi_mute_info;
70564e5310aSAndy Owen 	spi_switch.get = spi_mute_get;
70664e5310aSAndy Owen 	spi_switch.put = spi_mute_put;
70764e5310aSAndy Owen 
70864e5310aSAndy Owen 	switch (channel_id) {
70964e5310aSAndy Owen 	case PCM_FRONT_CHANNEL:
71064e5310aSAndy Owen 		spi_switch.name = "Analog Front Playback Switch";
71164e5310aSAndy Owen 		dac_id = (details->spi_dac & 0xf000) >> (4 * 3);
71264e5310aSAndy Owen 		break;
71364e5310aSAndy Owen 	case PCM_REAR_CHANNEL:
71464e5310aSAndy Owen 		spi_switch.name = "Analog Rear Playback Switch";
71564e5310aSAndy Owen 		dac_id = (details->spi_dac & 0x0f00) >> (4 * 2);
71664e5310aSAndy Owen 		break;
71764e5310aSAndy Owen 	case PCM_CENTER_LFE_CHANNEL:
71864e5310aSAndy Owen 		spi_switch.name = "Analog Center/LFE Playback Switch";
71964e5310aSAndy Owen 		dac_id = (details->spi_dac & 0x00f0) >> (4 * 1);
72064e5310aSAndy Owen 		break;
72164e5310aSAndy Owen 	case PCM_UNKNOWN_CHANNEL:
72264e5310aSAndy Owen 		spi_switch.name = "Analog Side Playback Switch";
72364e5310aSAndy Owen 		dac_id = (details->spi_dac & 0x000f) >> (4 * 0);
72464e5310aSAndy Owen 		break;
72564e5310aSAndy Owen 	default:
72664e5310aSAndy Owen 		/* Unused channel */
72764e5310aSAndy Owen 		spi_switch.name = NULL;
72864e5310aSAndy Owen 		dac_id = 0;
72964e5310aSAndy Owen 	}
73064e5310aSAndy Owen 	reg = spi_dmute_reg[dac_id];
73164e5310aSAndy Owen 	bit = spi_dmute_bit[dac_id];
73264e5310aSAndy Owen 
73364e5310aSAndy Owen 	spi_switch.private_value = (reg << SPI_REG_SHIFT) | bit;
73464e5310aSAndy Owen 
73564e5310aSAndy Owen 	return spi_switch;
73664e5310aSAndy Owen }
737b18cd538STrent Piepho 
738e4a3d145STakashi Iwai static int __devinit remove_ctl(struct snd_card *card, const char *name)
7391da177e4SLinus Torvalds {
740e4a3d145STakashi Iwai 	struct snd_ctl_elem_id id;
7411da177e4SLinus Torvalds 	memset(&id, 0, sizeof(id));
7421da177e4SLinus Torvalds 	strcpy(id.name, name);
7431da177e4SLinus Torvalds 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
7441da177e4SLinus Torvalds 	return snd_ctl_remove_id(card, &id);
7451da177e4SLinus Torvalds }
7461da177e4SLinus Torvalds 
747e4a3d145STakashi Iwai static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
7481da177e4SLinus Torvalds {
749e4a3d145STakashi Iwai 	struct snd_ctl_elem_id sid;
7501da177e4SLinus Torvalds 	memset(&sid, 0, sizeof(sid));
7511da177e4SLinus Torvalds 	/* FIXME: strcpy is bad. */
7521da177e4SLinus Torvalds 	strcpy(sid.name, name);
7531da177e4SLinus Torvalds 	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
7541da177e4SLinus Torvalds 	return snd_ctl_find_id(card, &sid);
7551da177e4SLinus Torvalds }
7561da177e4SLinus Torvalds 
757e4a3d145STakashi Iwai static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
7581da177e4SLinus Torvalds {
759e4a3d145STakashi Iwai 	struct snd_kcontrol *kctl = ctl_find(card, src);
7601da177e4SLinus Torvalds 	if (kctl) {
7611da177e4SLinus Torvalds 		strcpy(kctl->id.name, dst);
7621da177e4SLinus Torvalds 		return 0;
7631da177e4SLinus Torvalds 	}
7641da177e4SLinus Torvalds 	return -ENOENT;
7651da177e4SLinus Torvalds }
7661da177e4SLinus Torvalds 
767fca7f388STrent Piepho #define ADD_CTLS(emu, ctls)						\
768fca7f388STrent Piepho 	do {								\
769bed515b0SHarvey Harrison 		int i, _err;						\
770fca7f388STrent Piepho 		for (i = 0; i < ARRAY_SIZE(ctls); i++) {		\
771bed515b0SHarvey Harrison 			_err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
772bed515b0SHarvey Harrison 			if (_err < 0)					\
773bed515b0SHarvey Harrison 				return _err;				\
774fca7f388STrent Piepho 		}							\
775fca7f388STrent Piepho 	} while (0)
776fca7f388STrent Piepho 
77749c88b85STakashi Iwai static __devinitdata
778c4865679STakashi Iwai DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
77949c88b85STakashi Iwai 
78049c88b85STakashi Iwai static char *slave_vols[] __devinitdata = {
78149c88b85STakashi Iwai 	"Analog Front Playback Volume",
78249c88b85STakashi Iwai         "Analog Rear Playback Volume",
78349c88b85STakashi Iwai 	"Analog Center/LFE Playback Volume",
78449c88b85STakashi Iwai         "Analog Side Playback Volume",
78549c88b85STakashi Iwai         "IEC958 Front Playback Volume",
78649c88b85STakashi Iwai 	"IEC958 Rear Playback Volume",
78749c88b85STakashi Iwai 	"IEC958 Center/LFE Playback Volume",
78849c88b85STakashi Iwai 	"IEC958 Unknown Playback Volume",
78949c88b85STakashi Iwai         "CAPTURE feedback Playback Volume",
79049c88b85STakashi Iwai 	NULL
79149c88b85STakashi Iwai };
79249c88b85STakashi Iwai 
79349c88b85STakashi Iwai static char *slave_sws[] __devinitdata = {
79449c88b85STakashi Iwai 	"Analog Front Playback Switch",
79549c88b85STakashi Iwai 	"Analog Rear Playback Switch",
79649c88b85STakashi Iwai 	"Analog Center/LFE Playback Switch",
79749c88b85STakashi Iwai 	"Analog Side Playback Switch",
79849c88b85STakashi Iwai 	"IEC958 Playback Switch",
79949c88b85STakashi Iwai 	NULL
80049c88b85STakashi Iwai };
80149c88b85STakashi Iwai 
80249c88b85STakashi Iwai static void __devinit add_slaves(struct snd_card *card,
80349c88b85STakashi Iwai 				 struct snd_kcontrol *master, char **list)
80449c88b85STakashi Iwai {
80549c88b85STakashi Iwai 	for (; *list; list++) {
80649c88b85STakashi Iwai 		struct snd_kcontrol *slave = ctl_find(card, *list);
80749c88b85STakashi Iwai 		if (slave)
80849c88b85STakashi Iwai 			snd_ctl_add_slave(master, slave);
80949c88b85STakashi Iwai 	}
81049c88b85STakashi Iwai }
81149c88b85STakashi Iwai 
812e4a3d145STakashi Iwai int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
8131da177e4SLinus Torvalds {
814fca7f388STrent Piepho 	int err;
815e4a3d145STakashi Iwai         struct snd_card *card = emu->card;
8161da177e4SLinus Torvalds 	char **c;
81749c88b85STakashi Iwai 	struct snd_kcontrol *vmaster;
8181da177e4SLinus Torvalds 	static char *ca0106_remove_ctls[] = {
8191da177e4SLinus Torvalds 		"Master Mono Playback Switch",
8201da177e4SLinus Torvalds 		"Master Mono Playback Volume",
8211da177e4SLinus Torvalds 		"3D Control - Switch",
8221da177e4SLinus Torvalds 		"3D Control Sigmatel - Depth",
8231da177e4SLinus Torvalds 		"PCM Playback Switch",
8241da177e4SLinus Torvalds 		"PCM Playback Volume",
8251da177e4SLinus Torvalds 		"CD Playback Switch",
8261da177e4SLinus Torvalds 		"CD Playback Volume",
8271da177e4SLinus Torvalds 		"Phone Playback Switch",
8281da177e4SLinus Torvalds 		"Phone Playback Volume",
8291da177e4SLinus Torvalds 		"Video Playback Switch",
8301da177e4SLinus Torvalds 		"Video Playback Volume",
831d355c82aSJaroslav Kysela 		"Beep Playback Switch",
832d355c82aSJaroslav Kysela 		"Beep Playback Volume",
8331da177e4SLinus Torvalds 		"Mono Output Select",
8341da177e4SLinus Torvalds 		"Capture Source",
8351da177e4SLinus Torvalds 		"Capture Switch",
8361da177e4SLinus Torvalds 		"Capture Volume",
8371da177e4SLinus Torvalds 		"External Amplifier",
8381da177e4SLinus Torvalds 		"Sigmatel 4-Speaker Stereo Playback Switch",
839afe6d7e3SAndreas Mohr 		"Surround Phase Inversion Playback Switch",
8401da177e4SLinus Torvalds 		NULL
8411da177e4SLinus Torvalds 	};
8421da177e4SLinus Torvalds 	static char *ca0106_rename_ctls[] = {
8431da177e4SLinus Torvalds 		"Master Playback Switch", "Capture Switch",
8441da177e4SLinus Torvalds 		"Master Playback Volume", "Capture Volume",
8451da177e4SLinus Torvalds 		"Line Playback Switch", "AC97 Line Capture Switch",
8461da177e4SLinus Torvalds 		"Line Playback Volume", "AC97 Line Capture Volume",
8471da177e4SLinus Torvalds 		"Aux Playback Switch", "AC97 Aux Capture Switch",
8481da177e4SLinus Torvalds 		"Aux Playback Volume", "AC97 Aux Capture Volume",
8491da177e4SLinus Torvalds 		"Mic Playback Switch", "AC97 Mic Capture Switch",
8501da177e4SLinus Torvalds 		"Mic Playback Volume", "AC97 Mic Capture Volume",
8511da177e4SLinus Torvalds 		"Mic Select", "AC97 Mic Select",
8521da177e4SLinus Torvalds 		"Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
8531da177e4SLinus Torvalds 		NULL
8541da177e4SLinus Torvalds 	};
8551da177e4SLinus Torvalds #if 1
8561da177e4SLinus Torvalds 	for (c = ca0106_remove_ctls; *c; c++)
8571da177e4SLinus Torvalds 		remove_ctl(card, *c);
8581da177e4SLinus Torvalds 	for (c = ca0106_rename_ctls; *c; c += 2)
8591da177e4SLinus Torvalds 		rename_ctl(card, c[0], c[1]);
8601da177e4SLinus Torvalds #endif
8611da177e4SLinus Torvalds 
862fca7f388STrent Piepho 	ADD_CTLS(emu, snd_ca0106_volume_ctls);
86395a98265STakashi Iwai 	if (emu->details->i2c_adc == 1) {
864fca7f388STrent Piepho 		ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
865be0b7b01SJames Courtier-Dutton 		if (emu->details->gpio_type == 1)
86695a98265STakashi Iwai 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
867be0b7b01SJames Courtier-Dutton 		else  /* gpio_type == 2 */
868be0b7b01SJames Courtier-Dutton 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
86995a98265STakashi Iwai 		if (err < 0)
8701da177e4SLinus Torvalds 			return err;
87195a98265STakashi Iwai 	}
87264e5310aSAndy Owen 	if (emu->details->spi_dac) {
87364e5310aSAndy Owen 		int i;
87464e5310aSAndy Owen 		for (i = 0;; i++) {
87564e5310aSAndy Owen 			struct snd_kcontrol_new ctl;
87664e5310aSAndy Owen 			ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i);
87764e5310aSAndy Owen 			if (!ctl.name)
87864e5310aSAndy Owen 				break;
87964e5310aSAndy Owen 			err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu));
88064e5310aSAndy Owen 			if (err < 0)
88164e5310aSAndy Owen 				return err;
88264e5310aSAndy Owen 		}
88364e5310aSAndy Owen 	}
88449c88b85STakashi Iwai 
88549c88b85STakashi Iwai 	/* Create virtual master controls */
88649c88b85STakashi Iwai 	vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
88749c88b85STakashi Iwai 					      snd_ca0106_master_db_scale);
88849c88b85STakashi Iwai 	if (!vmaster)
88949c88b85STakashi Iwai 		return -ENOMEM;
890601e1cc5STakashi Iwai 	err = snd_ctl_add(card, vmaster);
891601e1cc5STakashi Iwai 	if (err < 0)
892601e1cc5STakashi Iwai 		return err;
89349c88b85STakashi Iwai 	add_slaves(card, vmaster, slave_vols);
89449c88b85STakashi Iwai 
8956fef153aSAndy Owen 	if (emu->details->spi_dac) {
89649c88b85STakashi Iwai 		vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
89749c88b85STakashi Iwai 						      NULL);
89849c88b85STakashi Iwai 		if (!vmaster)
89949c88b85STakashi Iwai 			return -ENOMEM;
900601e1cc5STakashi Iwai 		err = snd_ctl_add(card, vmaster);
901601e1cc5STakashi Iwai 		if (err < 0)
902601e1cc5STakashi Iwai 			return err;
90349c88b85STakashi Iwai 		add_slaves(card, vmaster, slave_sws);
90449c88b85STakashi Iwai 	}
905eeaf100dSTakashi Iwai 
906eeaf100dSTakashi Iwai 	strcpy(card->mixername, "CA0106");
9071da177e4SLinus Torvalds         return 0;
9081da177e4SLinus Torvalds }
9091da177e4SLinus Torvalds 
910*c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP
9115da95273STakashi Iwai struct ca0106_vol_tbl {
9125da95273STakashi Iwai 	unsigned int channel_id;
9138df0f707STakashi Iwai 	unsigned int reg;
9145da95273STakashi Iwai };
9155da95273STakashi Iwai 
9165da95273STakashi Iwai static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
9175da95273STakashi Iwai 	{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
9185da95273STakashi Iwai 	{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
9195da95273STakashi Iwai 	{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
9205da95273STakashi Iwai 	{ CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 },
9215da95273STakashi Iwai 	{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 },
9225da95273STakashi Iwai 	{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 },
9235da95273STakashi Iwai 	{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 },
9245da95273STakashi Iwai 	{ CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 },
9255da95273STakashi Iwai 	{ 1, CAPTURE_CONTROL },
9265da95273STakashi Iwai };
9275da95273STakashi Iwai 
9285da95273STakashi Iwai void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip)
9295da95273STakashi Iwai {
9305da95273STakashi Iwai 	int i;
9315da95273STakashi Iwai 
9325da95273STakashi Iwai 	/* save volumes */
9335da95273STakashi Iwai 	for (i = 0; i < NUM_SAVED_VOLUMES; i++)
9345da95273STakashi Iwai 		chip->saved_vol[i] =
9355da95273STakashi Iwai 			snd_ca0106_ptr_read(chip, saved_volumes[i].reg,
9365da95273STakashi Iwai 					    saved_volumes[i].channel_id);
9375da95273STakashi Iwai }
9385da95273STakashi Iwai 
9395da95273STakashi Iwai void snd_ca0106_mixer_resume(struct snd_ca0106  *chip)
9405da95273STakashi Iwai {
9415da95273STakashi Iwai 	int i;
9425da95273STakashi Iwai 
9435da95273STakashi Iwai 	for (i = 0; i < NUM_SAVED_VOLUMES; i++)
9445da95273STakashi Iwai 		snd_ca0106_ptr_write(chip, saved_volumes[i].reg,
9455da95273STakashi Iwai 				     saved_volumes[i].channel_id,
9465da95273STakashi Iwai 				     chip->saved_vol[i]);
9475da95273STakashi Iwai 
9485da95273STakashi Iwai 	ca0106_spdif_enable(chip);
9495da95273STakashi Iwai 	ca0106_set_capture_source(chip);
9505da95273STakashi Iwai 	ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1);
9515da95273STakashi Iwai 	for (i = 0; i < 4; i++)
9525da95273STakashi Iwai 		ca0106_set_spdif_bits(chip, i);
9535da95273STakashi Iwai 	if (chip->details->i2c_adc)
9545da95273STakashi Iwai 		ca0106_set_capture_mic_line_in(chip);
9555da95273STakashi Iwai }
956*c7561cd8STakashi Iwai #endif /* CONFIG_PM_SLEEP */
957