xref: /openbmc/linux/sound/pci/ca0106/ca0106_mixer.c (revision 9ab0cb309e7950a1649bffade985e7ccc7aaf675)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
41da177e4SLinus Torvalds  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
5b18cd538STrent Piepho  *  Version: 0.0.18
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  FEATURES currently supported:
81da177e4SLinus Torvalds  *    See ca0106_main.c for features.
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *  Changelog:
111da177e4SLinus Torvalds  *    Support interrupts per period.
121da177e4SLinus Torvalds  *    Removed noise from Center/LFE channel when in Analog mode.
131da177e4SLinus Torvalds  *    Rename and remove mixer controls.
141da177e4SLinus Torvalds  *  0.0.6
151da177e4SLinus Torvalds  *    Use separate card based DMA buffer for periods table list.
161da177e4SLinus Torvalds  *  0.0.7
171da177e4SLinus Torvalds  *    Change remove and rename ctrls into lists.
181da177e4SLinus Torvalds  *  0.0.8
191da177e4SLinus Torvalds  *    Try to fix capture sources.
201da177e4SLinus Torvalds  *  0.0.9
211da177e4SLinus Torvalds  *    Fix AC3 output.
221da177e4SLinus Torvalds  *    Enable S32_LE format support.
231da177e4SLinus Torvalds  *  0.0.10
241da177e4SLinus Torvalds  *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
251da177e4SLinus Torvalds  *  0.0.11
261da177e4SLinus Torvalds  *    Add Model name recognition.
271da177e4SLinus Torvalds  *  0.0.12
281da177e4SLinus Torvalds  *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
291da177e4SLinus Torvalds  *    Remove redundent "voice" handling.
301da177e4SLinus Torvalds  *  0.0.13
311da177e4SLinus Torvalds  *    Single trigger call for multi channels.
321da177e4SLinus Torvalds  *  0.0.14
331da177e4SLinus Torvalds  *    Set limits based on what the sound card hardware can do.
341da177e4SLinus Torvalds  *    playback periods_min=2, periods_max=8
351da177e4SLinus Torvalds  *    capture hw constraints require period_size = n * 64 bytes.
361da177e4SLinus Torvalds  *    playback hw constraints require period_size = n * 64 bytes.
371da177e4SLinus Torvalds  *  0.0.15
381da177e4SLinus Torvalds  *    Separated ca0106.c into separate functional .c files.
391da177e4SLinus Torvalds  *  0.0.16
401da177e4SLinus Torvalds  *    Modified Copyright message.
41ed144f3cSJames Courtier-Dutton  *  0.0.17
42ed144f3cSJames Courtier-Dutton  *    Implement Mic and Line in Capture.
43b18cd538STrent Piepho  *  0.0.18
44b18cd538STrent Piepho  *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
451da177e4SLinus Torvalds  *
4625985edcSLucas De Marchi  *  This code was initially based on code from ALSA's emu10k1x.c which is:
471da177e4SLinus Torvalds  *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
481da177e4SLinus Torvalds  */
491da177e4SLinus Torvalds #include <linux/delay.h>
501da177e4SLinus Torvalds #include <linux/init.h>
511da177e4SLinus Torvalds #include <linux/interrupt.h>
521da177e4SLinus Torvalds #include <linux/moduleparam.h>
531da177e4SLinus Torvalds #include <sound/core.h>
541da177e4SLinus Torvalds #include <sound/initval.h>
551da177e4SLinus Torvalds #include <sound/pcm.h>
561da177e4SLinus Torvalds #include <sound/ac97_codec.h>
571da177e4SLinus Torvalds #include <sound/info.h>
5842750b04SJaroslav Kysela #include <sound/tlv.h>
596cbbfe1cSTakashi Iwai #include <linux/io.h>
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #include "ca0106.h"
621da177e4SLinus Torvalds 
635da95273STakashi Iwai static void ca0106_spdif_enable(struct snd_ca0106 *emu)
645da95273STakashi Iwai {
655da95273STakashi Iwai 	unsigned int val;
665da95273STakashi Iwai 
675da95273STakashi Iwai 	if (emu->spdif_enable) {
685da95273STakashi Iwai 		/* Digital */
695da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
705da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
715da95273STakashi Iwai 		val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
725da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
735da95273STakashi Iwai 		val = inl(emu->port + GPIO) & ~0x101;
745da95273STakashi Iwai 		outl(val, emu->port + GPIO);
755da95273STakashi Iwai 
765da95273STakashi Iwai 	} else {
775da95273STakashi Iwai 		/* Analog */
785da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
795da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
805da95273STakashi Iwai 		val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
815da95273STakashi Iwai 		snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
825da95273STakashi Iwai 		val = inl(emu->port + GPIO) | 0x101;
835da95273STakashi Iwai 		outl(val, emu->port + GPIO);
845da95273STakashi Iwai 	}
855da95273STakashi Iwai }
865da95273STakashi Iwai 
875da95273STakashi Iwai static void ca0106_set_capture_source(struct snd_ca0106 *emu)
885da95273STakashi Iwai {
895da95273STakashi Iwai 	unsigned int val = emu->capture_source;
905da95273STakashi Iwai 	unsigned int source, mask;
915da95273STakashi Iwai 	source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
925da95273STakashi Iwai 	mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
935da95273STakashi Iwai 	snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
945da95273STakashi Iwai }
955da95273STakashi Iwai 
965da95273STakashi Iwai static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu,
975da95273STakashi Iwai 					  unsigned int val, int force)
985da95273STakashi Iwai {
995da95273STakashi Iwai 	unsigned int ngain, ogain;
1005da95273STakashi Iwai 	u32 source;
1015da95273STakashi Iwai 
1025da95273STakashi Iwai 	snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
1035da95273STakashi Iwai 	ngain = emu->i2c_capture_volume[val][0]; /* Left */
1045da95273STakashi Iwai 	ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
1055da95273STakashi Iwai 	if (force || ngain != ogain)
1065da95273STakashi Iwai 		snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff);
1075da95273STakashi Iwai 	ngain = emu->i2c_capture_volume[val][1]; /* Right */
1085da95273STakashi Iwai 	ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
1095da95273STakashi Iwai 	if (force || ngain != ogain)
1105da95273STakashi Iwai 		snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff);
1115da95273STakashi Iwai 	source = 1 << val;
1125da95273STakashi Iwai 	snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
1135da95273STakashi Iwai 	emu->i2c_capture_source = val;
1145da95273STakashi Iwai }
1155da95273STakashi Iwai 
1165da95273STakashi Iwai static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
1175da95273STakashi Iwai {
1185da95273STakashi Iwai 	u32 tmp;
1195da95273STakashi Iwai 
1205da95273STakashi Iwai 	if (emu->capture_mic_line_in) {
1215da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
1225da95273STakashi Iwai 		tmp = inl(emu->port+GPIO) & ~0x400;
1235da95273STakashi Iwai 		tmp = tmp | 0x400;
1245da95273STakashi Iwai 		outl(tmp, emu->port+GPIO);
1255da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
1265da95273STakashi Iwai 	} else {
1275da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
1285da95273STakashi Iwai 		tmp = inl(emu->port+GPIO) & ~0x400;
1295da95273STakashi Iwai 		outl(tmp, emu->port+GPIO);
1305da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
1315da95273STakashi Iwai 	}
1325da95273STakashi Iwai }
1335da95273STakashi Iwai 
1345da95273STakashi Iwai static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx)
1355da95273STakashi Iwai {
1363d475829STakashi Iwai 	snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]);
1375da95273STakashi Iwai }
1385da95273STakashi Iwai 
1395da95273STakashi Iwai /*
1405da95273STakashi Iwai  */
1410cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
1420cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
14342750b04SJaroslav Kysela 
144a5ce8890STakashi Iwai #define snd_ca0106_shared_spdif_info	snd_ctl_boolean_mono_info
1451da177e4SLinus Torvalds 
146e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
147e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1481da177e4SLinus Torvalds {
149e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1501da177e4SLinus Torvalds 
1515fe619f9STakashi Iwai 	ucontrol->value.integer.value[0] = emu->spdif_enable;
1521da177e4SLinus Torvalds 	return 0;
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds 
155e4a3d145STakashi Iwai static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
156e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1571da177e4SLinus Torvalds {
158e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1591da177e4SLinus Torvalds 	unsigned int val;
1601da177e4SLinus Torvalds 	int change = 0;
1611da177e4SLinus Torvalds 
1625fe619f9STakashi Iwai 	val = !!ucontrol->value.integer.value[0];
1631da177e4SLinus Torvalds 	change = (emu->spdif_enable != val);
1641da177e4SLinus Torvalds 	if (change) {
1651da177e4SLinus Torvalds 		emu->spdif_enable = val;
1665da95273STakashi Iwai 		ca0106_spdif_enable(emu);
1671da177e4SLinus Torvalds 	}
1681da177e4SLinus Torvalds         return change;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
171e4a3d145STakashi Iwai static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
172e4a3d145STakashi Iwai 					  struct snd_ctl_elem_info *uinfo)
1731da177e4SLinus Torvalds {
174de95eae2STakashi Iwai 	static const char * const texts[6] = {
17539596dc8SJames Courtier-Dutton 		"IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
17695a98265STakashi Iwai 	};
1771da177e4SLinus Torvalds 
178de95eae2STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 6, texts);
1791da177e4SLinus Torvalds }
1801da177e4SLinus Torvalds 
181e4a3d145STakashi Iwai static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
182e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1831da177e4SLinus Torvalds {
184e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	ucontrol->value.enumerated.item[0] = emu->capture_source;
1871da177e4SLinus Torvalds 	return 0;
1881da177e4SLinus Torvalds }
1891da177e4SLinus Torvalds 
190e4a3d145STakashi Iwai static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
191e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
1921da177e4SLinus Torvalds {
193e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
1941da177e4SLinus Torvalds 	unsigned int val;
1951da177e4SLinus Torvalds 	int change = 0;
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	val = ucontrol->value.enumerated.item[0] ;
1985fe619f9STakashi Iwai 	if (val >= 6)
1995fe619f9STakashi Iwai 		return -EINVAL;
2001da177e4SLinus Torvalds 	change = (emu->capture_source != val);
2011da177e4SLinus Torvalds 	if (change) {
2021da177e4SLinus Torvalds 		emu->capture_source = val;
2035da95273STakashi Iwai 		ca0106_set_capture_source(emu);
2041da177e4SLinus Torvalds 	}
2051da177e4SLinus Torvalds         return change;
2061da177e4SLinus Torvalds }
2071da177e4SLinus Torvalds 
2086129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
2096129daaaSJames Courtier-Dutton 					  struct snd_ctl_elem_info *uinfo)
2106129daaaSJames Courtier-Dutton {
211de95eae2STakashi Iwai 	static const char * const texts[4] = {
2126129daaaSJames Courtier-Dutton 		"Phone", "Mic", "Line in", "Aux"
2136129daaaSJames Courtier-Dutton 	};
2146129daaaSJames Courtier-Dutton 
215de95eae2STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 4, texts);
2166129daaaSJames Courtier-Dutton }
2176129daaaSJames Courtier-Dutton 
2186129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
2196129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
2206129daaaSJames Courtier-Dutton {
2216129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2226129daaaSJames Courtier-Dutton 
2236129daaaSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
2246129daaaSJames Courtier-Dutton 	return 0;
2256129daaaSJames Courtier-Dutton }
2266129daaaSJames Courtier-Dutton 
2276129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
2286129daaaSJames Courtier-Dutton 					struct snd_ctl_elem_value *ucontrol)
2296129daaaSJames Courtier-Dutton {
2306129daaaSJames Courtier-Dutton 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
2316129daaaSJames Courtier-Dutton 	unsigned int source_id;
2326129daaaSJames Courtier-Dutton 	int change = 0;
2336129daaaSJames Courtier-Dutton 	/* If the capture source has changed,
2346129daaaSJames Courtier-Dutton 	 * update the capture volume from the cached value
2356129daaaSJames Courtier-Dutton 	 * for the particular source.
2366129daaaSJames Courtier-Dutton 	 */
2376129daaaSJames Courtier-Dutton 	source_id = ucontrol->value.enumerated.item[0] ;
2385fe619f9STakashi Iwai 	if (source_id >= 4)
2395fe619f9STakashi Iwai 		return -EINVAL;
2406129daaaSJames Courtier-Dutton 	change = (emu->i2c_capture_source != source_id);
2416129daaaSJames Courtier-Dutton 	if (change) {
2425da95273STakashi Iwai 		ca0106_set_i2c_capture_source(emu, source_id, 0);
2436129daaaSJames Courtier-Dutton 	}
2446129daaaSJames Courtier-Dutton         return change;
2456129daaaSJames Courtier-Dutton }
2466129daaaSJames Courtier-Dutton 
247be0b7b01SJames Courtier-Dutton static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
248be0b7b01SJames Courtier-Dutton 					       struct snd_ctl_elem_info *uinfo)
249be0b7b01SJames Courtier-Dutton {
250de95eae2STakashi Iwai 	static const char * const texts[2] = { "Side out", "Line in" };
251be0b7b01SJames Courtier-Dutton 
252de95eae2STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 2, texts);
253be0b7b01SJames Courtier-Dutton }
254be0b7b01SJames Courtier-Dutton 
255e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
256e4a3d145STakashi Iwai 					       struct snd_ctl_elem_info *uinfo)
257ed144f3cSJames Courtier-Dutton {
258de95eae2STakashi Iwai 	static const char * const texts[2] = { "Line in", "Mic in" };
259ed144f3cSJames Courtier-Dutton 
260de95eae2STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 2, texts);
261ed144f3cSJames Courtier-Dutton }
262ed144f3cSJames Courtier-Dutton 
263e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
264e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
265ed144f3cSJames Courtier-Dutton {
266e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
267ed144f3cSJames Courtier-Dutton 
268ed144f3cSJames Courtier-Dutton 	ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
269ed144f3cSJames Courtier-Dutton 	return 0;
270ed144f3cSJames Courtier-Dutton }
271ed144f3cSJames Courtier-Dutton 
272e4a3d145STakashi Iwai static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
273e4a3d145STakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
274ed144f3cSJames Courtier-Dutton {
275e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
276ed144f3cSJames Courtier-Dutton 	unsigned int val;
277ed144f3cSJames Courtier-Dutton 	int change = 0;
278ed144f3cSJames Courtier-Dutton 
279ed144f3cSJames Courtier-Dutton 	val = ucontrol->value.enumerated.item[0] ;
2805fe619f9STakashi Iwai 	if (val > 1)
2815fe619f9STakashi Iwai 		return -EINVAL;
282ed144f3cSJames Courtier-Dutton 	change = (emu->capture_mic_line_in != val);
283ed144f3cSJames Courtier-Dutton 	if (change) {
284ed144f3cSJames Courtier-Dutton 		emu->capture_mic_line_in = val;
2855da95273STakashi Iwai 		ca0106_set_capture_mic_line_in(emu);
286ed144f3cSJames Courtier-Dutton 	}
287ed144f3cSJames Courtier-Dutton         return change;
288ed144f3cSJames Courtier-Dutton }
289ed144f3cSJames Courtier-Dutton 
290f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_ca0106_capture_mic_line_in =
291ed144f3cSJames Courtier-Dutton {
292ed144f3cSJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
2936129daaaSJames Courtier-Dutton 	.name =		"Shared Mic/Line in Capture Switch",
294ed144f3cSJames Courtier-Dutton 	.info =		snd_ca0106_capture_mic_line_in_info,
295ed144f3cSJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
296ed144f3cSJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
297ed144f3cSJames Courtier-Dutton };
298ed144f3cSJames Courtier-Dutton 
299f3b827e0SBhumika Goyal static const struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out =
300be0b7b01SJames Courtier-Dutton {
301be0b7b01SJames Courtier-Dutton 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
302be0b7b01SJames Courtier-Dutton 	.name =		"Shared Line in/Side out Capture Switch",
303be0b7b01SJames Courtier-Dutton 	.info =		snd_ca0106_capture_line_in_side_out_info,
304be0b7b01SJames Courtier-Dutton 	.get =		snd_ca0106_capture_mic_line_in_get,
305be0b7b01SJames Courtier-Dutton 	.put =		snd_ca0106_capture_mic_line_in_put
306be0b7b01SJames Courtier-Dutton };
307be0b7b01SJames Courtier-Dutton 
308be0b7b01SJames Courtier-Dutton 
309e4a3d145STakashi Iwai static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
310e4a3d145STakashi Iwai 				 struct snd_ctl_elem_info *uinfo)
3111da177e4SLinus Torvalds {
3121da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
3131da177e4SLinus Torvalds 	uinfo->count = 1;
3141da177e4SLinus Torvalds 	return 0;
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds 
3173d475829STakashi Iwai static void decode_spdif_bits(unsigned char *status, unsigned int bits)
3183d475829STakashi Iwai {
3193d475829STakashi Iwai 	status[0] = (bits >> 0) & 0xff;
3203d475829STakashi Iwai 	status[1] = (bits >> 8) & 0xff;
3213d475829STakashi Iwai 	status[2] = (bits >> 16) & 0xff;
3223d475829STakashi Iwai 	status[3] = (bits >> 24) & 0xff;
3233d475829STakashi Iwai }
3243d475829STakashi Iwai 
3253d475829STakashi Iwai static int snd_ca0106_spdif_get_default(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 
3313d475829STakashi Iwai 	decode_spdif_bits(ucontrol->value.iec958.status,
3323d475829STakashi Iwai 			  emu->spdif_bits[idx]);
3333d475829STakashi Iwai         return 0;
3343d475829STakashi Iwai }
3353d475829STakashi Iwai 
3363d475829STakashi Iwai static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol,
3373d475829STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3383d475829STakashi Iwai {
3393d475829STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3403d475829STakashi Iwai 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3413d475829STakashi Iwai 
3423d475829STakashi Iwai 	decode_spdif_bits(ucontrol->value.iec958.status,
3433d475829STakashi Iwai 			  emu->spdif_str_bits[idx]);
3441da177e4SLinus Torvalds         return 0;
3451da177e4SLinus Torvalds }
3461da177e4SLinus Torvalds 
347e4a3d145STakashi Iwai static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
348e4a3d145STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3491da177e4SLinus Torvalds {
3501da177e4SLinus Torvalds 	ucontrol->value.iec958.status[0] = 0xff;
3511da177e4SLinus Torvalds 	ucontrol->value.iec958.status[1] = 0xff;
3521da177e4SLinus Torvalds 	ucontrol->value.iec958.status[2] = 0xff;
3531da177e4SLinus Torvalds 	ucontrol->value.iec958.status[3] = 0xff;
3541da177e4SLinus Torvalds         return 0;
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds 
3573d475829STakashi Iwai static unsigned int encode_spdif_bits(unsigned char *status)
3583d475829STakashi Iwai {
3593d475829STakashi Iwai 	return ((unsigned int)status[0] << 0) |
3603d475829STakashi Iwai 		((unsigned int)status[1] << 8) |
3613d475829STakashi Iwai 		((unsigned int)status[2] << 16) |
3623d475829STakashi Iwai 		((unsigned int)status[3] << 24);
3633d475829STakashi Iwai }
3643d475829STakashi Iwai 
3653d475829STakashi Iwai static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol,
366e4a3d145STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3671da177e4SLinus Torvalds {
368e4a3d145STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3691da177e4SLinus Torvalds 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3701da177e4SLinus Torvalds 	unsigned int val;
3711da177e4SLinus Torvalds 
3723d475829STakashi Iwai 	val = encode_spdif_bits(ucontrol->value.iec958.status);
3733d475829STakashi Iwai 	if (val != emu->spdif_bits[idx]) {
3741da177e4SLinus Torvalds 		emu->spdif_bits[idx] = val;
3753d475829STakashi Iwai 		/* FIXME: this isn't safe, but needed to keep the compatibility
3763d475829STakashi Iwai 		 * with older alsa-lib config
3773d475829STakashi Iwai 		 */
3783d475829STakashi Iwai 		emu->spdif_str_bits[idx] = val;
3795da95273STakashi Iwai 		ca0106_set_spdif_bits(emu, idx);
3803d475829STakashi Iwai 		return 1;
3811da177e4SLinus Torvalds 	}
3823d475829STakashi Iwai 	return 0;
3833d475829STakashi Iwai }
3843d475829STakashi Iwai 
3853d475829STakashi Iwai static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol,
3863d475829STakashi Iwai                                  struct snd_ctl_elem_value *ucontrol)
3873d475829STakashi Iwai {
3883d475829STakashi Iwai 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
3893d475829STakashi Iwai 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
3903d475829STakashi Iwai 	unsigned int val;
3913d475829STakashi Iwai 
3923d475829STakashi Iwai 	val = encode_spdif_bits(ucontrol->value.iec958.status);
3933d475829STakashi Iwai 	if (val != emu->spdif_str_bits[idx]) {
3943d475829STakashi Iwai 		emu->spdif_str_bits[idx] = val;
3953d475829STakashi Iwai 		ca0106_set_spdif_bits(emu, idx);
3963d475829STakashi Iwai 		return 1;
3973d475829STakashi Iwai 	}
3983d475829STakashi Iwai         return 0;
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
401e4a3d145STakashi Iwai static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
402e4a3d145STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
4031da177e4SLinus Torvalds {
4041da177e4SLinus Torvalds         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4051da177e4SLinus Torvalds         uinfo->count = 2;
4061da177e4SLinus Torvalds         uinfo->value.integer.min = 0;
4071da177e4SLinus Torvalds         uinfo->value.integer.max = 255;
4081da177e4SLinus Torvalds         return 0;
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
411e4a3d145STakashi Iwai static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
412e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
4131da177e4SLinus Torvalds {
414e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4151da177e4SLinus Torvalds         unsigned int value;
41695a98265STakashi Iwai 	int channel_id, reg;
41795a98265STakashi Iwai 
41895a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
41995a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds         value = snd_ca0106_ptr_read(emu, reg, channel_id);
4221da177e4SLinus Torvalds         ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
4231da177e4SLinus Torvalds         ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
4241da177e4SLinus Torvalds         return 0;
4251da177e4SLinus Torvalds }
4261da177e4SLinus Torvalds 
427e4a3d145STakashi Iwai static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
428e4a3d145STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
4291da177e4SLinus Torvalds {
430e4a3d145STakashi Iwai         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
43195a98265STakashi Iwai         unsigned int oval, nval;
43295a98265STakashi Iwai 	int channel_id, reg;
43395a98265STakashi Iwai 
43495a98265STakashi Iwai 	channel_id = (kcontrol->private_value >> 8) & 0xff;
43595a98265STakashi Iwai 	reg = kcontrol->private_value & 0xff;
43695a98265STakashi Iwai 
43795a98265STakashi Iwai 	oval = snd_ca0106_ptr_read(emu, reg, channel_id);
43895a98265STakashi Iwai 	nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
43995a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) << 16);
44095a98265STakashi Iwai         nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
44195a98265STakashi Iwai 		((0xff - ucontrol->value.integer.value[1]) );
44295a98265STakashi Iwai 	if (oval == nval)
44395a98265STakashi Iwai 		return 0;
44495a98265STakashi Iwai 	snd_ca0106_ptr_write(emu, reg, channel_id, nval);
4451da177e4SLinus Torvalds 	return 1;
4461da177e4SLinus Torvalds }
44795a98265STakashi Iwai 
4486129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol,
4496129daaaSJames Courtier-Dutton 				  struct snd_ctl_elem_info *uinfo)
4506129daaaSJames Courtier-Dutton {
4516129daaaSJames Courtier-Dutton         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4526129daaaSJames Courtier-Dutton         uinfo->count = 2;
4536129daaaSJames Courtier-Dutton         uinfo->value.integer.min = 0;
4546129daaaSJames Courtier-Dutton         uinfo->value.integer.max = 255;
4556129daaaSJames Courtier-Dutton         return 0;
4566129daaaSJames Courtier-Dutton }
4576129daaaSJames Courtier-Dutton 
4586129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol,
4596129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4606129daaaSJames Courtier-Dutton {
4616129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4626129daaaSJames Courtier-Dutton 	int source_id;
4636129daaaSJames Courtier-Dutton 
4646129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
4656129daaaSJames Courtier-Dutton 
4666129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
4676129daaaSJames Courtier-Dutton         ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
4686129daaaSJames Courtier-Dutton         return 0;
4696129daaaSJames Courtier-Dutton }
4706129daaaSJames Courtier-Dutton 
4716129daaaSJames Courtier-Dutton static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
4726129daaaSJames Courtier-Dutton 				 struct snd_ctl_elem_value *ucontrol)
4736129daaaSJames Courtier-Dutton {
4746129daaaSJames Courtier-Dutton         struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
4756129daaaSJames Courtier-Dutton         unsigned int ogain;
4766129daaaSJames Courtier-Dutton         unsigned int ngain;
4776129daaaSJames Courtier-Dutton 	int source_id;
4786129daaaSJames Courtier-Dutton 	int change = 0;
4796129daaaSJames Courtier-Dutton 
4806129daaaSJames Courtier-Dutton 	source_id = kcontrol->private_value;
4816129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
4826129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[0];
4836129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
4845fe619f9STakashi Iwai 		return -EINVAL;
4856129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
4866129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
4876129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
4886129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0];
4896129daaaSJames Courtier-Dutton 		change = 1;
4906129daaaSJames Courtier-Dutton 	}
4916129daaaSJames Courtier-Dutton 	ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
4926129daaaSJames Courtier-Dutton 	ngain = ucontrol->value.integer.value[1];
4936129daaaSJames Courtier-Dutton 	if (ngain > 0xff)
4945fe619f9STakashi Iwai 		return -EINVAL;
4956129daaaSJames Courtier-Dutton 	if (ogain != ngain) {
4966129daaaSJames Courtier-Dutton 		if (emu->i2c_capture_source == source_id)
4976129daaaSJames Courtier-Dutton 			snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
4986129daaaSJames Courtier-Dutton 		emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1];
4996129daaaSJames Courtier-Dutton 		change = 1;
5006129daaaSJames Courtier-Dutton 	}
5016129daaaSJames Courtier-Dutton 
5026129daaaSJames Courtier-Dutton 	return change;
5036129daaaSJames Courtier-Dutton }
5046129daaaSJames Courtier-Dutton 
505b18cd538STrent Piepho #define spi_mute_info	snd_ctl_boolean_mono_info
506b18cd538STrent Piepho 
507b18cd538STrent Piepho static int spi_mute_get(struct snd_kcontrol *kcontrol,
508b18cd538STrent Piepho 			struct snd_ctl_elem_value *ucontrol)
509b18cd538STrent Piepho {
510b18cd538STrent Piepho 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
511b18cd538STrent Piepho 	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
512b18cd538STrent Piepho 	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
513b18cd538STrent Piepho 
514b18cd538STrent Piepho 	ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit);
515b18cd538STrent Piepho 	return 0;
516b18cd538STrent Piepho }
517b18cd538STrent Piepho 
518b18cd538STrent Piepho static int spi_mute_put(struct snd_kcontrol *kcontrol,
519b18cd538STrent Piepho 			struct snd_ctl_elem_value *ucontrol)
520b18cd538STrent Piepho {
521b18cd538STrent Piepho 	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
522b18cd538STrent Piepho 	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
523b18cd538STrent Piepho 	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
524b18cd538STrent Piepho 	int ret;
525b18cd538STrent Piepho 
526b18cd538STrent Piepho 	ret = emu->spi_dac_reg[reg] & bit;
527b18cd538STrent Piepho 	if (ucontrol->value.integer.value[0]) {
528b18cd538STrent Piepho 		if (!ret)	/* bit already cleared, do nothing */
529b18cd538STrent Piepho 			return 0;
530b18cd538STrent Piepho 		emu->spi_dac_reg[reg] &= ~bit;
531b18cd538STrent Piepho 	} else {
532b18cd538STrent Piepho 		if (ret)	/* bit already set, do nothing */
533b18cd538STrent Piepho 			return 0;
534b18cd538STrent Piepho 		emu->spi_dac_reg[reg] |= bit;
535b18cd538STrent Piepho 	}
536b18cd538STrent Piepho 
537b18cd538STrent Piepho 	ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]);
5385fe619f9STakashi Iwai 	return ret ? -EINVAL : 1;
539b18cd538STrent Piepho }
540b18cd538STrent Piepho 
54195a98265STakashi Iwai #define CA_VOLUME(xname,chid,reg) \
54295a98265STakashi Iwai {								\
54395a98265STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
544302e9c5aSJaroslav Kysela 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
545302e9c5aSJaroslav Kysela 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
54695a98265STakashi Iwai 	.info =	 snd_ca0106_volume_info,			\
54795a98265STakashi Iwai 	.get =   snd_ca0106_volume_get,				\
54895a98265STakashi Iwai 	.put =   snd_ca0106_volume_put,				\
5497cf0a953STakashi Iwai 	.tlv = { .p = snd_ca0106_db_scale1 },			\
55095a98265STakashi Iwai 	.private_value = ((chid) << 8) | (reg)			\
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds 
553b4e5e707STakashi Iwai static const struct snd_kcontrol_new snd_ca0106_volume_ctls[] = {
55495a98265STakashi Iwai 	CA_VOLUME("Analog Front Playback Volume",
55595a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
55695a98265STakashi Iwai         CA_VOLUME("Analog Rear Playback Volume",
55795a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
55895a98265STakashi Iwai 	CA_VOLUME("Analog Center/LFE Playback Volume",
55995a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
56095a98265STakashi Iwai         CA_VOLUME("Analog Side Playback Volume",
56195a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
56295a98265STakashi Iwai 
56339596dc8SJames Courtier-Dutton         CA_VOLUME("IEC958 Front Playback Volume",
56495a98265STakashi Iwai 		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
56539596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Rear Playback Volume",
56695a98265STakashi Iwai 		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
56739596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Center/LFE Playback Volume",
56895a98265STakashi Iwai 		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
56939596dc8SJames Courtier-Dutton 	CA_VOLUME("IEC958 Unknown Playback Volume",
57095a98265STakashi Iwai 		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
57195a98265STakashi Iwai 
57295a98265STakashi Iwai         CA_VOLUME("CAPTURE feedback Playback Volume",
57395a98265STakashi Iwai 		  1, CAPTURE_CONTROL),
57495a98265STakashi Iwai 
57595a98265STakashi Iwai 	{
57695a98265STakashi Iwai 		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
57795a98265STakashi Iwai 		.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
57895a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
57995a98265STakashi Iwai 		.count =	4,
58095a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
58195a98265STakashi Iwai 		.get =          snd_ca0106_spdif_get_mask
58295a98265STakashi Iwai 	},
5831da177e4SLinus Torvalds 	{
5841da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
58539596dc8SJames Courtier-Dutton 		.name =		"IEC958 Playback Switch",
58695a98265STakashi Iwai 		.info =		snd_ca0106_shared_spdif_info,
58795a98265STakashi Iwai 		.get =		snd_ca0106_shared_spdif_get,
58895a98265STakashi Iwai 		.put =		snd_ca0106_shared_spdif_put
58995a98265STakashi Iwai 	},
5901da177e4SLinus Torvalds 	{
5911da177e4SLinus Torvalds 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
592e6327cf9SJames Courtier-Dutton 		.name =		"Digital Source Capture Enum",
59395a98265STakashi Iwai 		.info =		snd_ca0106_capture_source_info,
59495a98265STakashi Iwai 		.get =		snd_ca0106_capture_source_get,
59595a98265STakashi Iwai 		.put =		snd_ca0106_capture_source_put
59695a98265STakashi Iwai 	},
5971da177e4SLinus Torvalds 	{
5986129daaaSJames Courtier-Dutton 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
599e6327cf9SJames Courtier-Dutton 		.name =		"Analog Source Capture Enum",
6006129daaaSJames Courtier-Dutton 		.info =		snd_ca0106_i2c_capture_source_info,
6016129daaaSJames Courtier-Dutton 		.get =		snd_ca0106_i2c_capture_source_get,
6026129daaaSJames Courtier-Dutton 		.put =		snd_ca0106_i2c_capture_source_put
6036129daaaSJames Courtier-Dutton 	},
6046129daaaSJames Courtier-Dutton 	{
60595a98265STakashi Iwai 		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
60695a98265STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
60795a98265STakashi Iwai 		.count =	4,
60895a98265STakashi Iwai 		.info =         snd_ca0106_spdif_info,
6093d475829STakashi Iwai 		.get =          snd_ca0106_spdif_get_default,
6103d475829STakashi Iwai 		.put =          snd_ca0106_spdif_put_default
6113d475829STakashi Iwai 	},
6123d475829STakashi Iwai 	{
6133d475829STakashi Iwai 		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
6143d475829STakashi Iwai 		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
6153d475829STakashi Iwai 		.count =	4,
6163d475829STakashi Iwai 		.info =         snd_ca0106_spdif_info,
6173d475829STakashi Iwai 		.get =          snd_ca0106_spdif_get_stream,
6183d475829STakashi Iwai 		.put =          snd_ca0106_spdif_put_stream
61995a98265STakashi Iwai 	},
6201da177e4SLinus Torvalds };
6211da177e4SLinus Torvalds 
6227c157069SJames Courtier-Dutton #define I2C_VOLUME(xname,chid) \
6237c157069SJames Courtier-Dutton {								\
6247c157069SJames Courtier-Dutton 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
6257c157069SJames Courtier-Dutton 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
6267c157069SJames Courtier-Dutton 	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
6277c157069SJames Courtier-Dutton 	.info =  snd_ca0106_i2c_volume_info,			\
6287c157069SJames Courtier-Dutton 	.get =   snd_ca0106_i2c_volume_get,			\
6297c157069SJames Courtier-Dutton 	.put =   snd_ca0106_i2c_volume_put,			\
6307c157069SJames Courtier-Dutton 	.tlv = { .p = snd_ca0106_db_scale2 },			\
6317c157069SJames Courtier-Dutton 	.private_value = chid					\
6327c157069SJames Courtier-Dutton }
6337c157069SJames Courtier-Dutton 
634b4e5e707STakashi Iwai static const struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] = {
6357c157069SJames Courtier-Dutton         I2C_VOLUME("Phone Capture Volume", 0),
6367c157069SJames Courtier-Dutton         I2C_VOLUME("Mic Capture Volume", 1),
6377c157069SJames Courtier-Dutton         I2C_VOLUME("Line in Capture Volume", 2),
6387c157069SJames Courtier-Dutton         I2C_VOLUME("Aux Capture Volume", 3),
6397c157069SJames Courtier-Dutton };
6407c157069SJames Courtier-Dutton 
64164e5310aSAndy Owen static const int spi_dmute_reg[] = {
64264e5310aSAndy Owen 	SPI_DMUTE0_REG,
64364e5310aSAndy Owen 	SPI_DMUTE1_REG,
64464e5310aSAndy Owen 	SPI_DMUTE2_REG,
64564e5310aSAndy Owen 	0,
64664e5310aSAndy Owen 	SPI_DMUTE4_REG,
647b18cd538STrent Piepho };
64864e5310aSAndy Owen static const int spi_dmute_bit[] = {
64964e5310aSAndy Owen 	SPI_DMUTE0_BIT,
65064e5310aSAndy Owen 	SPI_DMUTE1_BIT,
65164e5310aSAndy Owen 	SPI_DMUTE2_BIT,
65264e5310aSAndy Owen 	0,
65364e5310aSAndy Owen 	SPI_DMUTE4_BIT,
65464e5310aSAndy Owen };
65564e5310aSAndy Owen 
656e23e7a14SBill Pemberton static struct snd_kcontrol_new
657dc6ffaf8STakashi Iwai snd_ca0106_volume_spi_dac_ctl(const struct snd_ca0106_details *details,
65864e5310aSAndy Owen 			      int channel_id)
65964e5310aSAndy Owen {
66064e5310aSAndy Owen 	struct snd_kcontrol_new spi_switch = {0};
66164e5310aSAndy Owen 	int reg, bit;
66264e5310aSAndy Owen 	int dac_id;
66364e5310aSAndy Owen 
66464e5310aSAndy Owen 	spi_switch.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
66564e5310aSAndy Owen 	spi_switch.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
66664e5310aSAndy Owen 	spi_switch.info = spi_mute_info;
66764e5310aSAndy Owen 	spi_switch.get = spi_mute_get;
66864e5310aSAndy Owen 	spi_switch.put = spi_mute_put;
66964e5310aSAndy Owen 
67064e5310aSAndy Owen 	switch (channel_id) {
67164e5310aSAndy Owen 	case PCM_FRONT_CHANNEL:
67264e5310aSAndy Owen 		spi_switch.name = "Analog Front Playback Switch";
67364e5310aSAndy Owen 		dac_id = (details->spi_dac & 0xf000) >> (4 * 3);
67464e5310aSAndy Owen 		break;
67564e5310aSAndy Owen 	case PCM_REAR_CHANNEL:
67664e5310aSAndy Owen 		spi_switch.name = "Analog Rear Playback Switch";
67764e5310aSAndy Owen 		dac_id = (details->spi_dac & 0x0f00) >> (4 * 2);
67864e5310aSAndy Owen 		break;
67964e5310aSAndy Owen 	case PCM_CENTER_LFE_CHANNEL:
68064e5310aSAndy Owen 		spi_switch.name = "Analog Center/LFE Playback Switch";
68164e5310aSAndy Owen 		dac_id = (details->spi_dac & 0x00f0) >> (4 * 1);
68264e5310aSAndy Owen 		break;
68364e5310aSAndy Owen 	case PCM_UNKNOWN_CHANNEL:
68464e5310aSAndy Owen 		spi_switch.name = "Analog Side Playback Switch";
68564e5310aSAndy Owen 		dac_id = (details->spi_dac & 0x000f) >> (4 * 0);
68664e5310aSAndy Owen 		break;
68764e5310aSAndy Owen 	default:
68864e5310aSAndy Owen 		/* Unused channel */
68964e5310aSAndy Owen 		spi_switch.name = NULL;
69064e5310aSAndy Owen 		dac_id = 0;
69164e5310aSAndy Owen 	}
69264e5310aSAndy Owen 	reg = spi_dmute_reg[dac_id];
69364e5310aSAndy Owen 	bit = spi_dmute_bit[dac_id];
69464e5310aSAndy Owen 
69564e5310aSAndy Owen 	spi_switch.private_value = (reg << SPI_REG_SHIFT) | bit;
69664e5310aSAndy Owen 
69764e5310aSAndy Owen 	return spi_switch;
69864e5310aSAndy Owen }
699b18cd538STrent Piepho 
700e23e7a14SBill Pemberton static int remove_ctl(struct snd_card *card, const char *name)
7011da177e4SLinus Torvalds {
702e4a3d145STakashi Iwai 	struct snd_ctl_elem_id id;
7031da177e4SLinus Torvalds 	memset(&id, 0, sizeof(id));
7041da177e4SLinus Torvalds 	strcpy(id.name, name);
7051da177e4SLinus Torvalds 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
7061da177e4SLinus Torvalds 	return snd_ctl_remove_id(card, &id);
7071da177e4SLinus Torvalds }
7081da177e4SLinus Torvalds 
709e23e7a14SBill Pemberton static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
7101da177e4SLinus Torvalds {
711e4a3d145STakashi Iwai 	struct snd_ctl_elem_id sid;
7121da177e4SLinus Torvalds 	memset(&sid, 0, sizeof(sid));
7131da177e4SLinus Torvalds 	/* FIXME: strcpy is bad. */
7141da177e4SLinus Torvalds 	strcpy(sid.name, name);
7151da177e4SLinus Torvalds 	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
7161da177e4SLinus Torvalds 	return snd_ctl_find_id(card, &sid);
7171da177e4SLinus Torvalds }
7181da177e4SLinus Torvalds 
719e23e7a14SBill Pemberton static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
7201da177e4SLinus Torvalds {
721e4a3d145STakashi Iwai 	struct snd_kcontrol *kctl = ctl_find(card, src);
7221da177e4SLinus Torvalds 	if (kctl) {
7231da177e4SLinus Torvalds 		strcpy(kctl->id.name, dst);
7241da177e4SLinus Torvalds 		return 0;
7251da177e4SLinus Torvalds 	}
7261da177e4SLinus Torvalds 	return -ENOENT;
7271da177e4SLinus Torvalds }
7281da177e4SLinus Torvalds 
729fca7f388STrent Piepho #define ADD_CTLS(emu, ctls)						\
730fca7f388STrent Piepho 	do {								\
731bed515b0SHarvey Harrison 		int i, _err;						\
732fca7f388STrent Piepho 		for (i = 0; i < ARRAY_SIZE(ctls); i++) {		\
733bed515b0SHarvey Harrison 			_err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
734bed515b0SHarvey Harrison 			if (_err < 0)					\
735bed515b0SHarvey Harrison 				return _err;				\
736fca7f388STrent Piepho 		}							\
737fca7f388STrent Piepho 	} while (0)
738fca7f388STrent Piepho 
739e23e7a14SBill Pemberton static
740c4865679STakashi Iwai DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
74149c88b85STakashi Iwai 
742*9ab0cb30STakashi Iwai static const char * const follower_vols[] = {
74349c88b85STakashi Iwai 	"Analog Front Playback Volume",
74449c88b85STakashi Iwai         "Analog Rear Playback Volume",
74549c88b85STakashi Iwai 	"Analog Center/LFE Playback Volume",
74649c88b85STakashi Iwai         "Analog Side Playback Volume",
74749c88b85STakashi Iwai         "IEC958 Front Playback Volume",
74849c88b85STakashi Iwai 	"IEC958 Rear Playback Volume",
74949c88b85STakashi Iwai 	"IEC958 Center/LFE Playback Volume",
75049c88b85STakashi Iwai 	"IEC958 Unknown Playback Volume",
75149c88b85STakashi Iwai         "CAPTURE feedback Playback Volume",
75249c88b85STakashi Iwai 	NULL
75349c88b85STakashi Iwai };
75449c88b85STakashi Iwai 
755*9ab0cb30STakashi Iwai static const char * const follower_sws[] = {
75649c88b85STakashi Iwai 	"Analog Front Playback Switch",
75749c88b85STakashi Iwai 	"Analog Rear Playback Switch",
75849c88b85STakashi Iwai 	"Analog Center/LFE Playback Switch",
75949c88b85STakashi Iwai 	"Analog Side Playback Switch",
76049c88b85STakashi Iwai 	"IEC958 Playback Switch",
76149c88b85STakashi Iwai 	NULL
76249c88b85STakashi Iwai };
76349c88b85STakashi Iwai 
764*9ab0cb30STakashi Iwai static void add_followers(struct snd_card *card,
76597974309STakashi Iwai 			  struct snd_kcontrol *master, const char * const *list)
76649c88b85STakashi Iwai {
76749c88b85STakashi Iwai 	for (; *list; list++) {
768*9ab0cb30STakashi Iwai 		struct snd_kcontrol *follower = ctl_find(card, *list);
769*9ab0cb30STakashi Iwai 		if (follower)
770*9ab0cb30STakashi Iwai 			snd_ctl_add_follower(master, follower);
77149c88b85STakashi Iwai 	}
77249c88b85STakashi Iwai }
77349c88b85STakashi Iwai 
774e23e7a14SBill Pemberton int snd_ca0106_mixer(struct snd_ca0106 *emu)
7751da177e4SLinus Torvalds {
776fca7f388STrent Piepho 	int err;
777e4a3d145STakashi Iwai         struct snd_card *card = emu->card;
77897974309STakashi Iwai 	const char * const *c;
77949c88b85STakashi Iwai 	struct snd_kcontrol *vmaster;
78097974309STakashi Iwai 	static const char * const ca0106_remove_ctls[] = {
7811da177e4SLinus Torvalds 		"Master Mono Playback Switch",
7821da177e4SLinus Torvalds 		"Master Mono Playback Volume",
7831da177e4SLinus Torvalds 		"3D Control - Switch",
7841da177e4SLinus Torvalds 		"3D Control Sigmatel - Depth",
7851da177e4SLinus Torvalds 		"PCM Playback Switch",
7861da177e4SLinus Torvalds 		"PCM Playback Volume",
7871da177e4SLinus Torvalds 		"CD Playback Switch",
7881da177e4SLinus Torvalds 		"CD Playback Volume",
7891da177e4SLinus Torvalds 		"Phone Playback Switch",
7901da177e4SLinus Torvalds 		"Phone Playback Volume",
7911da177e4SLinus Torvalds 		"Video Playback Switch",
7921da177e4SLinus Torvalds 		"Video Playback Volume",
793d355c82aSJaroslav Kysela 		"Beep Playback Switch",
794d355c82aSJaroslav Kysela 		"Beep Playback Volume",
7951da177e4SLinus Torvalds 		"Mono Output Select",
7961da177e4SLinus Torvalds 		"Capture Source",
7971da177e4SLinus Torvalds 		"Capture Switch",
7981da177e4SLinus Torvalds 		"Capture Volume",
7991da177e4SLinus Torvalds 		"External Amplifier",
8001da177e4SLinus Torvalds 		"Sigmatel 4-Speaker Stereo Playback Switch",
801afe6d7e3SAndreas Mohr 		"Surround Phase Inversion Playback Switch",
8021da177e4SLinus Torvalds 		NULL
8031da177e4SLinus Torvalds 	};
80497974309STakashi Iwai 	static const char * const ca0106_rename_ctls[] = {
8051da177e4SLinus Torvalds 		"Master Playback Switch", "Capture Switch",
8061da177e4SLinus Torvalds 		"Master Playback Volume", "Capture Volume",
8071da177e4SLinus Torvalds 		"Line Playback Switch", "AC97 Line Capture Switch",
8081da177e4SLinus Torvalds 		"Line Playback Volume", "AC97 Line Capture Volume",
8091da177e4SLinus Torvalds 		"Aux Playback Switch", "AC97 Aux Capture Switch",
8101da177e4SLinus Torvalds 		"Aux Playback Volume", "AC97 Aux Capture Volume",
8111da177e4SLinus Torvalds 		"Mic Playback Switch", "AC97 Mic Capture Switch",
8121da177e4SLinus Torvalds 		"Mic Playback Volume", "AC97 Mic Capture Volume",
8131da177e4SLinus Torvalds 		"Mic Select", "AC97 Mic Select",
8141da177e4SLinus Torvalds 		"Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
8151da177e4SLinus Torvalds 		NULL
8161da177e4SLinus Torvalds 	};
8171da177e4SLinus Torvalds #if 1
8181da177e4SLinus Torvalds 	for (c = ca0106_remove_ctls; *c; c++)
8191da177e4SLinus Torvalds 		remove_ctl(card, *c);
8201da177e4SLinus Torvalds 	for (c = ca0106_rename_ctls; *c; c += 2)
8211da177e4SLinus Torvalds 		rename_ctl(card, c[0], c[1]);
8221da177e4SLinus Torvalds #endif
8231da177e4SLinus Torvalds 
824fca7f388STrent Piepho 	ADD_CTLS(emu, snd_ca0106_volume_ctls);
82595a98265STakashi Iwai 	if (emu->details->i2c_adc == 1) {
826fca7f388STrent Piepho 		ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
827be0b7b01SJames Courtier-Dutton 		if (emu->details->gpio_type == 1)
82895a98265STakashi Iwai 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
829be0b7b01SJames Courtier-Dutton 		else  /* gpio_type == 2 */
830be0b7b01SJames Courtier-Dutton 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
83195a98265STakashi Iwai 		if (err < 0)
8321da177e4SLinus Torvalds 			return err;
83395a98265STakashi Iwai 	}
83464e5310aSAndy Owen 	if (emu->details->spi_dac) {
83564e5310aSAndy Owen 		int i;
83664e5310aSAndy Owen 		for (i = 0;; i++) {
83764e5310aSAndy Owen 			struct snd_kcontrol_new ctl;
83864e5310aSAndy Owen 			ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i);
83964e5310aSAndy Owen 			if (!ctl.name)
84064e5310aSAndy Owen 				break;
84164e5310aSAndy Owen 			err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu));
84264e5310aSAndy Owen 			if (err < 0)
84364e5310aSAndy Owen 				return err;
84464e5310aSAndy Owen 		}
84564e5310aSAndy Owen 	}
84649c88b85STakashi Iwai 
84749c88b85STakashi Iwai 	/* Create virtual master controls */
84849c88b85STakashi Iwai 	vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
84949c88b85STakashi Iwai 					      snd_ca0106_master_db_scale);
85049c88b85STakashi Iwai 	if (!vmaster)
85149c88b85STakashi Iwai 		return -ENOMEM;
852601e1cc5STakashi Iwai 	err = snd_ctl_add(card, vmaster);
853601e1cc5STakashi Iwai 	if (err < 0)
854601e1cc5STakashi Iwai 		return err;
855*9ab0cb30STakashi Iwai 	add_followers(card, vmaster, follower_vols);
85649c88b85STakashi Iwai 
8576fef153aSAndy Owen 	if (emu->details->spi_dac) {
85849c88b85STakashi Iwai 		vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
85949c88b85STakashi Iwai 						      NULL);
86049c88b85STakashi Iwai 		if (!vmaster)
86149c88b85STakashi Iwai 			return -ENOMEM;
862601e1cc5STakashi Iwai 		err = snd_ctl_add(card, vmaster);
863601e1cc5STakashi Iwai 		if (err < 0)
864601e1cc5STakashi Iwai 			return err;
865*9ab0cb30STakashi Iwai 		add_followers(card, vmaster, follower_sws);
86649c88b85STakashi Iwai 	}
867eeaf100dSTakashi Iwai 
868eeaf100dSTakashi Iwai 	strcpy(card->mixername, "CA0106");
8691da177e4SLinus Torvalds         return 0;
8701da177e4SLinus Torvalds }
8711da177e4SLinus Torvalds 
872c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP
8735da95273STakashi Iwai struct ca0106_vol_tbl {
8745da95273STakashi Iwai 	unsigned int channel_id;
8758df0f707STakashi Iwai 	unsigned int reg;
8765da95273STakashi Iwai };
8775da95273STakashi Iwai 
87897974309STakashi Iwai static const struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
8795da95273STakashi Iwai 	{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
8805da95273STakashi Iwai 	{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
8815da95273STakashi Iwai 	{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
8825da95273STakashi Iwai 	{ CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 },
8835da95273STakashi Iwai 	{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 },
8845da95273STakashi Iwai 	{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 },
8855da95273STakashi Iwai 	{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 },
8865da95273STakashi Iwai 	{ CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 },
8875da95273STakashi Iwai 	{ 1, CAPTURE_CONTROL },
8885da95273STakashi Iwai };
8895da95273STakashi Iwai 
8905da95273STakashi Iwai void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip)
8915da95273STakashi Iwai {
8925da95273STakashi Iwai 	int i;
8935da95273STakashi Iwai 
8945da95273STakashi Iwai 	/* save volumes */
8955da95273STakashi Iwai 	for (i = 0; i < NUM_SAVED_VOLUMES; i++)
8965da95273STakashi Iwai 		chip->saved_vol[i] =
8975da95273STakashi Iwai 			snd_ca0106_ptr_read(chip, saved_volumes[i].reg,
8985da95273STakashi Iwai 					    saved_volumes[i].channel_id);
8995da95273STakashi Iwai }
9005da95273STakashi Iwai 
9015da95273STakashi Iwai void snd_ca0106_mixer_resume(struct snd_ca0106  *chip)
9025da95273STakashi Iwai {
9035da95273STakashi Iwai 	int i;
9045da95273STakashi Iwai 
9055da95273STakashi Iwai 	for (i = 0; i < NUM_SAVED_VOLUMES; i++)
9065da95273STakashi Iwai 		snd_ca0106_ptr_write(chip, saved_volumes[i].reg,
9075da95273STakashi Iwai 				     saved_volumes[i].channel_id,
9085da95273STakashi Iwai 				     chip->saved_vol[i]);
9095da95273STakashi Iwai 
9105da95273STakashi Iwai 	ca0106_spdif_enable(chip);
9115da95273STakashi Iwai 	ca0106_set_capture_source(chip);
9125da95273STakashi Iwai 	ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1);
9135da95273STakashi Iwai 	for (i = 0; i < 4; i++)
9145da95273STakashi Iwai 		ca0106_set_spdif_bits(chip, i);
9155da95273STakashi Iwai 	if (chip->details->i2c_adc)
9165da95273STakashi Iwai 		ca0106_set_capture_mic_line_in(chip);
9175da95273STakashi Iwai }
918c7561cd8STakashi Iwai #endif /* CONFIG_PM_SLEEP */
919