xref: /openbmc/linux/sound/pci/ca0106/ca0106_mixer.c (revision 49c88b85b53767f97eb8c9171cb0b976c62a0114)
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 
780cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
790cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
8042750b04SJaroslav Kysela 
81a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info	snd_ctl_boolean_mono_info
821da177e4SLinus Torvalds 
83e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
84e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
851da177e4SLinus Torvalds {
86e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
871da177e4SLinus Torvalds 
885fe619f9STakashi Iwai 	ucontrol->value.integer.value[0] = emu->spdif_enable;
891da177e4SLinus Torvalds 	return 0;
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
92e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
93e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
941da177e4SLinus Torvalds {
95e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
961da177e4SLinus Torvalds 	unsigned int val;
971da177e4SLinus Torvalds 	int change = 0;
981da177e4SLinus Torvalds 	u32 mask;
991da177e4SLinus Torvalds 
1005fe619f9STakashi Iwai 	val = !!ucontrol->value.integer.value[0];
1011da177e4SLinus Torvalds 	change = (emu->spdif_enable != val);
1021da177e4SLinus Torvalds 	if (change) {
1031da177e4SLinus Torvalds 		emu->spdif_enable = val;
1045fe619f9STakashi Iwai 		if (val) {
1051da177e4SLinus Torvalds 			/* Digital */
1061da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
1071da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
1081da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
1091da177e4SLinus Torvalds 				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
1101da177e4SLinus Torvalds 			mask = inl(emu->port + GPIO) & ~0x101;
1111da177e4SLinus Torvalds 			outl(mask, emu->port + GPIO);
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 		} else {
1141da177e4SLinus Torvalds 			/* Analog */
1151da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
1161f82941eSJames Courtier-Dutton 			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
1171da177e4SLinus Torvalds 			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
1181da177e4SLinus Torvalds 				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
1191da177e4SLinus Torvalds 			mask = inl(emu->port + GPIO) | 0x101;
1201da177e4SLinus Torvalds 			outl(mask, emu->port + GPIO);
1211da177e4SLinus Torvalds 		}
1221da177e4SLinus Torvalds 	}
1231da177e4SLinus Torvalds         return change;
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds 
126e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
127e4a3d145STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
1281da177e4SLinus Torvalds {
12995a98265STakashi Iwai 	static char *texts[6] = {
13039596dc8SJames Courtier-Dutton 		"IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
13195a98265STakashi Iwai 	};
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1341da177e4SLinus Torvalds 	uinfo->count = 1;
1351da177e4SLinus Torvalds 	uinfo->value.enumerated.items = 6;
1361da177e4SLinus Torvalds 	if (uinfo->value.enumerated.item > 5)
1371da177e4SLinus Torvalds                 uinfo->value.enumerated.item = 5;
1381da177e4SLinus Torvalds 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1391da177e4SLinus Torvalds 	return 0;
1401da177e4SLinus Torvalds }
1411da177e4SLinus Torvalds 
142e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
143e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1441da177e4SLinus Torvalds {
145e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds 	ucontrol->value.enumerated.item[0] = emu->capture_source;
1481da177e4SLinus Torvalds 	return 0;
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds 
151e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
152e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1531da177e4SLinus Torvalds {
154e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1551da177e4SLinus Torvalds 	unsigned int val;
1561da177e4SLinus Torvalds 	int change = 0;
1571da177e4SLinus Torvalds 	u32 mask;
1581da177e4SLinus Torvalds 	u32 source;
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds 	val = ucontrol->value.enumerated.item[0] ;
1615fe619f9STakashi Iwai 	if (val >= 6)
1625fe619f9STakashi Iwai 		return -EINVAL;
1631da177e4SLinus Torvalds 	change = (emu->capture_source != val);
1641da177e4SLinus Torvalds 	if (change) {
1651da177e4SLinus Torvalds 		emu->capture_source = val;
1661da177e4SLinus Torvalds 		source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
1671da177e4SLinus Torvalds 		mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
1681da177e4SLinus Torvalds 		snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
1691da177e4SLinus Torvalds 	}
1701da177e4SLinus Torvalds         return change;
1711da177e4SLinus Torvalds }
1721da177e4SLinus Torvalds 
1736129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
1746129daaaSJames Courtier-Dutton 					  struct snd_ctl_elem_info *uinfo)
1756129daaaSJames Courtier-Dutton {
1766129daaaSJames Courtier-Dutton 	static char *texts[6] = {
1776129daaaSJames Courtier-Dutton 		"Phone", "Mic", "Line in", "Aux"
1786129daaaSJames Courtier-Dutton 	};
1796129daaaSJames Courtier-Dutton 
1806129daaaSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1816129daaaSJames Courtier-Dutton 	uinfo->count = 1;
1826129daaaSJames Courtier-Dutton 	uinfo->value.enumerated.items = 4;
1836129daaaSJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 3)
1846129daaaSJames Courtier-Dutton                 uinfo->value.enumerated.item = 3;
1856129daaaSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1866129daaaSJames Courtier-Dutton 	return 0;
1876129daaaSJames Courtier-Dutton }
1886129daaaSJames Courtier-Dutton 
1896129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
1906129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
1916129daaaSJames Courtier-Dutton {
1926129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1936129daaaSJames Courtier-Dutton 
1946129daaaSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
1956129daaaSJames Courtier-Dutton 	return 0;
1966129daaaSJames Courtier-Dutton }
1976129daaaSJames Courtier-Dutton 
1986129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
1996129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
2006129daaaSJames Courtier-Dutton {
2016129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2026129daaaSJames Courtier-Dutton 	unsigned int source_id;
2036129daaaSJames Courtier-Dutton 	unsigned int ngain, ogain;
2046129daaaSJames Courtier-Dutton 	int change = 0;
2056129daaaSJames Courtier-Dutton 	u32 source;
2066129daaaSJames Courtier-Dutton 	/* If the capture source has changed,
2076129daaaSJames Courtier-Dutton 	 * update the capture volume from the cached value
2086129daaaSJames Courtier-Dutton 	 * for the particular source.
2096129daaaSJames Courtier-Dutton 	 */
2106129daaaSJames Courtier-Dutton 	source_id = ucontrol->value.enumerated.item[0] ;
2115fe619f9STakashi Iwai 	if (source_id >= 4)
2125fe619f9STakashi Iwai 		return -EINVAL;
2136129daaaSJames Courtier-Dutton 	change = (emu->i2c_capture_source != source_id);
2146129daaaSJames Courtier-Dutton 	if (change) {
2156129daaaSJames Courtier-Dutton 		snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
2166129daaaSJames Courtier-Dutton 		ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
2176129daaaSJames Courtier-Dutton 		ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
2186129daaaSJames Courtier-Dutton 		if (ngain != ogain)
2196129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
2206129daaaSJames Courtier-Dutton 		ngain = emu->i2c_capture_volume[source_id][1]; /* Left */
2216129daaaSJames Courtier-Dutton 		ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */
2226129daaaSJames Courtier-Dutton 		if (ngain != ogain)
2236129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
2246129daaaSJames Courtier-Dutton 		source = 1 << source_id;
2256129daaaSJames Courtier-Dutton 		snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
2266129daaaSJames Courtier-Dutton 		emu->i2c_capture_source = source_id;
2276129daaaSJames Courtier-Dutton 	}
2286129daaaSJames Courtier-Dutton         return change;
2296129daaaSJames Courtier-Dutton }
2306129daaaSJames Courtier-Dutton 
231be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
232be0b7b01SJames Courtier-Dutton 					       struct snd_ctl_elem_info *uinfo)
233be0b7b01SJames Courtier-Dutton {
234be0b7b01SJames Courtier-Dutton 	static char *texts[2] = { "Side out", "Line in" };
235be0b7b01SJames Courtier-Dutton 
236be0b7b01SJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
237be0b7b01SJames Courtier-Dutton 	uinfo->count = 1;
238be0b7b01SJames Courtier-Dutton 	uinfo->value.enumerated.items = 2;
239be0b7b01SJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 1)
240be0b7b01SJames Courtier-Dutton                 uinfo->value.enumerated.item = 1;
241be0b7b01SJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
242be0b7b01SJames Courtier-Dutton 	return 0;
243be0b7b01SJames Courtier-Dutton }
244be0b7b01SJames Courtier-Dutton 
245e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
246e4a3d145STakashi Iwai 					       struct snd_ctl_elem_info *uinfo)
247ed144f3cSJames Courtier-Dutton {
248ed144f3cSJames Courtier-Dutton 	static char *texts[2] = { "Line in", "Mic in" };
249ed144f3cSJames Courtier-Dutton 
250ed144f3cSJames Courtier-Dutton 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
251ed144f3cSJames Courtier-Dutton 	uinfo->count = 1;
252ed144f3cSJames Courtier-Dutton 	uinfo->value.enumerated.items = 2;
253ed144f3cSJames Courtier-Dutton 	if (uinfo->value.enumerated.item > 1)
254ed144f3cSJames Courtier-Dutton                 uinfo->value.enumerated.item = 1;
255ed144f3cSJames Courtier-Dutton 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
256ed144f3cSJames Courtier-Dutton 	return 0;
257ed144f3cSJames Courtier-Dutton }
258ed144f3cSJames Courtier-Dutton 
259e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
260e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
261ed144f3cSJames Courtier-Dutton {
262e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
263ed144f3cSJames Courtier-Dutton 
264ed144f3cSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
265ed144f3cSJames Courtier-Dutton 	return 0;
266ed144f3cSJames Courtier-Dutton }
267ed144f3cSJames Courtier-Dutton 
268e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
269e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
270ed144f3cSJames Courtier-Dutton {
271e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
272ed144f3cSJames Courtier-Dutton 	unsigned int val;
273ed144f3cSJames Courtier-Dutton 	int change = 0;
274ed144f3cSJames Courtier-Dutton 	u32 tmp;
275ed144f3cSJames Courtier-Dutton 
276ed144f3cSJames Courtier-Dutton 	val = ucontrol->value.enumerated.item[0] ;
2775fe619f9STakashi Iwai 	if (val > 1)
2785fe619f9STakashi Iwai 		return -EINVAL;
279ed144f3cSJames Courtier-Dutton 	change = (emu->capture_mic_line_in != val);
280ed144f3cSJames Courtier-Dutton 	if (change) {
281ed144f3cSJames Courtier-Dutton 		emu->capture_mic_line_in = val;
282ed144f3cSJames Courtier-Dutton 		if (val) {
2836129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
284ed144f3cSJames Courtier-Dutton 			tmp = inl(emu->port+GPIO) & ~0x400;
285ed144f3cSJames Courtier-Dutton 			tmp = tmp | 0x400;
286ed144f3cSJames Courtier-Dutton 			outl(tmp, emu->port+GPIO);
2876129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
288ed144f3cSJames Courtier-Dutton 		} else {
2896129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
290ed144f3cSJames Courtier-Dutton 			tmp = inl(emu->port+GPIO) & ~0x400;
291ed144f3cSJames Courtier-Dutton 			outl(tmp, emu->port+GPIO);
2926129daaaSJames Courtier-Dutton 			//snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
293ed144f3cSJames Courtier-Dutton 		}
294ed144f3cSJames Courtier-Dutton 	}
295ed144f3cSJames Courtier-Dutton         return change;
296ed144f3cSJames Courtier-Dutton }
297ed144f3cSJames Courtier-Dutton 
298e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
299ed144f3cSJames Courtier-Dutton {
300ed144f3cSJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
3016129daaaSJames Courtier-Dutton 	.name =		"Shared Mic/Line in Capture Switch",
302ed144f3cSJames Courtier-Dutton 	.info =		snd_ca0106_capture_mic_line_in_info,
303ed144f3cSJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
304ed144f3cSJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
305ed144f3cSJames Courtier-Dutton };
306ed144f3cSJames Courtier-Dutton 
307be0b7b01SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
308be0b7b01SJames Courtier-Dutton {
309be0b7b01SJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
310be0b7b01SJames Courtier-Dutton 	.name =		"Shared Line in/Side out Capture Switch",
311be0b7b01SJames Courtier-Dutton 	.info =		snd_ca0106_capture_line_in_side_out_info,
312be0b7b01SJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
313be0b7b01SJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
314be0b7b01SJames Courtier-Dutton };
315be0b7b01SJames Courtier-Dutton 
316be0b7b01SJames Courtier-Dutton 
317e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
318e4a3d145STakashi Iwai 				 struct snd_ctl_elem_info *uinfo)
3191da177e4SLinus Torvalds {
3201da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
3211da177e4SLinus Torvalds 	uinfo->count = 1;
3221da177e4SLinus Torvalds 	return 0;
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds 
325e4a3d145STakashi Iwai static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
326e4a3d145STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3271da177e4SLinus Torvalds {
328e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3291da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
3321da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
3331da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
3341da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
3351da177e4SLinus Torvalds         return 0;
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds 
338e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
339e4a3d145STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3401da177e4SLinus Torvalds {
3411da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = 0xff;
3421da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = 0xff;
3431da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = 0xff;
3441da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = 0xff;
3451da177e4SLinus Torvalds         return 0;
3461da177e4SLinus Torvalds }
3471da177e4SLinus Torvalds 
348e4a3d145STakashi Iwai static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
349e4a3d145STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3501da177e4SLinus Torvalds {
351e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3521da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3531da177e4SLinus Torvalds 	int change;
3541da177e4SLinus Torvalds 	unsigned int val;
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds 	val = (ucontrol->value.iec958.status[0] << 0) |
3571da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[1] << 8) |
3581da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[2] << 16) |
3591da177e4SLinus Torvalds 	      (ucontrol->value.iec958.status[3] << 24);
3601da177e4SLinus Torvalds 	change = val != emu->spdif_bits[idx];
3611da177e4SLinus Torvalds 	if (change) {
3621da177e4SLinus Torvalds 		snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
3631da177e4SLinus Torvalds 		emu->spdif_bits[idx] = val;
3641da177e4SLinus Torvalds 	}
3651da177e4SLinus Torvalds         return change;
3661da177e4SLinus Torvalds }
3671da177e4SLinus Torvalds 
368e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
369e4a3d145STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
3701da177e4SLinus Torvalds {
3711da177e4SLinus Torvalds         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3721da177e4SLinus Torvalds         uinfo->count = 2;
3731da177e4SLinus Torvalds         uinfo->value.integer.min = 0;
3741da177e4SLinus Torvalds         uinfo->value.integer.max = 255;
3751da177e4SLinus Torvalds         return 0;
3761da177e4SLinus Torvalds }
3771da177e4SLinus Torvalds 
378e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
379e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
3801da177e4SLinus Torvalds {
381e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3821da177e4SLinus Torvalds         unsigned int value;
38395a98265STakashi Iwai 	int channel_id, reg;
38495a98265STakashi Iwai 
38595a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
38695a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds         value = snd_ca0106_ptr_read(emu, reg, channel_id);
3891da177e4SLinus Torvalds         ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
3901da177e4SLinus Torvalds         ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
3911da177e4SLinus Torvalds         return 0;
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds 
394e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
395e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
3961da177e4SLinus Torvalds {
397e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
39895a98265STakashi Iwai         unsigned int oval, nval;
39995a98265STakashi Iwai 	int channel_id, reg;
40095a98265STakashi Iwai 
40195a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
40295a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
40395a98265STakashi Iwai 
40495a98265STakashi Iwai 	oval = snd_ca0106_ptr_read(emu, reg, channel_id);
40595a98265STakashi Iwai 	nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
40695a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) << 16);
40795a98265STakashi Iwai         nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
40895a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) );
40995a98265STakashi Iwai 	if (oval == nval)
41095a98265STakashi Iwai 		return 0;
41195a98265STakashi Iwai 	snd_ca0106_ptr_write(emu, reg, channel_id, nval);
4121da177e4SLinus Torvalds 	return 1;
4131da177e4SLinus Torvalds }
41495a98265STakashi Iwai 
4156129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
4166129daaaSJames Courtier-Dutton 				  struct snd_ctl_elem_info *uinfo)
4176129daaaSJames Courtier-Dutton {
4186129daaaSJames Courtier-Dutton         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4196129daaaSJames Courtier-Dutton         uinfo->count = 2;
4206129daaaSJames Courtier-Dutton         uinfo->value.integer.min = 0;
4216129daaaSJames Courtier-Dutton         uinfo->value.integer.max = 255;
4226129daaaSJames Courtier-Dutton         return 0;
4236129daaaSJames Courtier-Dutton }
4246129daaaSJames Courtier-Dutton 
4256129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
4266129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4276129daaaSJames Courtier-Dutton {
4286129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4296129daaaSJames Courtier-Dutton 	int source_id;
4306129daaaSJames Courtier-Dutton 
4316129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
4326129daaaSJames Courtier-Dutton 
4336129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
4346129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
4356129daaaSJames Courtier-Dutton         return 0;
4366129daaaSJames Courtier-Dutton }
4376129daaaSJames Courtier-Dutton 
4386129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
4396129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4406129daaaSJames Courtier-Dutton {
4416129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4426129daaaSJames Courtier-Dutton         unsigned int ogain;
4436129daaaSJames Courtier-Dutton         unsigned int ngain;
4446129daaaSJames Courtier-Dutton 	int source_id;
4456129daaaSJames Courtier-Dutton 	int change = 0;
4466129daaaSJames Courtier-Dutton 
4476129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
4486129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
4496129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[0];
4506129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
4515fe619f9STakashi Iwai 		return -EINVAL;
4526129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
4536129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
4546129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
4556129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
4566129daaaSJames Courtier-Dutton 		change = 1;
4576129daaaSJames Courtier-Dutton 	}
4586129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
4596129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[1];
4606129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
4615fe619f9STakashi Iwai 		return -EINVAL;
4626129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
4636129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
4646129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
4656129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
4666129daaaSJames Courtier-Dutton 		change = 1;
4676129daaaSJames Courtier-Dutton 	}
4686129daaaSJames Courtier-Dutton 
4696129daaaSJames Courtier-Dutton 	return change;
4706129daaaSJames Courtier-Dutton }
4716129daaaSJames Courtier-Dutton 
472b18cd538STrent Piepho #define spi_mute_info	snd_ctl_boolean_mono_info
473b18cd538STrent Piepho 
474b18cd538STrent Piepho static int spi_mute_get(struct snd_kcontrol *kcontrol,
475b18cd538STrent Piepho 			struct snd_ctl_elem_value *ucontrol)
476b18cd538STrent Piepho {
477b18cd538STrent Piepho 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
478b18cd538STrent Piepho 	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
479b18cd538STrent Piepho 	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
480b18cd538STrent Piepho 
481b18cd538STrent Piepho 	ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit);
482b18cd538STrent Piepho 	return 0;
483b18cd538STrent Piepho }
484b18cd538STrent Piepho 
485b18cd538STrent Piepho static int spi_mute_put(struct snd_kcontrol *kcontrol,
486b18cd538STrent Piepho 			struct snd_ctl_elem_value *ucontrol)
487b18cd538STrent Piepho {
488b18cd538STrent Piepho 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
489b18cd538STrent Piepho 	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
490b18cd538STrent Piepho 	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
491b18cd538STrent Piepho 	int ret;
492b18cd538STrent Piepho 
493b18cd538STrent Piepho 	ret = emu->spi_dac_reg[reg] & bit;
494b18cd538STrent Piepho 	if (ucontrol->value.integer.value[0]) {
495b18cd538STrent Piepho 		if (!ret)	/* bit already cleared, do nothing */
496b18cd538STrent Piepho 			return 0;
497b18cd538STrent Piepho 		emu->spi_dac_reg[reg] &= ~bit;
498b18cd538STrent Piepho 	} else {
499b18cd538STrent Piepho 		if (ret)	/* bit already set, do nothing */
500b18cd538STrent Piepho 			return 0;
501b18cd538STrent Piepho 		emu->spi_dac_reg[reg] |= bit;
502b18cd538STrent Piepho 	}
503b18cd538STrent Piepho 
504b18cd538STrent Piepho 	ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]);
5055fe619f9STakashi Iwai 	return ret ? -EINVAL : 1;
506b18cd538STrent Piepho }
507b18cd538STrent Piepho 
50895a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \
50995a98265STakashi Iwai {								\
51095a98265STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
511302e9c5aSJaroslav Kysela 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
512302e9c5aSJaroslav Kysela 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
51395a98265STakashi Iwai 	.info =	 snd_ca0106_volume_info,			\
51495a98265STakashi Iwai 	.get =   snd_ca0106_volume_get,				\
51595a98265STakashi Iwai 	.put =   snd_ca0106_volume_put,				\
5167cf0a953STakashi Iwai 	.tlv = { .p = snd_ca0106_db_scale1 },			\
51795a98265STakashi Iwai 	.private_value = ((chid) << 8) | (reg)			\
5181da177e4SLinus Torvalds }
5191da177e4SLinus Torvalds 
520e4a3d145STakashi Iwai static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
52195a98265STakashi Iwai 	CA_VOLUME("Analog Front Playback Volume",
52295a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
52395a98265STakashi Iwai         CA_VOLUME("Analog Rear Playback Volume",
52495a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
52595a98265STakashi Iwai 	CA_VOLUME("Analog Center/LFE Playback Volume",
52695a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
52795a98265STakashi Iwai         CA_VOLUME("Analog Side Playback Volume",
52895a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
52995a98265STakashi Iwai 
53039596dc8SJames Courtier-Dutton         CA_VOLUME("IEC958 Front Playback Volume",
53195a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
53239596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Rear Playback Volume",
53395a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
53439596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Center/LFE Playback Volume",
53595a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
53639596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Unknown Playback Volume",
53795a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
53895a98265STakashi Iwai 
53995a98265STakashi Iwai         CA_VOLUME("CAPTURE feedback Playback Volume",
54095a98265STakashi Iwai 		  1, CAPTURE_CONTROL),
54195a98265STakashi Iwai 
54295a98265STakashi Iwai 	{
54395a98265STakashi Iwai 		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
54495a98265STakashi Iwai 		.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
54595a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
54695a98265STakashi Iwai 		.count =	4,
54795a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
54895a98265STakashi Iwai 		.get =          snd_ca0106_spdif_get_mask
54995a98265STakashi Iwai 	},
5501da177e4SLinus Torvalds 	{
5511da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
55239596dc8SJames Courtier-Dutton 		.name =		"IEC958 Playback Switch",
55395a98265STakashi Iwai 		.info =		snd_ca0106_shared_spdif_info,
55495a98265STakashi Iwai 		.get =		snd_ca0106_shared_spdif_get,
55595a98265STakashi Iwai 		.put =		snd_ca0106_shared_spdif_put
55695a98265STakashi Iwai 	},
5571da177e4SLinus Torvalds 	{
5581da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
559e6327cf9SJames Courtier-Dutton 		.name =		"Digital Source Capture Enum",
56095a98265STakashi Iwai 		.info =		snd_ca0106_capture_source_info,
56195a98265STakashi Iwai 		.get =		snd_ca0106_capture_source_get,
56295a98265STakashi Iwai 		.put =		snd_ca0106_capture_source_put
56395a98265STakashi Iwai 	},
5641da177e4SLinus Torvalds 	{
5656129daaaSJames Courtier-Dutton 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
566e6327cf9SJames Courtier-Dutton 		.name =		"Analog Source Capture Enum",
5676129daaaSJames Courtier-Dutton 		.info =		snd_ca0106_i2c_capture_source_info,
5686129daaaSJames Courtier-Dutton 		.get =		snd_ca0106_i2c_capture_source_get,
5696129daaaSJames Courtier-Dutton 		.put =		snd_ca0106_i2c_capture_source_put
5706129daaaSJames Courtier-Dutton 	},
5716129daaaSJames Courtier-Dutton 	{
57295a98265STakashi Iwai 		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
57395a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
57495a98265STakashi Iwai 		.count =	4,
57595a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
57695a98265STakashi Iwai 		.get =          snd_ca0106_spdif_get,
57795a98265STakashi Iwai 		.put =          snd_ca0106_spdif_put
57895a98265STakashi Iwai 	},
5791da177e4SLinus Torvalds };
5801da177e4SLinus Torvalds 
5817c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \
5827c157069SJames Courtier-Dutton {								\
5837c157069SJames Courtier-Dutton 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
5847c157069SJames Courtier-Dutton 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
5857c157069SJames Courtier-Dutton 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
5867c157069SJames Courtier-Dutton 	.info =  snd_ca0106_i2c_volume_info,			\
5877c157069SJames Courtier-Dutton 	.get =   snd_ca0106_i2c_volume_get,			\
5887c157069SJames Courtier-Dutton 	.put =   snd_ca0106_i2c_volume_put,			\
5897c157069SJames Courtier-Dutton 	.tlv = { .p = snd_ca0106_db_scale2 },			\
5907c157069SJames Courtier-Dutton 	.private_value = chid					\
5917c157069SJames Courtier-Dutton }
5927c157069SJames Courtier-Dutton 
5937c157069SJames Courtier-Dutton static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
5947c157069SJames Courtier-Dutton         I2C_VOLUME("Phone Capture Volume", 0),
5957c157069SJames Courtier-Dutton         I2C_VOLUME("Mic Capture Volume", 1),
5967c157069SJames Courtier-Dutton         I2C_VOLUME("Line in Capture Volume", 2),
5977c157069SJames Courtier-Dutton         I2C_VOLUME("Aux Capture Volume", 3),
5987c157069SJames Courtier-Dutton };
5997c157069SJames Courtier-Dutton 
600b18cd538STrent Piepho #define SPI_SWITCH(xname,reg,bit) \
601b18cd538STrent Piepho {								\
602b18cd538STrent Piepho 	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
603b18cd538STrent Piepho 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
604b18cd538STrent Piepho 	.info	= spi_mute_info,				\
605b18cd538STrent Piepho 	.get	= spi_mute_get,					\
606b18cd538STrent Piepho 	.put	= spi_mute_put,					\
60718b5d32fSTrent Piepho 	.private_value = (reg<<SPI_REG_SHIFT) | (bit)		\
608b18cd538STrent Piepho }
609b18cd538STrent Piepho 
610b18cd538STrent Piepho static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[]
611b18cd538STrent Piepho __devinitdata = {
612b18cd538STrent Piepho 	SPI_SWITCH("Analog Front Playback Switch",
613b18cd538STrent Piepho 		   SPI_DMUTE4_REG, SPI_DMUTE4_BIT),
614b18cd538STrent Piepho 	SPI_SWITCH("Analog Rear Playback Switch",
615b18cd538STrent Piepho 		   SPI_DMUTE0_REG, SPI_DMUTE0_BIT),
616b18cd538STrent Piepho 	SPI_SWITCH("Analog Center/LFE Playback Switch",
617b18cd538STrent Piepho 		   SPI_DMUTE2_REG, SPI_DMUTE2_BIT),
618b18cd538STrent Piepho 	SPI_SWITCH("Analog Side Playback Switch",
619b18cd538STrent Piepho 		   SPI_DMUTE1_REG, SPI_DMUTE1_BIT),
620b18cd538STrent Piepho };
621b18cd538STrent Piepho 
622e4a3d145STakashi Iwai static int __devinit remove_ctl(struct snd_card *card, const char *name)
6231da177e4SLinus Torvalds {
624e4a3d145STakashi Iwai 	struct snd_ctl_elem_id id;
6251da177e4SLinus Torvalds 	memset(&id, 0, sizeof(id));
6261da177e4SLinus Torvalds 	strcpy(id.name, name);
6271da177e4SLinus Torvalds 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
6281da177e4SLinus Torvalds 	return snd_ctl_remove_id(card, &id);
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds 
631e4a3d145STakashi Iwai static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
6321da177e4SLinus Torvalds {
633e4a3d145STakashi Iwai 	struct snd_ctl_elem_id sid;
6341da177e4SLinus Torvalds 	memset(&sid, 0, sizeof(sid));
6351da177e4SLinus Torvalds 	/* FIXME: strcpy is bad. */
6361da177e4SLinus Torvalds 	strcpy(sid.name, name);
6371da177e4SLinus Torvalds 	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
6381da177e4SLinus Torvalds 	return snd_ctl_find_id(card, &sid);
6391da177e4SLinus Torvalds }
6401da177e4SLinus Torvalds 
641e4a3d145STakashi Iwai static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
6421da177e4SLinus Torvalds {
643e4a3d145STakashi Iwai 	struct snd_kcontrol *kctl = ctl_find(card, src);
6441da177e4SLinus Torvalds 	if (kctl) {
6451da177e4SLinus Torvalds 		strcpy(kctl->id.name, dst);
6461da177e4SLinus Torvalds 		return 0;
6471da177e4SLinus Torvalds 	}
6481da177e4SLinus Torvalds 	return -ENOENT;
6491da177e4SLinus Torvalds }
6501da177e4SLinus Torvalds 
651fca7f388STrent Piepho #define ADD_CTLS(emu, ctls)						\
652fca7f388STrent Piepho 	do {								\
653fca7f388STrent Piepho 		int i, err;						\
654fca7f388STrent Piepho 		for (i = 0; i < ARRAY_SIZE(ctls); i++) {		\
655fca7f388STrent Piepho 			err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
656fca7f388STrent Piepho 			if (err < 0)					\
657fca7f388STrent Piepho 				return err;				\
658fca7f388STrent Piepho 		}							\
659fca7f388STrent Piepho 	} while (0)
660fca7f388STrent Piepho 
661*49c88b85STakashi Iwai static __devinitdata
662*49c88b85STakashi Iwai DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1);
663*49c88b85STakashi Iwai 
664*49c88b85STakashi Iwai static char *slave_vols[] __devinitdata = {
665*49c88b85STakashi Iwai 	"Analog Front Playback Volume",
666*49c88b85STakashi Iwai         "Analog Rear Playback Volume",
667*49c88b85STakashi Iwai 	"Analog Center/LFE Playback Volume",
668*49c88b85STakashi Iwai         "Analog Side Playback Volume",
669*49c88b85STakashi Iwai         "IEC958 Front Playback Volume",
670*49c88b85STakashi Iwai 	"IEC958 Rear Playback Volume",
671*49c88b85STakashi Iwai 	"IEC958 Center/LFE Playback Volume",
672*49c88b85STakashi Iwai 	"IEC958 Unknown Playback Volume",
673*49c88b85STakashi Iwai         "CAPTURE feedback Playback Volume",
674*49c88b85STakashi Iwai 	NULL
675*49c88b85STakashi Iwai };
676*49c88b85STakashi Iwai 
677*49c88b85STakashi Iwai static char *slave_sws[] __devinitdata = {
678*49c88b85STakashi Iwai 	"Analog Front Playback Switch",
679*49c88b85STakashi Iwai 	"Analog Rear Playback Switch",
680*49c88b85STakashi Iwai 	"Analog Center/LFE Playback Switch",
681*49c88b85STakashi Iwai 	"Analog Side Playback Switch",
682*49c88b85STakashi Iwai 	"IEC958 Playback Switch",
683*49c88b85STakashi Iwai 	NULL
684*49c88b85STakashi Iwai };
685*49c88b85STakashi Iwai 
686*49c88b85STakashi Iwai static void __devinit add_slaves(struct snd_card *card,
687*49c88b85STakashi Iwai 				 struct snd_kcontrol *master, char **list)
688*49c88b85STakashi Iwai {
689*49c88b85STakashi Iwai 	for (; *list; list++) {
690*49c88b85STakashi Iwai 		struct snd_kcontrol *slave = ctl_find(card, *list);
691*49c88b85STakashi Iwai 		if (slave)
692*49c88b85STakashi Iwai 			snd_ctl_add_slave(master, slave);
693*49c88b85STakashi Iwai 	}
694*49c88b85STakashi Iwai }
695*49c88b85STakashi Iwai 
696e4a3d145STakashi Iwai int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
6971da177e4SLinus Torvalds {
698fca7f388STrent Piepho 	int err;
699e4a3d145STakashi Iwai         struct snd_card *card = emu->card;
7001da177e4SLinus Torvalds 	char **c;
701*49c88b85STakashi Iwai 	struct snd_kcontrol *vmaster;
7021da177e4SLinus Torvalds 	static char *ca0106_remove_ctls[] = {
7031da177e4SLinus Torvalds 		"Master Mono Playback Switch",
7041da177e4SLinus Torvalds 		"Master Mono Playback Volume",
7051da177e4SLinus Torvalds 		"3D Control - Switch",
7061da177e4SLinus Torvalds 		"3D Control Sigmatel - Depth",
7071da177e4SLinus Torvalds 		"PCM Playback Switch",
7081da177e4SLinus Torvalds 		"PCM Playback Volume",
7091da177e4SLinus Torvalds 		"CD Playback Switch",
7101da177e4SLinus Torvalds 		"CD Playback Volume",
7111da177e4SLinus Torvalds 		"Phone Playback Switch",
7121da177e4SLinus Torvalds 		"Phone Playback Volume",
7131da177e4SLinus Torvalds 		"Video Playback Switch",
7141da177e4SLinus Torvalds 		"Video Playback Volume",
7151da177e4SLinus Torvalds 		"PC Speaker Playback Switch",
7161da177e4SLinus Torvalds 		"PC Speaker Playback Volume",
7171da177e4SLinus Torvalds 		"Mono Output Select",
7181da177e4SLinus Torvalds 		"Capture Source",
7191da177e4SLinus Torvalds 		"Capture Switch",
7201da177e4SLinus Torvalds 		"Capture Volume",
7211da177e4SLinus Torvalds 		"External Amplifier",
7221da177e4SLinus Torvalds 		"Sigmatel 4-Speaker Stereo Playback Switch",
7231da177e4SLinus Torvalds 		"Sigmatel Surround Phase Inversion Playback ",
7241da177e4SLinus Torvalds 		NULL
7251da177e4SLinus Torvalds 	};
7261da177e4SLinus Torvalds 	static char *ca0106_rename_ctls[] = {
7271da177e4SLinus Torvalds 		"Master Playback Switch", "Capture Switch",
7281da177e4SLinus Torvalds 		"Master Playback Volume", "Capture Volume",
7291da177e4SLinus Torvalds 		"Line Playback Switch", "AC97 Line Capture Switch",
7301da177e4SLinus Torvalds 		"Line Playback Volume", "AC97 Line Capture Volume",
7311da177e4SLinus Torvalds 		"Aux Playback Switch", "AC97 Aux Capture Switch",
7321da177e4SLinus Torvalds 		"Aux Playback Volume", "AC97 Aux Capture Volume",
7331da177e4SLinus Torvalds 		"Mic Playback Switch", "AC97 Mic Capture Switch",
7341da177e4SLinus Torvalds 		"Mic Playback Volume", "AC97 Mic Capture Volume",
7351da177e4SLinus Torvalds 		"Mic Select", "AC97 Mic Select",
7361da177e4SLinus Torvalds 		"Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
7371da177e4SLinus Torvalds 		NULL
7381da177e4SLinus Torvalds 	};
7391da177e4SLinus Torvalds #if 1
7401da177e4SLinus Torvalds 	for (c = ca0106_remove_ctls; *c; c++)
7411da177e4SLinus Torvalds 		remove_ctl(card, *c);
7421da177e4SLinus Torvalds 	for (c = ca0106_rename_ctls; *c; c += 2)
7431da177e4SLinus Torvalds 		rename_ctl(card, c[0], c[1]);
7441da177e4SLinus Torvalds #endif
7451da177e4SLinus Torvalds 
746fca7f388STrent Piepho 	ADD_CTLS(emu, snd_ca0106_volume_ctls);
74795a98265STakashi Iwai 	if (emu->details->i2c_adc == 1) {
748fca7f388STrent Piepho 		ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
749be0b7b01SJames Courtier-Dutton 		if (emu->details->gpio_type == 1)
75095a98265STakashi Iwai 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
751be0b7b01SJames Courtier-Dutton 		else  /* gpio_type == 2 */
752be0b7b01SJames Courtier-Dutton 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
75395a98265STakashi Iwai 		if (err < 0)
7541da177e4SLinus Torvalds 			return err;
75595a98265STakashi Iwai 	}
756fca7f388STrent Piepho 	if (emu->details->spi_dac == 1)
757fca7f388STrent Piepho 		ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls);
758*49c88b85STakashi Iwai 
759*49c88b85STakashi Iwai 	/* Create virtual master controls */
760*49c88b85STakashi Iwai 	vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
761*49c88b85STakashi Iwai 					      snd_ca0106_master_db_scale);
762*49c88b85STakashi Iwai 	if (!vmaster)
763*49c88b85STakashi Iwai 		return -ENOMEM;
764*49c88b85STakashi Iwai 	add_slaves(card, vmaster, slave_vols);
765*49c88b85STakashi Iwai 
766*49c88b85STakashi Iwai 	if (emu->details->spi_dac == 1) {
767*49c88b85STakashi Iwai 		vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
768*49c88b85STakashi Iwai 						      NULL);
769*49c88b85STakashi Iwai 		if (!vmaster)
770*49c88b85STakashi Iwai 			return -ENOMEM;
771*49c88b85STakashi Iwai 		add_slaves(card, vmaster, slave_sws);
772*49c88b85STakashi Iwai 	}
7731da177e4SLinus Torvalds         return 0;
7741da177e4SLinus Torvalds }
7751da177e4SLinus Torvalds 
776