xref: /openbmc/linux/sound/pci/ice1712/phase.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *   ALSA driver for ICEnsemble ICE1724 (Envy24)
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *   Lowlevel functions for Terratec PHASE 22
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *	Copyright (c) 2005 Misha Zhilin <misha@epiphan.com>
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds /* PHASE 22 overview:
11eee75a6cSVedran Miletic  *   Audio controller: VIA Envy24HT-S (slightly trimmed down Envy24HT, 4in/4out)
121da177e4SLinus Torvalds  *   Analog chip: AK4524 (partially via Philip's 74HCT125)
13eee75a6cSVedran Miletic  *   Digital receiver: CS8414-CS (supported in this release)
14eee75a6cSVedran Miletic  *		PHASE 22 revision 2.0 and Terrasoniq/Musonik TS22PCI have CS8416
15eee75a6cSVedran Miletic  *		(support status unknown, please test and report)
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  *   Envy connects to AK4524
181da177e4SLinus Torvalds  *	- CS directly from GPIO 10
191da177e4SLinus Torvalds  *	- CCLK via 74HCT125's gate #4 from GPIO 4
201da177e4SLinus Torvalds  *	- CDTI via 74HCT125's gate #2 from GPIO 5
21eee75a6cSVedran Miletic  *		CDTI may be completely blocked by 74HCT125's gate #1
22eee75a6cSVedran Miletic  *		controlled by GPIO 3
23eee75a6cSVedran Miletic  */
24eee75a6cSVedran Miletic 
25eee75a6cSVedran Miletic /* PHASE 28 overview:
26cc67b7f7SVedran Miletic  *   Audio controller: VIA Envy24HT (full untrimmed version, 4in/8out)
27eee75a6cSVedran Miletic  *   Analog chip: WM8770 (8 channel 192k DAC, 2 channel 96k ADC)
28eee75a6cSVedran Miletic  *   Digital receiver: CS8414-CS (supported in this release)
291da177e4SLinus Torvalds  */
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds #include <linux/delay.h>
321da177e4SLinus Torvalds #include <linux/interrupt.h>
331da177e4SLinus Torvalds #include <linux/init.h>
341da177e4SLinus Torvalds #include <linux/slab.h>
3562932df8SIngo Molnar #include <linux/mutex.h>
3662932df8SIngo Molnar 
371da177e4SLinus Torvalds #include <sound/core.h>
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds #include "ice1712.h"
401da177e4SLinus Torvalds #include "envy24ht.h"
411da177e4SLinus Torvalds #include "phase.h"
42f640c320STakashi Iwai #include <sound/tlv.h>
431da177e4SLinus Torvalds 
447cda8ba9STakashi Iwai /* AC97 register cache for Phase28 */
457cda8ba9STakashi Iwai struct phase28_spec {
467cda8ba9STakashi Iwai 	unsigned short master[2];
477cda8ba9STakashi Iwai 	unsigned short vol[8];
48008f3599SHarvey Harrison };
497cda8ba9STakashi Iwai 
50aed058e8SSimone Zinanni /* WM8770 registers */
51aed058e8SSimone Zinanni #define WM_DAC_ATTEN		0x00	/* DAC1-8 analog attenuation */
52aed058e8SSimone Zinanni #define WM_DAC_MASTER_ATTEN	0x08	/* DAC master analog attenuation */
53aed058e8SSimone Zinanni #define WM_DAC_DIG_ATTEN	0x09	/* DAC1-8 digital attenuation */
54aed058e8SSimone Zinanni #define WM_DAC_DIG_MASTER_ATTEN	0x11	/* DAC master digital attenuation */
55aed058e8SSimone Zinanni #define WM_PHASE_SWAP		0x12	/* DAC phase */
56aed058e8SSimone Zinanni #define WM_DAC_CTRL1		0x13	/* DAC control bits */
57aed058e8SSimone Zinanni #define WM_MUTE			0x14	/* mute controls */
58aed058e8SSimone Zinanni #define WM_DAC_CTRL2		0x15	/* de-emphasis and zefo-flag */
59aed058e8SSimone Zinanni #define WM_INT_CTRL		0x16	/* interface control */
60aed058e8SSimone Zinanni #define WM_MASTER		0x17	/* master clock and mode */
61aed058e8SSimone Zinanni #define WM_POWERDOWN		0x18	/* power-down controls */
62aed058e8SSimone Zinanni #define WM_ADC_GAIN		0x19	/* ADC gain L(19)/R(1a) */
63aed058e8SSimone Zinanni #define WM_ADC_MUX		0x1b	/* input MUX */
64aed058e8SSimone Zinanni #define WM_OUT_MUX1		0x1c	/* output MUX */
65aed058e8SSimone Zinanni #define WM_OUT_MUX2		0x1e	/* output MUX */
66aed058e8SSimone Zinanni #define WM_RESET		0x1f	/* software reset */
67aed058e8SSimone Zinanni 
68aed058e8SSimone Zinanni 
69aed058e8SSimone Zinanni /*
70aed058e8SSimone Zinanni  * Logarithmic volume values for WM8770
71aed058e8SSimone Zinanni  * Computed as 20 * Log10(255 / x)
72aed058e8SSimone Zinanni  */
7332b47da0STakashi Iwai static const unsigned char wm_vol[256] = {
74cc67b7f7SVedran Miletic 	127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24,
75cc67b7f7SVedran Miletic 	24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18,
76cc67b7f7SVedran Miletic 	17, 17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14,
77cc67b7f7SVedran Miletic 	14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11,
78cc67b7f7SVedran Miletic 	11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9,
79cc67b7f7SVedran Miletic 	9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7,
80cc67b7f7SVedran Miletic 	7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5,
81cc67b7f7SVedran Miletic 	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
82cc67b7f7SVedran Miletic 	4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
83cc67b7f7SVedran Miletic 	3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
84cc67b7f7SVedran Miletic 	2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
85cc67b7f7SVedran Miletic 	1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
86aed058e8SSimone Zinanni };
87aed058e8SSimone Zinanni 
88aed058e8SSimone Zinanni #define WM_VOL_MAX	(sizeof(wm_vol) - 1)
89aed058e8SSimone Zinanni #define WM_VOL_MUTE	0x8000
90aed058e8SSimone Zinanni 
913135432eSBhumika Goyal static const struct snd_akm4xxx akm_phase22 = {
921da177e4SLinus Torvalds 	.type = SND_AK4524,
931da177e4SLinus Torvalds 	.num_dacs = 2,
941da177e4SLinus Torvalds 	.num_adcs = 2,
951da177e4SLinus Torvalds };
961da177e4SLinus Torvalds 
974647e8d5SBhumika Goyal static const struct snd_ak4xxx_private akm_phase22_priv = {
981da177e4SLinus Torvalds 	.caddr =	2,
991da177e4SLinus Torvalds 	.cif =		1,
1001da177e4SLinus Torvalds 	.data_mask =	1 << 4,
1011da177e4SLinus Torvalds 	.clk_mask =	1 << 5,
1021da177e4SLinus Torvalds 	.cs_mask =	1 << 10,
1031da177e4SLinus Torvalds 	.cs_addr =	1 << 10,
1041da177e4SLinus Torvalds 	.cs_none =	0,
1051da177e4SLinus Torvalds 	.add_flags = 	1 << 3,
1061da177e4SLinus Torvalds 	.mask_flags =	0,
1071da177e4SLinus Torvalds };
1081da177e4SLinus Torvalds 
phase22_init(struct snd_ice1712 * ice)109e23e7a14SBill Pemberton static int phase22_init(struct snd_ice1712 *ice)
1101da177e4SLinus Torvalds {
111ab0c7d72STakashi Iwai 	struct snd_akm4xxx *ak;
1121da177e4SLinus Torvalds 	int err;
1131da177e4SLinus Torvalds 
114cc67b7f7SVedran Miletic 	/* Configure DAC/ADC description for generic part of ice1724 */
1151da177e4SLinus Torvalds 	switch (ice->eeprom.subvendor) {
1161da177e4SLinus Torvalds 	case VT1724_SUBDEVICE_PHASE22:
117740dc9c4SMisha Zhilin 	case VT1724_SUBDEVICE_TS22:
1181da177e4SLinus Torvalds 		ice->num_total_dacs = 2;
1191da177e4SLinus Torvalds 		ice->num_total_adcs = 2;
120cc67b7f7SVedran Miletic 		ice->vt1720 = 1; /* Envy24HT-S have 16 bit wide GPIO */
1211da177e4SLinus Torvalds 		break;
1221da177e4SLinus Torvalds 	default:
1231da177e4SLinus Torvalds 		snd_BUG();
1241da177e4SLinus Torvalds 		return -EINVAL;
1251da177e4SLinus Torvalds 	}
1261da177e4SLinus Torvalds 
127cc67b7f7SVedran Miletic 	/* Initialize analog chips */
128cc67b7f7SVedran Miletic 	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
129cc67b7f7SVedran Miletic 	ak = ice->akm;
1301da177e4SLinus Torvalds 	if (!ak)
1311da177e4SLinus Torvalds 		return -ENOMEM;
1321da177e4SLinus Torvalds 	ice->akm_codecs = 1;
1331da177e4SLinus Torvalds 	switch (ice->eeprom.subvendor) {
1341da177e4SLinus Torvalds 	case VT1724_SUBDEVICE_PHASE22:
135740dc9c4SMisha Zhilin 	case VT1724_SUBDEVICE_TS22:
136cc67b7f7SVedran Miletic 		err = snd_ice1712_akm4xxx_init(ak, &akm_phase22,
137cc67b7f7SVedran Miletic 						&akm_phase22_priv, ice);
138cc67b7f7SVedran Miletic 		if (err < 0)
1391da177e4SLinus Torvalds 			return err;
1401da177e4SLinus Torvalds 		break;
1411da177e4SLinus Torvalds 	}
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	return 0;
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds 
phase22_add_controls(struct snd_ice1712 * ice)146e23e7a14SBill Pemberton static int phase22_add_controls(struct snd_ice1712 *ice)
1471da177e4SLinus Torvalds {
1481da177e4SLinus Torvalds 	int err = 0;
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 	switch (ice->eeprom.subvendor) {
1511da177e4SLinus Torvalds 	case VT1724_SUBDEVICE_PHASE22:
152740dc9c4SMisha Zhilin 	case VT1724_SUBDEVICE_TS22:
1531da177e4SLinus Torvalds 		err = snd_ice1712_akm4xxx_build_controls(ice);
1541da177e4SLinus Torvalds 		if (err < 0)
1551da177e4SLinus Torvalds 			return err;
1561da177e4SLinus Torvalds 	}
1571da177e4SLinus Torvalds 	return 0;
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds 
160*f16a4e96STakashi Iwai static const unsigned char phase22_eeprom[] = {
161eee75a6cSVedran Miletic 	[ICE_EEP2_SYSCONF]     = 0x28,  /* clock 512, mpu 401,
162eee75a6cSVedran Miletic 					spdif-in/1xADC, 1xDACs */
163189bc171STakashi Iwai 	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */
164eee75a6cSVedran Miletic 	[ICE_EEP2_I2S]         = 0xf0,	/* vol, 96k, 24bit */
165189bc171STakashi Iwai 	[ICE_EEP2_SPDIF]       = 0xc3,	/* out-en, out-int, spdif-in */
166189bc171STakashi Iwai 	[ICE_EEP2_GPIO_DIR]    = 0xff,
167189bc171STakashi Iwai 	[ICE_EEP2_GPIO_DIR1]   = 0xff,
168189bc171STakashi Iwai 	[ICE_EEP2_GPIO_DIR2]   = 0xff,
169189bc171STakashi Iwai 	[ICE_EEP2_GPIO_MASK]   = 0x00,
170189bc171STakashi Iwai 	[ICE_EEP2_GPIO_MASK1]  = 0x00,
171189bc171STakashi Iwai 	[ICE_EEP2_GPIO_MASK2]  = 0x00,
172189bc171STakashi Iwai 	[ICE_EEP2_GPIO_STATE]  = 0x00,
173189bc171STakashi Iwai 	[ICE_EEP2_GPIO_STATE1] = 0x00,
174189bc171STakashi Iwai 	[ICE_EEP2_GPIO_STATE2] = 0x00,
1751da177e4SLinus Torvalds };
1761da177e4SLinus Torvalds 
177*f16a4e96STakashi Iwai static const unsigned char phase28_eeprom[] = {
178eee75a6cSVedran Miletic 	[ICE_EEP2_SYSCONF]     = 0x2b,  /* clock 512, mpu401,
179eee75a6cSVedran Miletic 					spdif-in/1xADC, 4xDACs */
180189bc171STakashi Iwai 	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */
181189bc171STakashi Iwai 	[ICE_EEP2_I2S]         = 0xfc,	/* vol, 96k, 24bit, 192k */
182189bc171STakashi Iwai 	[ICE_EEP2_SPDIF]       = 0xc3,	/* out-en, out-int, spdif-in */
183189bc171STakashi Iwai 	[ICE_EEP2_GPIO_DIR]    = 0xff,
184189bc171STakashi Iwai 	[ICE_EEP2_GPIO_DIR1]   = 0xff,
185189bc171STakashi Iwai 	[ICE_EEP2_GPIO_DIR2]   = 0x5f,
186189bc171STakashi Iwai 	[ICE_EEP2_GPIO_MASK]   = 0x00,
187189bc171STakashi Iwai 	[ICE_EEP2_GPIO_MASK1]  = 0x00,
188189bc171STakashi Iwai 	[ICE_EEP2_GPIO_MASK2]  = 0x00,
189189bc171STakashi Iwai 	[ICE_EEP2_GPIO_STATE]  = 0x00,
190189bc171STakashi Iwai 	[ICE_EEP2_GPIO_STATE1] = 0x00,
191189bc171STakashi Iwai 	[ICE_EEP2_GPIO_STATE2] = 0x00,
192aed058e8SSimone Zinanni };
193aed058e8SSimone Zinanni 
194aed058e8SSimone Zinanni /*
195aed058e8SSimone Zinanni  * write data in the SPI mode
196aed058e8SSimone Zinanni  */
phase28_spi_write(struct snd_ice1712 * ice,unsigned int cs,unsigned int data,int bits)197cc67b7f7SVedran Miletic static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs,
198cc67b7f7SVedran Miletic 				unsigned int data, int bits)
199aed058e8SSimone Zinanni {
200aed058e8SSimone Zinanni 	unsigned int tmp;
201aed058e8SSimone Zinanni 	int i;
202aed058e8SSimone Zinanni 
203aed058e8SSimone Zinanni 	tmp = snd_ice1712_gpio_read(ice);
204aed058e8SSimone Zinanni 
205cc67b7f7SVedran Miletic 	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|
206cc67b7f7SVedran Miletic 					PHASE28_SPI_CLK|PHASE28_WM_CS));
207aed058e8SSimone Zinanni 	tmp |= PHASE28_WM_RW;
208aed058e8SSimone Zinanni 	tmp &= ~cs;
209aed058e8SSimone Zinanni 	snd_ice1712_gpio_write(ice, tmp);
210aed058e8SSimone Zinanni 	udelay(1);
211aed058e8SSimone Zinanni 
212aed058e8SSimone Zinanni 	for (i = bits - 1; i >= 0; i--) {
213aed058e8SSimone Zinanni 		tmp &= ~PHASE28_SPI_CLK;
214aed058e8SSimone Zinanni 		snd_ice1712_gpio_write(ice, tmp);
215aed058e8SSimone Zinanni 		udelay(1);
216aed058e8SSimone Zinanni 		if (data & (1 << i))
217aed058e8SSimone Zinanni 			tmp |= PHASE28_SPI_MOSI;
218aed058e8SSimone Zinanni 		else
219aed058e8SSimone Zinanni 			tmp &= ~PHASE28_SPI_MOSI;
220aed058e8SSimone Zinanni 		snd_ice1712_gpio_write(ice, tmp);
221aed058e8SSimone Zinanni 		udelay(1);
222aed058e8SSimone Zinanni 		tmp |= PHASE28_SPI_CLK;
223aed058e8SSimone Zinanni 		snd_ice1712_gpio_write(ice, tmp);
224aed058e8SSimone Zinanni 		udelay(1);
225aed058e8SSimone Zinanni 	}
226aed058e8SSimone Zinanni 
227aed058e8SSimone Zinanni 	tmp &= ~PHASE28_SPI_CLK;
228aed058e8SSimone Zinanni 	tmp |= cs;
229aed058e8SSimone Zinanni 	snd_ice1712_gpio_write(ice, tmp);
230aed058e8SSimone Zinanni 	udelay(1);
231aed058e8SSimone Zinanni 	tmp |= PHASE28_SPI_CLK;
232aed058e8SSimone Zinanni 	snd_ice1712_gpio_write(ice, tmp);
233aed058e8SSimone Zinanni 	udelay(1);
234aed058e8SSimone Zinanni }
235aed058e8SSimone Zinanni 
236aed058e8SSimone Zinanni /*
237aed058e8SSimone Zinanni  * get the current register value of WM codec
238aed058e8SSimone Zinanni  */
wm_get(struct snd_ice1712 * ice,int reg)239ab0c7d72STakashi Iwai static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
240aed058e8SSimone Zinanni {
241aed058e8SSimone Zinanni 	reg <<= 1;
242aed058e8SSimone Zinanni 	return ((unsigned short)ice->akm[0].images[reg] << 8) |
243aed058e8SSimone Zinanni 		ice->akm[0].images[reg + 1];
244aed058e8SSimone Zinanni }
245aed058e8SSimone Zinanni 
246aed058e8SSimone Zinanni /*
247aed058e8SSimone Zinanni  * set the register value of WM codec
248aed058e8SSimone Zinanni  */
wm_put_nocache(struct snd_ice1712 * ice,int reg,unsigned short val)249ab0c7d72STakashi Iwai static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
250aed058e8SSimone Zinanni {
251aed058e8SSimone Zinanni 	phase28_spi_write(ice, PHASE28_WM_CS, (reg << 9) | (val & 0x1ff), 16);
252aed058e8SSimone Zinanni }
253aed058e8SSimone Zinanni 
254aed058e8SSimone Zinanni /*
255aed058e8SSimone Zinanni  * set the register value of WM codec and remember it
256aed058e8SSimone Zinanni  */
wm_put(struct snd_ice1712 * ice,int reg,unsigned short val)257ab0c7d72STakashi Iwai static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
258aed058e8SSimone Zinanni {
259aed058e8SSimone Zinanni 	wm_put_nocache(ice, reg, val);
260aed058e8SSimone Zinanni 	reg <<= 1;
261aed058e8SSimone Zinanni 	ice->akm[0].images[reg] = val >> 8;
262aed058e8SSimone Zinanni 	ice->akm[0].images[reg + 1] = val;
263aed058e8SSimone Zinanni }
264aed058e8SSimone Zinanni 
wm_set_vol(struct snd_ice1712 * ice,unsigned int index,unsigned short vol,unsigned short master)265cc67b7f7SVedran Miletic static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index,
266cc67b7f7SVedran Miletic 			unsigned short vol, unsigned short master)
267aed058e8SSimone Zinanni {
268aed058e8SSimone Zinanni 	unsigned char nvol;
269aed058e8SSimone Zinanni 
270aed058e8SSimone Zinanni 	if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
271aed058e8SSimone Zinanni 		nvol = 0;
272aed058e8SSimone Zinanni 	else
273cc67b7f7SVedran Miletic 		nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) *
274cc67b7f7SVedran Miletic 			(master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
275aed058e8SSimone Zinanni 
276aed058e8SSimone Zinanni 	wm_put(ice, index, nvol);
277aed058e8SSimone Zinanni 	wm_put_nocache(ice, index, 0x180 | nvol);
278aed058e8SSimone Zinanni }
279aed058e8SSimone Zinanni 
280aed058e8SSimone Zinanni /*
281aed058e8SSimone Zinanni  * DAC mute control
282aed058e8SSimone Zinanni  */
283a5ce8890STakashi Iwai #define wm_pcm_mute_info	snd_ctl_boolean_mono_info
284aed058e8SSimone Zinanni 
wm_pcm_mute_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)285cc67b7f7SVedran Miletic static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol,
286cc67b7f7SVedran Miletic 				struct snd_ctl_elem_value *ucontrol)
287aed058e8SSimone Zinanni {
288ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
289aed058e8SSimone Zinanni 
29062932df8SIngo Molnar 	mutex_lock(&ice->gpio_mutex);
291cc67b7f7SVedran Miletic 	ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ?
292cc67b7f7SVedran Miletic 						0 : 1;
29362932df8SIngo Molnar 	mutex_unlock(&ice->gpio_mutex);
294aed058e8SSimone Zinanni 	return 0;
295aed058e8SSimone Zinanni }
296aed058e8SSimone Zinanni 
wm_pcm_mute_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)297cc67b7f7SVedran Miletic static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol,
298cc67b7f7SVedran Miletic 				struct snd_ctl_elem_value *ucontrol)
299aed058e8SSimone Zinanni {
300ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
301aed058e8SSimone Zinanni 	unsigned short nval, oval;
302aed058e8SSimone Zinanni 	int change;
303aed058e8SSimone Zinanni 
304aed058e8SSimone Zinanni 	snd_ice1712_save_gpio_status(ice);
305aed058e8SSimone Zinanni 	oval = wm_get(ice, WM_MUTE);
306aed058e8SSimone Zinanni 	nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
307cc67b7f7SVedran Miletic 	change = (nval != oval);
308cc67b7f7SVedran Miletic 	if (change)
309aed058e8SSimone Zinanni 		wm_put(ice, WM_MUTE, nval);
310aed058e8SSimone Zinanni 	snd_ice1712_restore_gpio_status(ice);
311aed058e8SSimone Zinanni 
312aed058e8SSimone Zinanni 	return change;
313aed058e8SSimone Zinanni }
314aed058e8SSimone Zinanni 
315aed058e8SSimone Zinanni /*
316aed058e8SSimone Zinanni  * Master volume attenuation mixer control
317aed058e8SSimone Zinanni  */
wm_master_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)318cc67b7f7SVedran Miletic static int wm_master_vol_info(struct snd_kcontrol *kcontrol,
319cc67b7f7SVedran Miletic 				struct snd_ctl_elem_info *uinfo)
320aed058e8SSimone Zinanni {
321aed058e8SSimone Zinanni 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
322aed058e8SSimone Zinanni 	uinfo->count = 2;
323aed058e8SSimone Zinanni 	uinfo->value.integer.min = 0;
324aed058e8SSimone Zinanni 	uinfo->value.integer.max = WM_VOL_MAX;
325aed058e8SSimone Zinanni 	return 0;
326aed058e8SSimone Zinanni }
327aed058e8SSimone Zinanni 
wm_master_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)328cc67b7f7SVedran Miletic static int wm_master_vol_get(struct snd_kcontrol *kcontrol,
329cc67b7f7SVedran Miletic 				struct snd_ctl_elem_value *ucontrol)
330aed058e8SSimone Zinanni {
331ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
3327cda8ba9STakashi Iwai 	struct phase28_spec *spec = ice->spec;
333aed058e8SSimone Zinanni 	int i;
334aed058e8SSimone Zinanni 	for (i = 0; i < 2; i++)
335cc67b7f7SVedran Miletic 		ucontrol->value.integer.value[i] = spec->master[i] &
336cc67b7f7SVedran Miletic 							~WM_VOL_MUTE;
337aed058e8SSimone Zinanni 	return 0;
338aed058e8SSimone Zinanni }
339aed058e8SSimone Zinanni 
wm_master_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)340cc67b7f7SVedran Miletic static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
341cc67b7f7SVedran Miletic 				struct snd_ctl_elem_value *ucontrol)
342aed058e8SSimone Zinanni {
343ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
3447cda8ba9STakashi Iwai 	struct phase28_spec *spec = ice->spec;
345aed058e8SSimone Zinanni 	int ch, change = 0;
346aed058e8SSimone Zinanni 
347aed058e8SSimone Zinanni 	snd_ice1712_save_gpio_status(ice);
348aed058e8SSimone Zinanni 	for (ch = 0; ch < 2; ch++) {
3499cd17cd2STakashi Iwai 		unsigned int vol = ucontrol->value.integer.value[ch];
3509cd17cd2STakashi Iwai 		if (vol > WM_VOL_MAX)
3519cd17cd2STakashi Iwai 			continue;
3527cda8ba9STakashi Iwai 		vol |= spec->master[ch] & WM_VOL_MUTE;
3537cda8ba9STakashi Iwai 		if (vol != spec->master[ch]) {
354aed058e8SSimone Zinanni 			int dac;
3557cda8ba9STakashi Iwai 			spec->master[ch] = vol;
356aed058e8SSimone Zinanni 			for (dac = 0; dac < ice->num_total_dacs; dac += 2)
357aed058e8SSimone Zinanni 				wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
3587cda8ba9STakashi Iwai 					   spec->vol[dac + ch],
3597cda8ba9STakashi Iwai 					   spec->master[ch]);
360aed058e8SSimone Zinanni 			change = 1;
361aed058e8SSimone Zinanni 		}
362aed058e8SSimone Zinanni 	}
363aed058e8SSimone Zinanni 	snd_ice1712_restore_gpio_status(ice);
364aed058e8SSimone Zinanni 	return change;
365aed058e8SSimone Zinanni }
366aed058e8SSimone Zinanni 
phase28_init(struct snd_ice1712 * ice)367e23e7a14SBill Pemberton static int phase28_init(struct snd_ice1712 *ice)
368aed058e8SSimone Zinanni {
36932b47da0STakashi Iwai 	static const unsigned short wm_inits_phase28[] = {
370aed058e8SSimone Zinanni 		/* These come first to reduce init pop noise */
371aed058e8SSimone Zinanni 		0x1b, 0x044,	/* ADC Mux (AC'97 source) */
372aed058e8SSimone Zinanni 		0x1c, 0x00B,	/* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
373aed058e8SSimone Zinanni 		0x1d, 0x009,	/* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
374aed058e8SSimone Zinanni 
375aed058e8SSimone Zinanni 		0x18, 0x000,	/* All power-up */
376aed058e8SSimone Zinanni 
377aed058e8SSimone Zinanni 		0x16, 0x122,	/* I2S, normal polarity, 24bit */
378aed058e8SSimone Zinanni 		0x17, 0x022,	/* 256fs, slave mode */
379aed058e8SSimone Zinanni 		0x00, 0,	/* DAC1 analog mute */
380aed058e8SSimone Zinanni 		0x01, 0,	/* DAC2 analog mute */
381aed058e8SSimone Zinanni 		0x02, 0,	/* DAC3 analog mute */
382aed058e8SSimone Zinanni 		0x03, 0,	/* DAC4 analog mute */
383aed058e8SSimone Zinanni 		0x04, 0,	/* DAC5 analog mute */
384aed058e8SSimone Zinanni 		0x05, 0,	/* DAC6 analog mute */
385aed058e8SSimone Zinanni 		0x06, 0,	/* DAC7 analog mute */
386aed058e8SSimone Zinanni 		0x07, 0,	/* DAC8 analog mute */
387aed058e8SSimone Zinanni 		0x08, 0x100,	/* master analog mute */
388aed058e8SSimone Zinanni 		0x09, 0xff,	/* DAC1 digital full */
389aed058e8SSimone Zinanni 		0x0a, 0xff,	/* DAC2 digital full */
390aed058e8SSimone Zinanni 		0x0b, 0xff,	/* DAC3 digital full */
391aed058e8SSimone Zinanni 		0x0c, 0xff,	/* DAC4 digital full */
392aed058e8SSimone Zinanni 		0x0d, 0xff,	/* DAC5 digital full */
393aed058e8SSimone Zinanni 		0x0e, 0xff,	/* DAC6 digital full */
394aed058e8SSimone Zinanni 		0x0f, 0xff,	/* DAC7 digital full */
395aed058e8SSimone Zinanni 		0x10, 0xff,	/* DAC8 digital full */
396aed058e8SSimone Zinanni 		0x11, 0x1ff,	/* master digital full */
397aed058e8SSimone Zinanni 		0x12, 0x000,	/* phase normal */
398aed058e8SSimone Zinanni 		0x13, 0x090,	/* unmute DAC L/R */
399aed058e8SSimone Zinanni 		0x14, 0x000,	/* all unmute */
400aed058e8SSimone Zinanni 		0x15, 0x000,	/* no deemphasis, no ZFLG */
401aed058e8SSimone Zinanni 		0x19, 0x000,	/* -12dB ADC/L */
402aed058e8SSimone Zinanni 		0x1a, 0x000,	/* -12dB ADC/R */
403aed058e8SSimone Zinanni 		(unsigned short)-1
404aed058e8SSimone Zinanni 	};
405aed058e8SSimone Zinanni 
406aed058e8SSimone Zinanni 	unsigned int tmp;
407ab0c7d72STakashi Iwai 	struct snd_akm4xxx *ak;
4087cda8ba9STakashi Iwai 	struct phase28_spec *spec;
40932b47da0STakashi Iwai 	const unsigned short *p;
410aed058e8SSimone Zinanni 	int i;
411aed058e8SSimone Zinanni 
412aed058e8SSimone Zinanni 	ice->num_total_dacs = 8;
413aed058e8SSimone Zinanni 	ice->num_total_adcs = 2;
414aed058e8SSimone Zinanni 
4157cda8ba9STakashi Iwai 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4167cda8ba9STakashi Iwai 	if (!spec)
4177cda8ba9STakashi Iwai 		return -ENOMEM;
4187cda8ba9STakashi Iwai 	ice->spec = spec;
4197cda8ba9STakashi Iwai 
420cc67b7f7SVedran Miletic 	/* Initialize analog chips */
421cc67b7f7SVedran Miletic 	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
422cc67b7f7SVedran Miletic 	ak = ice->akm;
423aed058e8SSimone Zinanni 	if (!ak)
424aed058e8SSimone Zinanni 		return -ENOMEM;
425aed058e8SSimone Zinanni 	ice->akm_codecs = 1;
426aed058e8SSimone Zinanni 
427cc67b7f7SVedran Miletic 	snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for time being */
428aed058e8SSimone Zinanni 
429aed058e8SSimone Zinanni 	/* reset the wm codec as the SPI mode */
430aed058e8SSimone Zinanni 	snd_ice1712_save_gpio_status(ice);
431cc67b7f7SVedran Miletic 	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|
432cc67b7f7SVedran Miletic 					PHASE28_HP_SEL));
433aed058e8SSimone Zinanni 
434aed058e8SSimone Zinanni 	tmp = snd_ice1712_gpio_read(ice);
435aed058e8SSimone Zinanni 	tmp &= ~PHASE28_WM_RESET;
436aed058e8SSimone Zinanni 	snd_ice1712_gpio_write(ice, tmp);
437aed058e8SSimone Zinanni 	udelay(1);
438aed058e8SSimone Zinanni 	tmp |= PHASE28_WM_CS;
439aed058e8SSimone Zinanni 	snd_ice1712_gpio_write(ice, tmp);
440aed058e8SSimone Zinanni 	udelay(1);
441aed058e8SSimone Zinanni 	tmp |= PHASE28_WM_RESET;
442aed058e8SSimone Zinanni 	snd_ice1712_gpio_write(ice, tmp);
443aed058e8SSimone Zinanni 	udelay(1);
444aed058e8SSimone Zinanni 
445aed058e8SSimone Zinanni 	p = wm_inits_phase28;
446aed058e8SSimone Zinanni 	for (; *p != (unsigned short)-1; p += 2)
447aed058e8SSimone Zinanni 		wm_put(ice, p[0], p[1]);
448aed058e8SSimone Zinanni 
449aed058e8SSimone Zinanni 	snd_ice1712_restore_gpio_status(ice);
450aed058e8SSimone Zinanni 
4517cda8ba9STakashi Iwai 	spec->master[0] = WM_VOL_MUTE;
4527cda8ba9STakashi Iwai 	spec->master[1] = WM_VOL_MUTE;
453aed058e8SSimone Zinanni 	for (i = 0; i < ice->num_total_dacs; i++) {
4547cda8ba9STakashi Iwai 		spec->vol[i] = WM_VOL_MUTE;
4557cda8ba9STakashi Iwai 		wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
456aed058e8SSimone Zinanni 	}
457aed058e8SSimone Zinanni 
458aed058e8SSimone Zinanni 	return 0;
459aed058e8SSimone Zinanni }
460aed058e8SSimone Zinanni 
461aed058e8SSimone Zinanni /*
462aed058e8SSimone Zinanni  * DAC volume attenuation mixer control
463aed058e8SSimone Zinanni  */
wm_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)464cc67b7f7SVedran Miletic static int wm_vol_info(struct snd_kcontrol *kcontrol,
465cc67b7f7SVedran Miletic 			struct snd_ctl_elem_info *uinfo)
466aed058e8SSimone Zinanni {
467aed058e8SSimone Zinanni 	int voices = kcontrol->private_value >> 8;
468aed058e8SSimone Zinanni 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
469aed058e8SSimone Zinanni 	uinfo->count = voices;
470aed058e8SSimone Zinanni 	uinfo->value.integer.min = 0;		/* mute (-101dB) */
471aed058e8SSimone Zinanni 	uinfo->value.integer.max = 0x7F;	/* 0dB */
472aed058e8SSimone Zinanni 	return 0;
473aed058e8SSimone Zinanni }
474aed058e8SSimone Zinanni 
wm_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)475cc67b7f7SVedran Miletic static int wm_vol_get(struct snd_kcontrol *kcontrol,
476cc67b7f7SVedran Miletic 			struct snd_ctl_elem_value *ucontrol)
477aed058e8SSimone Zinanni {
478ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
4797cda8ba9STakashi Iwai 	struct phase28_spec *spec = ice->spec;
480aed058e8SSimone Zinanni 	int i, ofs, voices;
481aed058e8SSimone Zinanni 
482aed058e8SSimone Zinanni 	voices = kcontrol->private_value >> 8;
483aed058e8SSimone Zinanni 	ofs = kcontrol->private_value & 0xff;
484aed058e8SSimone Zinanni 	for (i = 0; i < voices; i++)
4857cda8ba9STakashi Iwai 		ucontrol->value.integer.value[i] =
4867cda8ba9STakashi Iwai 			spec->vol[ofs+i] & ~WM_VOL_MUTE;
487aed058e8SSimone Zinanni 	return 0;
488aed058e8SSimone Zinanni }
489aed058e8SSimone Zinanni 
wm_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)490cc67b7f7SVedran Miletic static int wm_vol_put(struct snd_kcontrol *kcontrol,
491cc67b7f7SVedran Miletic 			struct snd_ctl_elem_value *ucontrol)
492aed058e8SSimone Zinanni {
493ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
4947cda8ba9STakashi Iwai 	struct phase28_spec *spec = ice->spec;
495aed058e8SSimone Zinanni 	int i, idx, ofs, voices;
496aed058e8SSimone Zinanni 	int change = 0;
497aed058e8SSimone Zinanni 
498aed058e8SSimone Zinanni 	voices = kcontrol->private_value >> 8;
499aed058e8SSimone Zinanni 	ofs = kcontrol->private_value & 0xff;
500aed058e8SSimone Zinanni 	snd_ice1712_save_gpio_status(ice);
501aed058e8SSimone Zinanni 	for (i = 0; i < voices; i++) {
5029cd17cd2STakashi Iwai 		unsigned int vol;
5039cd17cd2STakashi Iwai 		vol = ucontrol->value.integer.value[i];
5049cd17cd2STakashi Iwai 		if (vol > 0x7f)
5059cd17cd2STakashi Iwai 			continue;
5067cda8ba9STakashi Iwai 		vol |= spec->vol[ofs+i] & WM_VOL_MUTE;
5077cda8ba9STakashi Iwai 		if (vol != spec->vol[ofs+i]) {
5087cda8ba9STakashi Iwai 			spec->vol[ofs+i] = vol;
509aed058e8SSimone Zinanni 			idx  = WM_DAC_ATTEN + ofs + i;
5107cda8ba9STakashi Iwai 			wm_set_vol(ice, idx, spec->vol[ofs+i],
5117cda8ba9STakashi Iwai 				   spec->master[i]);
512aed058e8SSimone Zinanni 			change = 1;
513aed058e8SSimone Zinanni 		}
514aed058e8SSimone Zinanni 	}
515aed058e8SSimone Zinanni 	snd_ice1712_restore_gpio_status(ice);
516aed058e8SSimone Zinanni 	return change;
517aed058e8SSimone Zinanni }
518aed058e8SSimone Zinanni 
519aed058e8SSimone Zinanni /*
520aed058e8SSimone Zinanni  * WM8770 mute control
521aed058e8SSimone Zinanni  */
wm_mute_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)522cc67b7f7SVedran Miletic static int wm_mute_info(struct snd_kcontrol *kcontrol,
523cc67b7f7SVedran Miletic 			struct snd_ctl_elem_info *uinfo) {
524aed058e8SSimone Zinanni 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
525aed058e8SSimone Zinanni 	uinfo->count = kcontrol->private_value >> 8;
526aed058e8SSimone Zinanni 	uinfo->value.integer.min = 0;
527aed058e8SSimone Zinanni 	uinfo->value.integer.max = 1;
528aed058e8SSimone Zinanni 	return 0;
529aed058e8SSimone Zinanni }
530aed058e8SSimone Zinanni 
wm_mute_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)531cc67b7f7SVedran Miletic static int wm_mute_get(struct snd_kcontrol *kcontrol,
532cc67b7f7SVedran Miletic 			struct snd_ctl_elem_value *ucontrol)
533aed058e8SSimone Zinanni {
534ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
5357cda8ba9STakashi Iwai 	struct phase28_spec *spec = ice->spec;
536aed058e8SSimone Zinanni 	int voices, ofs, i;
537aed058e8SSimone Zinanni 
538aed058e8SSimone Zinanni 	voices = kcontrol->private_value >> 8;
539aed058e8SSimone Zinanni 	ofs = kcontrol->private_value & 0xFF;
540aed058e8SSimone Zinanni 
541aed058e8SSimone Zinanni 	for (i = 0; i < voices; i++)
5427cda8ba9STakashi Iwai 		ucontrol->value.integer.value[i] =
5437cda8ba9STakashi Iwai 			(spec->vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
544aed058e8SSimone Zinanni 	return 0;
545aed058e8SSimone Zinanni }
546aed058e8SSimone Zinanni 
wm_mute_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)547cc67b7f7SVedran Miletic static int wm_mute_put(struct snd_kcontrol *kcontrol,
548cc67b7f7SVedran Miletic 			struct snd_ctl_elem_value *ucontrol)
549aed058e8SSimone Zinanni {
550ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
5517cda8ba9STakashi Iwai 	struct phase28_spec *spec = ice->spec;
552aed058e8SSimone Zinanni 	int change = 0, voices, ofs, i;
553aed058e8SSimone Zinanni 
554aed058e8SSimone Zinanni 	voices = kcontrol->private_value >> 8;
555aed058e8SSimone Zinanni 	ofs = kcontrol->private_value & 0xFF;
556aed058e8SSimone Zinanni 
557aed058e8SSimone Zinanni 	snd_ice1712_save_gpio_status(ice);
558aed058e8SSimone Zinanni 	for (i = 0; i < voices; i++) {
5597cda8ba9STakashi Iwai 		int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
560aed058e8SSimone Zinanni 		if (ucontrol->value.integer.value[i] != val) {
5617cda8ba9STakashi Iwai 			spec->vol[ofs + i] &= ~WM_VOL_MUTE;
5627cda8ba9STakashi Iwai 			spec->vol[ofs + i] |=
563cc67b7f7SVedran Miletic 				ucontrol->value.integer.value[i] ? 0 :
564cc67b7f7SVedran Miletic 				WM_VOL_MUTE;
5657cda8ba9STakashi Iwai 			wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
5667cda8ba9STakashi Iwai 					spec->master[i]);
567aed058e8SSimone Zinanni 			change = 1;
568aed058e8SSimone Zinanni 		}
569aed058e8SSimone Zinanni 	}
570aed058e8SSimone Zinanni 	snd_ice1712_restore_gpio_status(ice);
571aed058e8SSimone Zinanni 
572aed058e8SSimone Zinanni 	return change;
573aed058e8SSimone Zinanni }
574aed058e8SSimone Zinanni 
575aed058e8SSimone Zinanni /*
576aed058e8SSimone Zinanni  * WM8770 master mute control
577aed058e8SSimone Zinanni  */
578a5ce8890STakashi Iwai #define wm_master_mute_info		snd_ctl_boolean_stereo_info
579aed058e8SSimone Zinanni 
wm_master_mute_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)580cc67b7f7SVedran Miletic static int wm_master_mute_get(struct snd_kcontrol *kcontrol,
581cc67b7f7SVedran Miletic 				struct snd_ctl_elem_value *ucontrol)
582aed058e8SSimone Zinanni {
583ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
5847cda8ba9STakashi Iwai 	struct phase28_spec *spec = ice->spec;
585aed058e8SSimone Zinanni 
5867cda8ba9STakashi Iwai 	ucontrol->value.integer.value[0] =
5877cda8ba9STakashi Iwai 		(spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
5887cda8ba9STakashi Iwai 	ucontrol->value.integer.value[1] =
5897cda8ba9STakashi Iwai 		(spec->master[1] & WM_VOL_MUTE) ? 0 : 1;
590aed058e8SSimone Zinanni 	return 0;
591aed058e8SSimone Zinanni }
592aed058e8SSimone Zinanni 
wm_master_mute_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)593cc67b7f7SVedran Miletic static int wm_master_mute_put(struct snd_kcontrol *kcontrol,
594cc67b7f7SVedran Miletic 				struct snd_ctl_elem_value *ucontrol)
595aed058e8SSimone Zinanni {
596ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
5977cda8ba9STakashi Iwai 	struct phase28_spec *spec = ice->spec;
598aed058e8SSimone Zinanni 	int change = 0, i;
599aed058e8SSimone Zinanni 
600aed058e8SSimone Zinanni 	snd_ice1712_save_gpio_status(ice);
601aed058e8SSimone Zinanni 	for (i = 0; i < 2; i++) {
6027cda8ba9STakashi Iwai 		int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1;
603aed058e8SSimone Zinanni 		if (ucontrol->value.integer.value[i] != val) {
604aed058e8SSimone Zinanni 			int dac;
6057cda8ba9STakashi Iwai 			spec->master[i] &= ~WM_VOL_MUTE;
6067cda8ba9STakashi Iwai 			spec->master[i] |=
607cc67b7f7SVedran Miletic 				ucontrol->value.integer.value[i] ? 0 :
608cc67b7f7SVedran Miletic 				WM_VOL_MUTE;
609aed058e8SSimone Zinanni 			for (dac = 0; dac < ice->num_total_dacs; dac += 2)
610aed058e8SSimone Zinanni 				wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
6117cda8ba9STakashi Iwai 						spec->vol[dac + i],
6127cda8ba9STakashi Iwai 						spec->master[i]);
613aed058e8SSimone Zinanni 			change = 1;
614aed058e8SSimone Zinanni 		}
615aed058e8SSimone Zinanni 	}
616aed058e8SSimone Zinanni 	snd_ice1712_restore_gpio_status(ice);
617aed058e8SSimone Zinanni 
618aed058e8SSimone Zinanni 	return change;
619aed058e8SSimone Zinanni }
620aed058e8SSimone Zinanni 
621aed058e8SSimone Zinanni /* digital master volume */
622aed058e8SSimone Zinanni #define PCM_0dB 0xff
623aed058e8SSimone Zinanni #define PCM_RES 128	/* -64dB */
624aed058e8SSimone Zinanni #define PCM_MIN (PCM_0dB - PCM_RES)
wm_pcm_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)625cc67b7f7SVedran Miletic static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol,
626cc67b7f7SVedran Miletic 				struct snd_ctl_elem_info *uinfo)
627aed058e8SSimone Zinanni {
628aed058e8SSimone Zinanni 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
629aed058e8SSimone Zinanni 	uinfo->count = 1;
630aed058e8SSimone Zinanni 	uinfo->value.integer.min = 0;		/* mute (-64dB) */
631aed058e8SSimone Zinanni 	uinfo->value.integer.max = PCM_RES;	/* 0dB */
632aed058e8SSimone Zinanni 	return 0;
633aed058e8SSimone Zinanni }
634aed058e8SSimone Zinanni 
wm_pcm_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)635cc67b7f7SVedran Miletic static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol,
636cc67b7f7SVedran Miletic 				struct snd_ctl_elem_value *ucontrol)
637aed058e8SSimone Zinanni {
638ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
639aed058e8SSimone Zinanni 	unsigned short val;
640aed058e8SSimone Zinanni 
64162932df8SIngo Molnar 	mutex_lock(&ice->gpio_mutex);
642aed058e8SSimone Zinanni 	val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
643aed058e8SSimone Zinanni 	val = val > PCM_MIN ? (val - PCM_MIN) : 0;
644aed058e8SSimone Zinanni 	ucontrol->value.integer.value[0] = val;
64562932df8SIngo Molnar 	mutex_unlock(&ice->gpio_mutex);
646aed058e8SSimone Zinanni 	return 0;
647aed058e8SSimone Zinanni }
648aed058e8SSimone Zinanni 
wm_pcm_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)649cc67b7f7SVedran Miletic static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol,
650cc67b7f7SVedran Miletic 				struct snd_ctl_elem_value *ucontrol)
651aed058e8SSimone Zinanni {
652ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
653aed058e8SSimone Zinanni 	unsigned short ovol, nvol;
654aed058e8SSimone Zinanni 	int change = 0;
655aed058e8SSimone Zinanni 
656aed058e8SSimone Zinanni 	nvol = ucontrol->value.integer.value[0];
6579cd17cd2STakashi Iwai 	if (nvol > PCM_RES)
6589cd17cd2STakashi Iwai 		return -EINVAL;
6599cd17cd2STakashi Iwai 	snd_ice1712_save_gpio_status(ice);
660aed058e8SSimone Zinanni 	nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
661aed058e8SSimone Zinanni 	ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
662aed058e8SSimone Zinanni 	if (ovol != nvol) {
663aed058e8SSimone Zinanni 		wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
664cc67b7f7SVedran Miletic 		/* update */
665cc67b7f7SVedran Miletic 		wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100);
666aed058e8SSimone Zinanni 		change = 1;
667aed058e8SSimone Zinanni 	}
668aed058e8SSimone Zinanni 	snd_ice1712_restore_gpio_status(ice);
669aed058e8SSimone Zinanni 	return change;
670aed058e8SSimone Zinanni }
671aed058e8SSimone Zinanni 
672aed058e8SSimone Zinanni /*
673aed058e8SSimone Zinanni  * Deemphasis
674aed058e8SSimone Zinanni  */
675a5ce8890STakashi Iwai #define phase28_deemp_info	snd_ctl_boolean_mono_info
676aed058e8SSimone Zinanni 
phase28_deemp_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)677cc67b7f7SVedran Miletic static int phase28_deemp_get(struct snd_kcontrol *kcontrol,
678cc67b7f7SVedran Miletic 				struct snd_ctl_elem_value *ucontrol)
679aed058e8SSimone Zinanni {
680ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
681cc67b7f7SVedran Miletic 	ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) ==
682cc67b7f7SVedran Miletic 						0xf;
683aed058e8SSimone Zinanni 	return 0;
684aed058e8SSimone Zinanni }
685aed058e8SSimone Zinanni 
phase28_deemp_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)686cc67b7f7SVedran Miletic static int phase28_deemp_put(struct snd_kcontrol *kcontrol,
687cc67b7f7SVedran Miletic 				struct snd_ctl_elem_value *ucontrol)
688aed058e8SSimone Zinanni {
689ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
690aed058e8SSimone Zinanni 	int temp, temp2;
691cc67b7f7SVedran Miletic 	temp = wm_get(ice, WM_DAC_CTRL2);
692cc67b7f7SVedran Miletic 	temp2 = temp;
693aed058e8SSimone Zinanni 	if (ucontrol->value.integer.value[0])
694aed058e8SSimone Zinanni 		temp |= 0xf;
695aed058e8SSimone Zinanni 	else
696aed058e8SSimone Zinanni 		temp &= ~0xf;
697aed058e8SSimone Zinanni 	if (temp != temp2) {
698aed058e8SSimone Zinanni 		wm_put(ice, WM_DAC_CTRL2, temp);
699aed058e8SSimone Zinanni 		return 1;
700aed058e8SSimone Zinanni 	}
701aed058e8SSimone Zinanni 	return 0;
702aed058e8SSimone Zinanni }
703aed058e8SSimone Zinanni 
704aed058e8SSimone Zinanni /*
705aed058e8SSimone Zinanni  * ADC Oversampling
706aed058e8SSimone Zinanni  */
phase28_oversampling_info(struct snd_kcontrol * k,struct snd_ctl_elem_info * uinfo)707cc67b7f7SVedran Miletic static int phase28_oversampling_info(struct snd_kcontrol *k,
708cc67b7f7SVedran Miletic 					struct snd_ctl_elem_info *uinfo)
709aed058e8SSimone Zinanni {
710a2af050fSTakashi Iwai 	static const char * const texts[2] = { "128x", "64x"	};
711aed058e8SSimone Zinanni 
712597da2e4STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, 2, texts);
713aed058e8SSimone Zinanni }
714aed058e8SSimone Zinanni 
phase28_oversampling_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)715cc67b7f7SVedran Miletic static int phase28_oversampling_get(struct snd_kcontrol *kcontrol,
716cc67b7f7SVedran Miletic 					struct snd_ctl_elem_value *ucontrol)
717aed058e8SSimone Zinanni {
718ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
719cc67b7f7SVedran Miletic 	ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) ==
720cc67b7f7SVedran Miletic 						0x8;
721aed058e8SSimone Zinanni 	return 0;
722aed058e8SSimone Zinanni }
723aed058e8SSimone Zinanni 
phase28_oversampling_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)724cc67b7f7SVedran Miletic static int phase28_oversampling_put(struct snd_kcontrol *kcontrol,
725cc67b7f7SVedran Miletic 					struct snd_ctl_elem_value *ucontrol)
726aed058e8SSimone Zinanni {
727aed058e8SSimone Zinanni 	int temp, temp2;
728ab0c7d72STakashi Iwai 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
729aed058e8SSimone Zinanni 
730cc67b7f7SVedran Miletic 	temp = wm_get(ice, WM_MASTER);
731cc67b7f7SVedran Miletic 	temp2 = temp;
732aed058e8SSimone Zinanni 
733aed058e8SSimone Zinanni 	if (ucontrol->value.enumerated.item[0])
734aed058e8SSimone Zinanni 		temp |= 0x8;
735aed058e8SSimone Zinanni 	else
736aed058e8SSimone Zinanni 		temp &= ~0x8;
737aed058e8SSimone Zinanni 
738aed058e8SSimone Zinanni 	if (temp != temp2) {
739aed058e8SSimone Zinanni 		wm_put(ice, WM_MASTER, temp);
740aed058e8SSimone Zinanni 		return 1;
741aed058e8SSimone Zinanni 	}
742aed058e8SSimone Zinanni 	return 0;
743aed058e8SSimone Zinanni }
744aed058e8SSimone Zinanni 
7450cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
7460cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
747f640c320STakashi Iwai 
748b4e5e707STakashi Iwai static const struct snd_kcontrol_new phase28_dac_controls[] = {
749aed058e8SSimone Zinanni 	{
750aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
751aed058e8SSimone Zinanni 		.name = "Master Playback Switch",
752aed058e8SSimone Zinanni 		.info = wm_master_mute_info,
753aed058e8SSimone Zinanni 		.get = wm_master_mute_get,
754aed058e8SSimone Zinanni 		.put = wm_master_mute_put
755aed058e8SSimone Zinanni 	},
756aed058e8SSimone Zinanni 	{
757aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
758f640c320STakashi Iwai 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
759f640c320STakashi Iwai 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
760aed058e8SSimone Zinanni 		.name = "Master Playback Volume",
761aed058e8SSimone Zinanni 		.info = wm_master_vol_info,
762aed058e8SSimone Zinanni 		.get = wm_master_vol_get,
763f640c320STakashi Iwai 		.put = wm_master_vol_put,
764f640c320STakashi Iwai 		.tlv = { .p = db_scale_wm_dac }
765aed058e8SSimone Zinanni 	},
766aed058e8SSimone Zinanni 	{
767aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
768aed058e8SSimone Zinanni 		.name = "Front Playback Switch",
769aed058e8SSimone Zinanni 		.info = wm_mute_info,
770aed058e8SSimone Zinanni 		.get = wm_mute_get,
771aed058e8SSimone Zinanni 		.put = wm_mute_put,
772aed058e8SSimone Zinanni 		.private_value = (2 << 8) | 0
773aed058e8SSimone Zinanni 	},
774aed058e8SSimone Zinanni 	{
775aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
776f640c320STakashi Iwai 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
777f640c320STakashi Iwai 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
778aed058e8SSimone Zinanni 		.name = "Front Playback Volume",
779aed058e8SSimone Zinanni 		.info = wm_vol_info,
780aed058e8SSimone Zinanni 		.get = wm_vol_get,
781aed058e8SSimone Zinanni 		.put = wm_vol_put,
782f640c320STakashi Iwai 		.private_value = (2 << 8) | 0,
783f640c320STakashi Iwai 		.tlv = { .p = db_scale_wm_dac }
784aed058e8SSimone Zinanni 	},
785aed058e8SSimone Zinanni 	{
786aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
787aed058e8SSimone Zinanni 		.name = "Rear Playback Switch",
788aed058e8SSimone Zinanni 		.info = wm_mute_info,
789aed058e8SSimone Zinanni 		.get = wm_mute_get,
790aed058e8SSimone Zinanni 		.put = wm_mute_put,
791aed058e8SSimone Zinanni 		.private_value = (2 << 8) | 2
792aed058e8SSimone Zinanni 	},
793aed058e8SSimone Zinanni 	{
794aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
795f640c320STakashi Iwai 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
796f640c320STakashi Iwai 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
797aed058e8SSimone Zinanni 		.name = "Rear Playback Volume",
798aed058e8SSimone Zinanni 		.info = wm_vol_info,
799aed058e8SSimone Zinanni 		.get = wm_vol_get,
800aed058e8SSimone Zinanni 		.put = wm_vol_put,
801f640c320STakashi Iwai 		.private_value = (2 << 8) | 2,
802f640c320STakashi Iwai 		.tlv = { .p = db_scale_wm_dac }
803aed058e8SSimone Zinanni 	},
804aed058e8SSimone Zinanni 	{
805aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
806aed058e8SSimone Zinanni 		.name = "Center Playback Switch",
807aed058e8SSimone Zinanni 		.info = wm_mute_info,
808aed058e8SSimone Zinanni 		.get = wm_mute_get,
809aed058e8SSimone Zinanni 		.put = wm_mute_put,
810aed058e8SSimone Zinanni 		.private_value = (1 << 8) | 4
811aed058e8SSimone Zinanni 	},
812aed058e8SSimone Zinanni 	{
813aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
814f640c320STakashi Iwai 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
815f640c320STakashi Iwai 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
816aed058e8SSimone Zinanni 		.name = "Center Playback Volume",
817aed058e8SSimone Zinanni 		.info = wm_vol_info,
818aed058e8SSimone Zinanni 		.get = wm_vol_get,
819aed058e8SSimone Zinanni 		.put = wm_vol_put,
820f640c320STakashi Iwai 		.private_value = (1 << 8) | 4,
821f640c320STakashi Iwai 		.tlv = { .p = db_scale_wm_dac }
822aed058e8SSimone Zinanni 	},
823aed058e8SSimone Zinanni 	{
824aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
825aed058e8SSimone Zinanni 		.name = "LFE Playback Switch",
826aed058e8SSimone Zinanni 		.info = wm_mute_info,
827aed058e8SSimone Zinanni 		.get = wm_mute_get,
828aed058e8SSimone Zinanni 		.put = wm_mute_put,
829aed058e8SSimone Zinanni 		.private_value = (1 << 8) | 5
830aed058e8SSimone Zinanni 	},
831aed058e8SSimone Zinanni 	{
832aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
833f640c320STakashi Iwai 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
834f640c320STakashi Iwai 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
835aed058e8SSimone Zinanni 		.name = "LFE Playback Volume",
836aed058e8SSimone Zinanni 		.info = wm_vol_info,
837aed058e8SSimone Zinanni 		.get = wm_vol_get,
838aed058e8SSimone Zinanni 		.put = wm_vol_put,
839f640c320STakashi Iwai 		.private_value = (1 << 8) | 5,
840f640c320STakashi Iwai 		.tlv = { .p = db_scale_wm_dac }
841aed058e8SSimone Zinanni 	},
842aed058e8SSimone Zinanni 	{
843aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
844aed058e8SSimone Zinanni 		.name = "Side Playback Switch",
845aed058e8SSimone Zinanni 		.info = wm_mute_info,
846aed058e8SSimone Zinanni 		.get = wm_mute_get,
847aed058e8SSimone Zinanni 		.put = wm_mute_put,
848aed058e8SSimone Zinanni 		.private_value = (2 << 8) | 6
849aed058e8SSimone Zinanni 	},
850aed058e8SSimone Zinanni 	{
851aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
852f640c320STakashi Iwai 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
853f640c320STakashi Iwai 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
854aed058e8SSimone Zinanni 		.name = "Side Playback Volume",
855aed058e8SSimone Zinanni 		.info = wm_vol_info,
856aed058e8SSimone Zinanni 		.get = wm_vol_get,
857aed058e8SSimone Zinanni 		.put = wm_vol_put,
858f640c320STakashi Iwai 		.private_value = (2 << 8) | 6,
859f640c320STakashi Iwai 		.tlv = { .p = db_scale_wm_dac }
860aed058e8SSimone Zinanni 	}
861aed058e8SSimone Zinanni };
862aed058e8SSimone Zinanni 
863b4e5e707STakashi Iwai static const struct snd_kcontrol_new wm_controls[] = {
864aed058e8SSimone Zinanni 	{
865aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
866aed058e8SSimone Zinanni 		.name = "PCM Playback Switch",
867aed058e8SSimone Zinanni 		.info = wm_pcm_mute_info,
868aed058e8SSimone Zinanni 		.get = wm_pcm_mute_get,
869aed058e8SSimone Zinanni 		.put = wm_pcm_mute_put
870aed058e8SSimone Zinanni 	},
871aed058e8SSimone Zinanni 	{
872aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
873f640c320STakashi Iwai 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
874f640c320STakashi Iwai 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
875aed058e8SSimone Zinanni 		.name = "PCM Playback Volume",
876aed058e8SSimone Zinanni 		.info = wm_pcm_vol_info,
877aed058e8SSimone Zinanni 		.get = wm_pcm_vol_get,
878f640c320STakashi Iwai 		.put = wm_pcm_vol_put,
879f640c320STakashi Iwai 		.tlv = { .p = db_scale_wm_pcm }
880aed058e8SSimone Zinanni 	},
881aed058e8SSimone Zinanni 	{
882aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
883aed058e8SSimone Zinanni 		.name = "DAC Deemphasis Switch",
884aed058e8SSimone Zinanni 		.info = phase28_deemp_info,
885aed058e8SSimone Zinanni 		.get = phase28_deemp_get,
886aed058e8SSimone Zinanni 		.put = phase28_deemp_put
887aed058e8SSimone Zinanni 	},
888aed058e8SSimone Zinanni 	{
889aed058e8SSimone Zinanni 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
890aed058e8SSimone Zinanni 		.name = "ADC Oversampling",
891aed058e8SSimone Zinanni 		.info = phase28_oversampling_info,
892aed058e8SSimone Zinanni 		.get = phase28_oversampling_get,
893aed058e8SSimone Zinanni 		.put = phase28_oversampling_put
894aed058e8SSimone Zinanni 	}
895aed058e8SSimone Zinanni };
896aed058e8SSimone Zinanni 
phase28_add_controls(struct snd_ice1712 * ice)897e23e7a14SBill Pemberton static int phase28_add_controls(struct snd_ice1712 *ice)
898aed058e8SSimone Zinanni {
899aed058e8SSimone Zinanni 	unsigned int i, counts;
900aed058e8SSimone Zinanni 	int err;
901aed058e8SSimone Zinanni 
902aed058e8SSimone Zinanni 	counts = ARRAY_SIZE(phase28_dac_controls);
903aed058e8SSimone Zinanni 	for (i = 0; i < counts; i++) {
904cc67b7f7SVedran Miletic 		err = snd_ctl_add(ice->card,
905cc67b7f7SVedran Miletic 					snd_ctl_new1(&phase28_dac_controls[i],
906cc67b7f7SVedran Miletic 							ice));
907aed058e8SSimone Zinanni 		if (err < 0)
908aed058e8SSimone Zinanni 			return err;
909aed058e8SSimone Zinanni 	}
910aed058e8SSimone Zinanni 
911aed058e8SSimone Zinanni 	for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
912cc67b7f7SVedran Miletic 		err = snd_ctl_add(ice->card,
913cc67b7f7SVedran Miletic 					snd_ctl_new1(&wm_controls[i], ice));
914aed058e8SSimone Zinanni 		if (err < 0)
915aed058e8SSimone Zinanni 			return err;
916aed058e8SSimone Zinanni 	}
917aed058e8SSimone Zinanni 
918aed058e8SSimone Zinanni 	return 0;
919aed058e8SSimone Zinanni }
920aed058e8SSimone Zinanni 
921e23e7a14SBill Pemberton struct snd_ice1712_card_info snd_vt1724_phase_cards[] = {
9221da177e4SLinus Torvalds 	{
9231da177e4SLinus Torvalds 		.subvendor = VT1724_SUBDEVICE_PHASE22,
9241da177e4SLinus Torvalds 		.name = "Terratec PHASE 22",
9251da177e4SLinus Torvalds 		.model = "phase22",
9261da177e4SLinus Torvalds 		.chip_init = phase22_init,
9271da177e4SLinus Torvalds 		.build_controls = phase22_add_controls,
9281da177e4SLinus Torvalds 		.eeprom_size = sizeof(phase22_eeprom),
9291da177e4SLinus Torvalds 		.eeprom_data = phase22_eeprom,
9301da177e4SLinus Torvalds 	},
931aed058e8SSimone Zinanni 	{
932aed058e8SSimone Zinanni 		.subvendor = VT1724_SUBDEVICE_PHASE28,
933aed058e8SSimone Zinanni 		.name = "Terratec PHASE 28",
934aed058e8SSimone Zinanni 		.model = "phase28",
935aed058e8SSimone Zinanni 		.chip_init = phase28_init,
936aed058e8SSimone Zinanni 		.build_controls = phase28_add_controls,
937aed058e8SSimone Zinanni 		.eeprom_size = sizeof(phase28_eeprom),
938aed058e8SSimone Zinanni 		.eeprom_data = phase28_eeprom,
939aed058e8SSimone Zinanni 	},
940740dc9c4SMisha Zhilin 	{
941740dc9c4SMisha Zhilin 		.subvendor = VT1724_SUBDEVICE_TS22,
942740dc9c4SMisha Zhilin 		.name = "Terrasoniq TS22 PCI",
943740dc9c4SMisha Zhilin 		.model = "TS22",
944740dc9c4SMisha Zhilin 		.chip_init = phase22_init,
945740dc9c4SMisha Zhilin 		.build_controls = phase22_add_controls,
946740dc9c4SMisha Zhilin 		.eeprom_size = sizeof(phase22_eeprom),
947740dc9c4SMisha Zhilin 		.eeprom_data = phase22_eeprom,
948740dc9c4SMisha Zhilin 	},
9491da177e4SLinus Torvalds 	{ } /* terminator */
9501da177e4SLinus Torvalds };
951