xref: /openbmc/linux/sound/pci/ca0106/ca0106_mixer.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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 
ca0106_spdif_enable(struct snd_ca0106 * emu)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);
73fdb1e569STakashi Iwai 		val = inl(emu->port + CA0106_GPIO) & ~0x101;
74fdb1e569STakashi Iwai 		outl(val, emu->port + CA0106_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);
82fdb1e569STakashi Iwai 		val = inl(emu->port + CA0106_GPIO) | 0x101;
83fdb1e569STakashi Iwai 		outl(val, emu->port + CA0106_GPIO);
845da95273STakashi Iwai 	}
855da95273STakashi Iwai }
865da95273STakashi Iwai 
ca0106_set_capture_source(struct snd_ca0106 * emu)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 
ca0106_set_i2c_capture_source(struct snd_ca0106 * emu,unsigned int val,int force)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 
ca0106_set_capture_mic_line_in(struct snd_ca0106 * emu)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 */
122fdb1e569STakashi Iwai 		tmp = inl(emu->port + CA0106_GPIO) & ~0x400;
1235da95273STakashi Iwai 		tmp = tmp | 0x400;
124fdb1e569STakashi Iwai 		outl(tmp, emu->port + CA0106_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 */
128fdb1e569STakashi Iwai 		tmp = inl(emu->port + CA0106_GPIO) & ~0x400;
129fdb1e569STakashi Iwai 		outl(tmp, emu->port + CA0106_GPIO);
1305da95273STakashi Iwai 		/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
1315da95273STakashi Iwai 	}
1325da95273STakashi Iwai }
1335da95273STakashi Iwai 
ca0106_set_spdif_bits(struct snd_ca0106 * emu,int idx)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 
snd_ca0106_shared_spdif_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_shared_spdif_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_capture_source_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_ca0106_capture_source_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_capture_source_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_i2c_capture_source_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_ca0106_i2c_capture_source_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_i2c_capture_source_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_spdif_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
decode_spdif_bits(unsigned char * status,unsigned int bits)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 
snd_ca0106_spdif_get_default(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_spdif_get_stream(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_spdif_get_mask(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
encode_spdif_bits(unsigned char * status)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 
snd_ca0106_spdif_put_default(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_spdif_put_stream(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_volume_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_ca0106_volume_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_volume_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_i2c_volume_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_ca0106_i2c_volume_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_ca0106_i2c_volume_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
spi_mute_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
spi_mute_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
snd_ca0106_volume_spi_dac_ctl(const struct snd_ca0106_details * details,int channel_id)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 
remove_ctl(struct snd_card * card,const char * name)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 
rename_ctl(struct snd_card * card,const char * src,const char * dst)709e23e7a14SBill Pemberton static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
7101da177e4SLinus Torvalds {
7117affe6fdSTakashi Iwai 	struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src);
7121da177e4SLinus Torvalds 	if (kctl) {
713957ccc43SMaciej S. Szmigiero 		snd_ctl_rename(card, kctl, dst);
7141da177e4SLinus Torvalds 		return 0;
7151da177e4SLinus Torvalds 	}
7161da177e4SLinus Torvalds 	return -ENOENT;
7171da177e4SLinus Torvalds }
7181da177e4SLinus Torvalds 
719fca7f388STrent Piepho #define ADD_CTLS(emu, ctls)						\
720fca7f388STrent Piepho 	do {								\
721bed515b0SHarvey Harrison 		int i, _err;						\
722fca7f388STrent Piepho 		for (i = 0; i < ARRAY_SIZE(ctls); i++) {		\
723bed515b0SHarvey Harrison 			_err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
724bed515b0SHarvey Harrison 			if (_err < 0)					\
725bed515b0SHarvey Harrison 				return _err;				\
726fca7f388STrent Piepho 		}							\
727fca7f388STrent Piepho 	} while (0)
728fca7f388STrent Piepho 
729e23e7a14SBill Pemberton static
730c4865679STakashi Iwai DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
73149c88b85STakashi Iwai 
7329ab0cb30STakashi Iwai static const char * const follower_vols[] = {
73349c88b85STakashi Iwai 	"Analog Front Playback Volume",
73449c88b85STakashi Iwai         "Analog Rear Playback Volume",
73549c88b85STakashi Iwai 	"Analog Center/LFE Playback Volume",
73649c88b85STakashi Iwai         "Analog Side Playback Volume",
73749c88b85STakashi Iwai         "IEC958 Front Playback Volume",
73849c88b85STakashi Iwai 	"IEC958 Rear Playback Volume",
73949c88b85STakashi Iwai 	"IEC958 Center/LFE Playback Volume",
74049c88b85STakashi Iwai 	"IEC958 Unknown Playback Volume",
74149c88b85STakashi Iwai         "CAPTURE feedback Playback Volume",
74249c88b85STakashi Iwai 	NULL
74349c88b85STakashi Iwai };
74449c88b85STakashi Iwai 
7459ab0cb30STakashi Iwai static const char * const follower_sws[] = {
74649c88b85STakashi Iwai 	"Analog Front Playback Switch",
74749c88b85STakashi Iwai 	"Analog Rear Playback Switch",
74849c88b85STakashi Iwai 	"Analog Center/LFE Playback Switch",
74949c88b85STakashi Iwai 	"Analog Side Playback Switch",
75049c88b85STakashi Iwai 	"IEC958 Playback Switch",
75149c88b85STakashi Iwai 	NULL
75249c88b85STakashi Iwai };
75349c88b85STakashi Iwai 
snd_ca0106_mixer(struct snd_ca0106 * emu)754e23e7a14SBill Pemberton int snd_ca0106_mixer(struct snd_ca0106 *emu)
7551da177e4SLinus Torvalds {
756fca7f388STrent Piepho 	int err;
757e4a3d145STakashi Iwai         struct snd_card *card = emu->card;
75897974309STakashi Iwai 	const char * const *c;
75949c88b85STakashi Iwai 	struct snd_kcontrol *vmaster;
76097974309STakashi Iwai 	static const char * const ca0106_remove_ctls[] = {
7611da177e4SLinus Torvalds 		"Master Mono Playback Switch",
7621da177e4SLinus Torvalds 		"Master Mono Playback Volume",
7631da177e4SLinus Torvalds 		"3D Control - Switch",
7641da177e4SLinus Torvalds 		"3D Control Sigmatel - Depth",
7651da177e4SLinus Torvalds 		"PCM Playback Switch",
7661da177e4SLinus Torvalds 		"PCM Playback Volume",
7671da177e4SLinus Torvalds 		"CD Playback Switch",
7681da177e4SLinus Torvalds 		"CD Playback Volume",
7691da177e4SLinus Torvalds 		"Phone Playback Switch",
7701da177e4SLinus Torvalds 		"Phone Playback Volume",
7711da177e4SLinus Torvalds 		"Video Playback Switch",
7721da177e4SLinus Torvalds 		"Video Playback Volume",
773d355c82aSJaroslav Kysela 		"Beep Playback Switch",
774d355c82aSJaroslav Kysela 		"Beep Playback Volume",
7751da177e4SLinus Torvalds 		"Mono Output Select",
7761da177e4SLinus Torvalds 		"Capture Source",
7771da177e4SLinus Torvalds 		"Capture Switch",
7781da177e4SLinus Torvalds 		"Capture Volume",
7791da177e4SLinus Torvalds 		"External Amplifier",
7801da177e4SLinus Torvalds 		"Sigmatel 4-Speaker Stereo Playback Switch",
781afe6d7e3SAndreas Mohr 		"Surround Phase Inversion Playback Switch",
7821da177e4SLinus Torvalds 		NULL
7831da177e4SLinus Torvalds 	};
78497974309STakashi Iwai 	static const char * const ca0106_rename_ctls[] = {
7851da177e4SLinus Torvalds 		"Master Playback Switch", "Capture Switch",
7861da177e4SLinus Torvalds 		"Master Playback Volume", "Capture Volume",
7871da177e4SLinus Torvalds 		"Line Playback Switch", "AC97 Line Capture Switch",
7881da177e4SLinus Torvalds 		"Line Playback Volume", "AC97 Line Capture Volume",
7891da177e4SLinus Torvalds 		"Aux Playback Switch", "AC97 Aux Capture Switch",
7901da177e4SLinus Torvalds 		"Aux Playback Volume", "AC97 Aux Capture Volume",
7911da177e4SLinus Torvalds 		"Mic Playback Switch", "AC97 Mic Capture Switch",
7921da177e4SLinus Torvalds 		"Mic Playback Volume", "AC97 Mic Capture Volume",
7931da177e4SLinus Torvalds 		"Mic Select", "AC97 Mic Select",
7941da177e4SLinus Torvalds 		"Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
7951da177e4SLinus Torvalds 		NULL
7961da177e4SLinus Torvalds 	};
7971da177e4SLinus Torvalds #if 1
7981da177e4SLinus Torvalds 	for (c = ca0106_remove_ctls; *c; c++)
7991da177e4SLinus Torvalds 		remove_ctl(card, *c);
8001da177e4SLinus Torvalds 	for (c = ca0106_rename_ctls; *c; c += 2)
8011da177e4SLinus Torvalds 		rename_ctl(card, c[0], c[1]);
8021da177e4SLinus Torvalds #endif
8031da177e4SLinus Torvalds 
804fca7f388STrent Piepho 	ADD_CTLS(emu, snd_ca0106_volume_ctls);
80595a98265STakashi Iwai 	if (emu->details->i2c_adc == 1) {
806fca7f388STrent Piepho 		ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
807be0b7b01SJames Courtier-Dutton 		if (emu->details->gpio_type == 1)
80895a98265STakashi Iwai 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
809be0b7b01SJames Courtier-Dutton 		else  /* gpio_type == 2 */
810be0b7b01SJames Courtier-Dutton 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu));
81195a98265STakashi Iwai 		if (err < 0)
8121da177e4SLinus Torvalds 			return err;
81395a98265STakashi Iwai 	}
81464e5310aSAndy Owen 	if (emu->details->spi_dac) {
81564e5310aSAndy Owen 		int i;
81664e5310aSAndy Owen 		for (i = 0;; i++) {
81764e5310aSAndy Owen 			struct snd_kcontrol_new ctl;
81864e5310aSAndy Owen 			ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i);
81964e5310aSAndy Owen 			if (!ctl.name)
82064e5310aSAndy Owen 				break;
82164e5310aSAndy Owen 			err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu));
82264e5310aSAndy Owen 			if (err < 0)
82364e5310aSAndy Owen 				return err;
82464e5310aSAndy Owen 		}
82564e5310aSAndy Owen 	}
82649c88b85STakashi Iwai 
82749c88b85STakashi Iwai 	/* Create virtual master controls */
82849c88b85STakashi Iwai 	vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
82949c88b85STakashi Iwai 					      snd_ca0106_master_db_scale);
83049c88b85STakashi Iwai 	if (!vmaster)
83149c88b85STakashi Iwai 		return -ENOMEM;
832601e1cc5STakashi Iwai 	err = snd_ctl_add(card, vmaster);
833601e1cc5STakashi Iwai 	if (err < 0)
834601e1cc5STakashi Iwai 		return err;
835*b7bb11faSTakashi Iwai 	err = snd_ctl_add_followers(card, vmaster, follower_vols);
836*b7bb11faSTakashi Iwai 	if (err < 0)
837*b7bb11faSTakashi Iwai 		return err;
83849c88b85STakashi Iwai 
8396fef153aSAndy Owen 	if (emu->details->spi_dac) {
84049c88b85STakashi Iwai 		vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
84149c88b85STakashi Iwai 						      NULL);
84249c88b85STakashi Iwai 		if (!vmaster)
84349c88b85STakashi Iwai 			return -ENOMEM;
844601e1cc5STakashi Iwai 		err = snd_ctl_add(card, vmaster);
845601e1cc5STakashi Iwai 		if (err < 0)
846601e1cc5STakashi Iwai 			return err;
847*b7bb11faSTakashi Iwai 		err = snd_ctl_add_followers(card, vmaster, follower_sws);
848*b7bb11faSTakashi Iwai 		if (err < 0)
849*b7bb11faSTakashi Iwai 			return err;
85049c88b85STakashi Iwai 	}
851eeaf100dSTakashi Iwai 
852eeaf100dSTakashi Iwai 	strcpy(card->mixername, "CA0106");
8531da177e4SLinus Torvalds         return 0;
8541da177e4SLinus Torvalds }
8551da177e4SLinus Torvalds 
856c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP
8575da95273STakashi Iwai struct ca0106_vol_tbl {
8585da95273STakashi Iwai 	unsigned int channel_id;
8598df0f707STakashi Iwai 	unsigned int reg;
8605da95273STakashi Iwai };
8615da95273STakashi Iwai 
86297974309STakashi Iwai static const struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
8635da95273STakashi Iwai 	{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
8645da95273STakashi Iwai 	{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
8655da95273STakashi Iwai 	{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
8665da95273STakashi Iwai 	{ CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 },
8675da95273STakashi Iwai 	{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 },
8685da95273STakashi Iwai 	{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 },
8695da95273STakashi Iwai 	{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 },
8705da95273STakashi Iwai 	{ CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 },
8715da95273STakashi Iwai 	{ 1, CAPTURE_CONTROL },
8725da95273STakashi Iwai };
8735da95273STakashi Iwai 
snd_ca0106_mixer_suspend(struct snd_ca0106 * chip)8745da95273STakashi Iwai void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip)
8755da95273STakashi Iwai {
8765da95273STakashi Iwai 	int i;
8775da95273STakashi Iwai 
8785da95273STakashi Iwai 	/* save volumes */
8795da95273STakashi Iwai 	for (i = 0; i < NUM_SAVED_VOLUMES; i++)
8805da95273STakashi Iwai 		chip->saved_vol[i] =
8815da95273STakashi Iwai 			snd_ca0106_ptr_read(chip, saved_volumes[i].reg,
8825da95273STakashi Iwai 					    saved_volumes[i].channel_id);
8835da95273STakashi Iwai }
8845da95273STakashi Iwai 
snd_ca0106_mixer_resume(struct snd_ca0106 * chip)8855da95273STakashi Iwai void snd_ca0106_mixer_resume(struct snd_ca0106  *chip)
8865da95273STakashi Iwai {
8875da95273STakashi Iwai 	int i;
8885da95273STakashi Iwai 
8895da95273STakashi Iwai 	for (i = 0; i < NUM_SAVED_VOLUMES; i++)
8905da95273STakashi Iwai 		snd_ca0106_ptr_write(chip, saved_volumes[i].reg,
8915da95273STakashi Iwai 				     saved_volumes[i].channel_id,
8925da95273STakashi Iwai 				     chip->saved_vol[i]);
8935da95273STakashi Iwai 
8945da95273STakashi Iwai 	ca0106_spdif_enable(chip);
8955da95273STakashi Iwai 	ca0106_set_capture_source(chip);
8965da95273STakashi Iwai 	ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1);
8975da95273STakashi Iwai 	for (i = 0; i < 4; i++)
8985da95273STakashi Iwai 		ca0106_set_spdif_bits(chip, i);
8995da95273STakashi Iwai 	if (chip->details->i2c_adc)
9005da95273STakashi Iwai 		ca0106_set_capture_mic_line_in(chip);
9015da95273STakashi Iwai }
902c7561cd8STakashi Iwai #endif /* CONFIG_PM_SLEEP */
903