xref: /linux/sound/pci/ice1712/prodigy_hifi.c (revision c7561cd80469f2fe4a6be0984db57832ee7f2a3b)
16b8d6e55SJulian Scheel /*
26b8d6e55SJulian Scheel  *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
36b8d6e55SJulian Scheel  *
46b8d6e55SJulian Scheel  *   Lowlevel functions for Audiotrak Prodigy 7.1 Hifi
56b8d6e55SJulian Scheel  *   based on pontis.c
66b8d6e55SJulian Scheel  *
76b8d6e55SJulian Scheel  *      Copyright (c) 2007 Julian Scheel <julian@jusst.de>
86b8d6e55SJulian Scheel  *      Copyright (c) 2007 allank
96b8d6e55SJulian Scheel  *      Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
106b8d6e55SJulian Scheel  *
116b8d6e55SJulian Scheel  *   This program is free software; you can redistribute it and/or modify
126b8d6e55SJulian Scheel  *   it under the terms of the GNU General Public License as published by
136b8d6e55SJulian Scheel  *   the Free Software Foundation; either version 2 of the License, or
146b8d6e55SJulian Scheel  *   (at your option) any later version.
156b8d6e55SJulian Scheel  *
166b8d6e55SJulian Scheel  *   This program is distributed in the hope that it will be useful,
176b8d6e55SJulian Scheel  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
186b8d6e55SJulian Scheel  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
196b8d6e55SJulian Scheel  *   GNU General Public License for more details.
206b8d6e55SJulian Scheel  *
216b8d6e55SJulian Scheel  *   You should have received a copy of the GNU General Public License
226b8d6e55SJulian Scheel  *   along with this program; if not, write to the Free Software
236b8d6e55SJulian Scheel  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
246b8d6e55SJulian Scheel  *
256b8d6e55SJulian Scheel  */
266b8d6e55SJulian Scheel 
276b8d6e55SJulian Scheel 
286b8d6e55SJulian Scheel #include <asm/io.h>
296b8d6e55SJulian Scheel #include <linux/delay.h>
306b8d6e55SJulian Scheel #include <linux/interrupt.h>
316b8d6e55SJulian Scheel #include <linux/init.h>
326b8d6e55SJulian Scheel #include <linux/slab.h>
336b8d6e55SJulian Scheel #include <linux/mutex.h>
346b8d6e55SJulian Scheel 
356b8d6e55SJulian Scheel #include <sound/core.h>
366b8d6e55SJulian Scheel #include <sound/info.h>
376b8d6e55SJulian Scheel #include <sound/tlv.h>
386b8d6e55SJulian Scheel 
396b8d6e55SJulian Scheel #include "ice1712.h"
406b8d6e55SJulian Scheel #include "envy24ht.h"
416b8d6e55SJulian Scheel #include "prodigy_hifi.h"
426b8d6e55SJulian Scheel 
437cda8ba9STakashi Iwai struct prodigy_hifi_spec {
447cda8ba9STakashi Iwai 	unsigned short master[2];
457cda8ba9STakashi Iwai 	unsigned short vol[8];
467cda8ba9STakashi Iwai };
477cda8ba9STakashi Iwai 
486b8d6e55SJulian Scheel /* I2C addresses */
496b8d6e55SJulian Scheel #define WM_DEV		0x34
506b8d6e55SJulian Scheel 
516b8d6e55SJulian Scheel /* WM8776 registers */
526b8d6e55SJulian Scheel #define WM_HP_ATTEN_L		0x00	/* headphone left attenuation */
536b8d6e55SJulian Scheel #define WM_HP_ATTEN_R		0x01	/* headphone left attenuation */
546b8d6e55SJulian Scheel #define WM_HP_MASTER		0x02	/* headphone master (both channels),
556b8d6e55SJulian Scheel 						override LLR */
566b8d6e55SJulian Scheel #define WM_DAC_ATTEN_L		0x03	/* digital left attenuation */
576b8d6e55SJulian Scheel #define WM_DAC_ATTEN_R		0x04
586b8d6e55SJulian Scheel #define WM_DAC_MASTER		0x05
596b8d6e55SJulian Scheel #define WM_PHASE_SWAP		0x06	/* DAC phase swap */
606b8d6e55SJulian Scheel #define WM_DAC_CTRL1		0x07
616b8d6e55SJulian Scheel #define WM_DAC_MUTE		0x08
626b8d6e55SJulian Scheel #define WM_DAC_CTRL2		0x09
636b8d6e55SJulian Scheel #define WM_DAC_INT		0x0a
646b8d6e55SJulian Scheel #define WM_ADC_INT		0x0b
656b8d6e55SJulian Scheel #define WM_MASTER_CTRL		0x0c
666b8d6e55SJulian Scheel #define WM_POWERDOWN		0x0d
676b8d6e55SJulian Scheel #define WM_ADC_ATTEN_L		0x0e
686b8d6e55SJulian Scheel #define WM_ADC_ATTEN_R		0x0f
696b8d6e55SJulian Scheel #define WM_ALC_CTRL1		0x10
706b8d6e55SJulian Scheel #define WM_ALC_CTRL2		0x11
716b8d6e55SJulian Scheel #define WM_ALC_CTRL3		0x12
726b8d6e55SJulian Scheel #define WM_NOISE_GATE		0x13
736b8d6e55SJulian Scheel #define WM_LIMITER		0x14
746b8d6e55SJulian Scheel #define WM_ADC_MUX		0x15
756b8d6e55SJulian Scheel #define WM_OUT_MUX		0x16
766b8d6e55SJulian Scheel #define WM_RESET		0x17
776b8d6e55SJulian Scheel 
786b8d6e55SJulian Scheel /* Analog Recording Source :- Mic, LineIn, CD/Video, */
796b8d6e55SJulian Scheel 
806b8d6e55SJulian Scheel /* implement capture source select control for WM8776 */
816b8d6e55SJulian Scheel 
826b8d6e55SJulian Scheel #define WM_AIN1 "AIN1"
836b8d6e55SJulian Scheel #define WM_AIN2 "AIN2"
846b8d6e55SJulian Scheel #define WM_AIN3 "AIN3"
856b8d6e55SJulian Scheel #define WM_AIN4 "AIN4"
866b8d6e55SJulian Scheel #define WM_AIN5 "AIN5"
876b8d6e55SJulian Scheel 
886b8d6e55SJulian Scheel /* GPIO pins of envy24ht connected to wm8766 */
896b8d6e55SJulian Scheel #define WM8766_SPI_CLK	 (1<<17) /* CLK, Pin97 on ICE1724 */
906b8d6e55SJulian Scheel #define WM8766_SPI_MD	  (1<<16) /* DATA VT1724 -> WM8766, Pin96 */
916b8d6e55SJulian Scheel #define WM8766_SPI_ML	  (1<<18) /* Latch, Pin98 */
926b8d6e55SJulian Scheel 
936b8d6e55SJulian Scheel /* WM8766 registers */
946b8d6e55SJulian Scheel #define WM8766_DAC_CTRL	 0x02   /* DAC Control */
956b8d6e55SJulian Scheel #define WM8766_INT_CTRL	 0x03   /* Interface Control */
966b8d6e55SJulian Scheel #define WM8766_DAC_CTRL2	0x09
976b8d6e55SJulian Scheel #define WM8766_DAC_CTRL3	0x0a
986b8d6e55SJulian Scheel #define WM8766_RESET	    0x1f
996b8d6e55SJulian Scheel #define WM8766_LDA1	     0x00
1006b8d6e55SJulian Scheel #define WM8766_LDA2	     0x04
1016b8d6e55SJulian Scheel #define WM8766_LDA3	     0x06
1026b8d6e55SJulian Scheel #define WM8766_RDA1	     0x01
1036b8d6e55SJulian Scheel #define WM8766_RDA2	     0x05
1046b8d6e55SJulian Scheel #define WM8766_RDA3	     0x07
1056b8d6e55SJulian Scheel #define WM8766_MUTE1	    0x0C
1066b8d6e55SJulian Scheel #define WM8766_MUTE2	    0x0F
1076b8d6e55SJulian Scheel 
1086b8d6e55SJulian Scheel 
1096b8d6e55SJulian Scheel /*
1106b8d6e55SJulian Scheel  * Prodigy HD2
1116b8d6e55SJulian Scheel  */
1126b8d6e55SJulian Scheel #define AK4396_ADDR    0x00
1136b8d6e55SJulian Scheel #define AK4396_CSN    (1 << 8)    /* CSN->GPIO8, pin 75 */
1146b8d6e55SJulian Scheel #define AK4396_CCLK   (1 << 9)    /* CCLK->GPIO9, pin 76 */
1156b8d6e55SJulian Scheel #define AK4396_CDTI   (1 << 10)   /* CDTI->GPIO10, pin 77 */
1166b8d6e55SJulian Scheel 
1176b8d6e55SJulian Scheel /* ak4396 registers */
1186b8d6e55SJulian Scheel #define AK4396_CTRL1	    0x00
1196b8d6e55SJulian Scheel #define AK4396_CTRL2	    0x01
1206b8d6e55SJulian Scheel #define AK4396_CTRL3	    0x02
1216b8d6e55SJulian Scheel #define AK4396_LCH_ATT	  0x03
1226b8d6e55SJulian Scheel #define AK4396_RCH_ATT	  0x04
1236b8d6e55SJulian Scheel 
1246b8d6e55SJulian Scheel 
1256b8d6e55SJulian Scheel /*
1266b8d6e55SJulian Scheel  * get the current register value of WM codec
1276b8d6e55SJulian Scheel  */
1286b8d6e55SJulian Scheel static unsigned short wm_get(struct snd_ice1712 *ice, int reg)
1296b8d6e55SJulian Scheel {
1306b8d6e55SJulian Scheel 	reg <<= 1;
1316b8d6e55SJulian Scheel 	return ((unsigned short)ice->akm[0].images[reg] << 8) |
1326b8d6e55SJulian Scheel 		ice->akm[0].images[reg + 1];
1336b8d6e55SJulian Scheel }
1346b8d6e55SJulian Scheel 
1356b8d6e55SJulian Scheel /*
1366b8d6e55SJulian Scheel  * set the register value of WM codec and remember it
1376b8d6e55SJulian Scheel  */
1386b8d6e55SJulian Scheel static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val)
1396b8d6e55SJulian Scheel {
1406b8d6e55SJulian Scheel 	unsigned short cval;
1416b8d6e55SJulian Scheel 	cval = (reg << 9) | val;
1426b8d6e55SJulian Scheel 	snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);
1436b8d6e55SJulian Scheel }
1446b8d6e55SJulian Scheel 
1456b8d6e55SJulian Scheel static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
1466b8d6e55SJulian Scheel {
1476b8d6e55SJulian Scheel 	wm_put_nocache(ice, reg, val);
1486b8d6e55SJulian Scheel 	reg <<= 1;
1496b8d6e55SJulian Scheel 	ice->akm[0].images[reg] = val >> 8;
1506b8d6e55SJulian Scheel 	ice->akm[0].images[reg + 1] = val;
1516b8d6e55SJulian Scheel }
1526b8d6e55SJulian Scheel 
1536b8d6e55SJulian Scheel /*
1546b8d6e55SJulian Scheel  * write data in the SPI mode
1556b8d6e55SJulian Scheel  */
1566b8d6e55SJulian Scheel 
1576b8d6e55SJulian Scheel static void set_gpio_bit(struct snd_ice1712 *ice, unsigned int bit, int val)
1586b8d6e55SJulian Scheel {
1596b8d6e55SJulian Scheel 	unsigned int tmp = snd_ice1712_gpio_read(ice);
1606b8d6e55SJulian Scheel 	if (val)
1616b8d6e55SJulian Scheel 		tmp |= bit;
1626b8d6e55SJulian Scheel 	else
1636b8d6e55SJulian Scheel 		tmp &= ~bit;
1646b8d6e55SJulian Scheel 	snd_ice1712_gpio_write(ice, tmp);
1656b8d6e55SJulian Scheel }
1666b8d6e55SJulian Scheel 
1676b8d6e55SJulian Scheel /*
1686b8d6e55SJulian Scheel  * SPI implementation for WM8766 codec - only writing supported, no readback
1696b8d6e55SJulian Scheel  */
1706b8d6e55SJulian Scheel 
1716b8d6e55SJulian Scheel static void wm8766_spi_send_word(struct snd_ice1712 *ice, unsigned int data)
1726b8d6e55SJulian Scheel {
1736b8d6e55SJulian Scheel 	int i;
1746b8d6e55SJulian Scheel 	for (i = 0; i < 16; i++) {
1756b8d6e55SJulian Scheel 		set_gpio_bit(ice, WM8766_SPI_CLK, 0);
1766b8d6e55SJulian Scheel 		udelay(1);
1776b8d6e55SJulian Scheel 		set_gpio_bit(ice, WM8766_SPI_MD, data & 0x8000);
1786b8d6e55SJulian Scheel 		udelay(1);
1796b8d6e55SJulian Scheel 		set_gpio_bit(ice, WM8766_SPI_CLK, 1);
1806b8d6e55SJulian Scheel 		udelay(1);
1816b8d6e55SJulian Scheel 		data <<= 1;
1826b8d6e55SJulian Scheel 	}
1836b8d6e55SJulian Scheel }
1846b8d6e55SJulian Scheel 
1857cda8ba9STakashi Iwai static void wm8766_spi_write(struct snd_ice1712 *ice, unsigned int reg,
1867cda8ba9STakashi Iwai 			     unsigned int data)
1876b8d6e55SJulian Scheel {
1886b8d6e55SJulian Scheel 	unsigned int block;
1896b8d6e55SJulian Scheel 
1906b8d6e55SJulian Scheel 	snd_ice1712_gpio_set_dir(ice, WM8766_SPI_MD|
1916b8d6e55SJulian Scheel 					WM8766_SPI_CLK|WM8766_SPI_ML);
1926b8d6e55SJulian Scheel 	snd_ice1712_gpio_set_mask(ice, ~(WM8766_SPI_MD|
1936b8d6e55SJulian Scheel 					WM8766_SPI_CLK|WM8766_SPI_ML));
1946b8d6e55SJulian Scheel 	/* latch must be low when writing */
1956b8d6e55SJulian Scheel 	set_gpio_bit(ice, WM8766_SPI_ML, 0);
1966b8d6e55SJulian Scheel 	block = (reg << 9) | (data & 0x1ff);
1976b8d6e55SJulian Scheel 	wm8766_spi_send_word(ice, block); /* REGISTER ADDRESS */
1986b8d6e55SJulian Scheel 	/* release latch */
1996b8d6e55SJulian Scheel 	set_gpio_bit(ice, WM8766_SPI_ML, 1);
2006b8d6e55SJulian Scheel 	udelay(1);
2016b8d6e55SJulian Scheel 	/* restore */
2026b8d6e55SJulian Scheel 	snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
2036b8d6e55SJulian Scheel 	snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
2046b8d6e55SJulian Scheel }
2056b8d6e55SJulian Scheel 
2066b8d6e55SJulian Scheel 
2076b8d6e55SJulian Scheel /*
2086b8d6e55SJulian Scheel  * serial interface for ak4396 - only writing supported, no readback
2096b8d6e55SJulian Scheel  */
2106b8d6e55SJulian Scheel 
2116b8d6e55SJulian Scheel static void ak4396_send_word(struct snd_ice1712 *ice, unsigned int data)
2126b8d6e55SJulian Scheel {
2136b8d6e55SJulian Scheel 	int i;
2146b8d6e55SJulian Scheel 	for (i = 0; i < 16; i++) {
2156b8d6e55SJulian Scheel 		set_gpio_bit(ice, AK4396_CCLK, 0);
2166b8d6e55SJulian Scheel 		udelay(1);
2176b8d6e55SJulian Scheel 		set_gpio_bit(ice, AK4396_CDTI, data & 0x8000);
2186b8d6e55SJulian Scheel 		udelay(1);
2196b8d6e55SJulian Scheel 		set_gpio_bit(ice, AK4396_CCLK, 1);
2206b8d6e55SJulian Scheel 		udelay(1);
2216b8d6e55SJulian Scheel 		data <<= 1;
2226b8d6e55SJulian Scheel 	}
2236b8d6e55SJulian Scheel }
2246b8d6e55SJulian Scheel 
2257cda8ba9STakashi Iwai static void ak4396_write(struct snd_ice1712 *ice, unsigned int reg,
2267cda8ba9STakashi Iwai 			 unsigned int data)
2276b8d6e55SJulian Scheel {
2286b8d6e55SJulian Scheel 	unsigned int block;
2296b8d6e55SJulian Scheel 
2306b8d6e55SJulian Scheel 	snd_ice1712_gpio_set_dir(ice, AK4396_CSN|AK4396_CCLK|AK4396_CDTI);
2316b8d6e55SJulian Scheel 	snd_ice1712_gpio_set_mask(ice, ~(AK4396_CSN|AK4396_CCLK|AK4396_CDTI));
2326b8d6e55SJulian Scheel 	/* latch must be low when writing */
2336b8d6e55SJulian Scheel 	set_gpio_bit(ice, AK4396_CSN, 0);
2346b8d6e55SJulian Scheel 	block =  ((AK4396_ADDR & 0x03) << 14) | (1 << 13) |
2356b8d6e55SJulian Scheel 			((reg & 0x1f) << 8) | (data & 0xff);
2366b8d6e55SJulian Scheel 	ak4396_send_word(ice, block); /* REGISTER ADDRESS */
2376b8d6e55SJulian Scheel 	/* release latch */
2386b8d6e55SJulian Scheel 	set_gpio_bit(ice, AK4396_CSN, 1);
2396b8d6e55SJulian Scheel 	udelay(1);
2406b8d6e55SJulian Scheel 	/* restore */
2416b8d6e55SJulian Scheel 	snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
2426b8d6e55SJulian Scheel 	snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
2436b8d6e55SJulian Scheel }
2446b8d6e55SJulian Scheel 
2456b8d6e55SJulian Scheel 
2466b8d6e55SJulian Scheel /*
2476b8d6e55SJulian Scheel  * ak4396 mixers
2486b8d6e55SJulian Scheel  */
2496b8d6e55SJulian Scheel 
2506b8d6e55SJulian Scheel 
2516b8d6e55SJulian Scheel 
2526b8d6e55SJulian Scheel /*
2536b8d6e55SJulian Scheel  * DAC volume attenuation mixer control (-64dB to 0dB)
2546b8d6e55SJulian Scheel  */
2556b8d6e55SJulian Scheel 
2567cda8ba9STakashi Iwai static int ak4396_dac_vol_info(struct snd_kcontrol *kcontrol,
2577cda8ba9STakashi Iwai 			       struct snd_ctl_elem_info *uinfo)
2586b8d6e55SJulian Scheel {
2596b8d6e55SJulian Scheel 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2606b8d6e55SJulian Scheel 	uinfo->count = 2;
2616b8d6e55SJulian Scheel 	uinfo->value.integer.min = 0;   /* mute */
2626b8d6e55SJulian Scheel 	uinfo->value.integer.max = 0xFF; /* linear */
2636b8d6e55SJulian Scheel 	return 0;
2646b8d6e55SJulian Scheel }
2656b8d6e55SJulian Scheel 
2667cda8ba9STakashi Iwai static int ak4396_dac_vol_get(struct snd_kcontrol *kcontrol,
2677cda8ba9STakashi Iwai 			      struct snd_ctl_elem_value *ucontrol)
2686b8d6e55SJulian Scheel {
2696b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
2707cda8ba9STakashi Iwai 	struct prodigy_hifi_spec *spec = ice->spec;
2716b8d6e55SJulian Scheel 	int i;
2726b8d6e55SJulian Scheel 
2737cda8ba9STakashi Iwai 	for (i = 0; i < 2; i++)
2747cda8ba9STakashi Iwai 		ucontrol->value.integer.value[i] = spec->vol[i];
2757cda8ba9STakashi Iwai 
2766b8d6e55SJulian Scheel 	return 0;
2776b8d6e55SJulian Scheel }
2786b8d6e55SJulian Scheel 
2796b8d6e55SJulian Scheel static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2806b8d6e55SJulian Scheel {
2816b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
2827cda8ba9STakashi Iwai 	struct prodigy_hifi_spec *spec = ice->spec;
2836b8d6e55SJulian Scheel 	int i;
2846b8d6e55SJulian Scheel 	int change = 0;
2856b8d6e55SJulian Scheel 
2866b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
2876b8d6e55SJulian Scheel 	for (i = 0; i < 2; i++) {
2887cda8ba9STakashi Iwai 		if (ucontrol->value.integer.value[i] != spec->vol[i]) {
2897cda8ba9STakashi Iwai 			spec->vol[i] = ucontrol->value.integer.value[i];
2906b8d6e55SJulian Scheel 			ak4396_write(ice, AK4396_LCH_ATT + i,
2917cda8ba9STakashi Iwai 				     spec->vol[i] & 0xff);
2926b8d6e55SJulian Scheel 			change = 1;
2936b8d6e55SJulian Scheel 		}
2946b8d6e55SJulian Scheel 	}
2956b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
2966b8d6e55SJulian Scheel 	return change;
2976b8d6e55SJulian Scheel }
2986b8d6e55SJulian Scheel 
2996b8d6e55SJulian Scheel static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
3006b8d6e55SJulian Scheel 
3016b8d6e55SJulian Scheel static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
3026b8d6e55SJulian Scheel     {
3036b8d6e55SJulian Scheel 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3046b8d6e55SJulian Scheel 	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
3056b8d6e55SJulian Scheel 		SNDRV_CTL_ELEM_ACCESS_TLV_READ),
3066b8d6e55SJulian Scheel 	.name = "Front Playback Volume",
3076b8d6e55SJulian Scheel 	.info = ak4396_dac_vol_info,
3086b8d6e55SJulian Scheel 	.get = ak4396_dac_vol_get,
3096b8d6e55SJulian Scheel 	.put = ak4396_dac_vol_put,
3106b8d6e55SJulian Scheel 	.tlv = { .p = db_scale_wm_dac },
3116b8d6e55SJulian Scheel     },
3126b8d6e55SJulian Scheel };
3136b8d6e55SJulian Scheel 
3146b8d6e55SJulian Scheel 
3156b8d6e55SJulian Scheel /* --------------- */
3166b8d6e55SJulian Scheel 
3176b8d6e55SJulian Scheel /*
3186b8d6e55SJulian Scheel  * Logarithmic volume values for WM87*6
3196b8d6e55SJulian Scheel  * Computed as 20 * Log10(255 / x)
3206b8d6e55SJulian Scheel  */
3216b8d6e55SJulian Scheel static const unsigned char wm_vol[256] = {
3226b8d6e55SJulian Scheel 	127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
3236b8d6e55SJulian Scheel 	23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
3246b8d6e55SJulian Scheel 	17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
3256b8d6e55SJulian Scheel 	13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
3266b8d6e55SJulian Scheel 	11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
3276b8d6e55SJulian Scheel 	8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
3286b8d6e55SJulian Scheel 	6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
3296b8d6e55SJulian Scheel 	5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
3306b8d6e55SJulian Scheel 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3316b8d6e55SJulian Scheel 	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3326b8d6e55SJulian Scheel 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3336b8d6e55SJulian Scheel 	0, 0
3346b8d6e55SJulian Scheel };
3356b8d6e55SJulian Scheel 
3366b8d6e55SJulian Scheel #define WM_VOL_MAX	(sizeof(wm_vol) - 1)
3376b8d6e55SJulian Scheel #define WM_VOL_MUTE	0x8000
3386b8d6e55SJulian Scheel 
3396b8d6e55SJulian Scheel 
3406b8d6e55SJulian Scheel #define DAC_0dB	0xff
3416b8d6e55SJulian Scheel #define DAC_RES	128
3426b8d6e55SJulian Scheel #define DAC_MIN	(DAC_0dB - DAC_RES)
3436b8d6e55SJulian Scheel 
3446b8d6e55SJulian Scheel 
3457cda8ba9STakashi Iwai static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index,
3467cda8ba9STakashi Iwai 		       unsigned short vol, unsigned short master)
3476b8d6e55SJulian Scheel {
3486b8d6e55SJulian Scheel 	unsigned char nvol;
3496b8d6e55SJulian Scheel 
3506b8d6e55SJulian Scheel 	if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
3516b8d6e55SJulian Scheel 		nvol = 0;
3526b8d6e55SJulian Scheel 	else {
3536b8d6e55SJulian Scheel 		nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128)
3546b8d6e55SJulian Scheel 				& WM_VOL_MAX;
3556b8d6e55SJulian Scheel 		nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff;
3566b8d6e55SJulian Scheel 	}
3576b8d6e55SJulian Scheel 
3586b8d6e55SJulian Scheel 	wm_put(ice, index, nvol);
3596b8d6e55SJulian Scheel 	wm_put_nocache(ice, index, 0x100 | nvol);
3606b8d6e55SJulian Scheel }
3616b8d6e55SJulian Scheel 
3627cda8ba9STakashi Iwai static void wm8766_set_vol(struct snd_ice1712 *ice, unsigned int index,
3637cda8ba9STakashi Iwai 			   unsigned short vol, unsigned short master)
3646b8d6e55SJulian Scheel {
3656b8d6e55SJulian Scheel 	unsigned char nvol;
3666b8d6e55SJulian Scheel 
3676b8d6e55SJulian Scheel 	if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
3686b8d6e55SJulian Scheel 		nvol = 0;
3696b8d6e55SJulian Scheel 	else {
3706b8d6e55SJulian Scheel 		nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128)
3716b8d6e55SJulian Scheel 				& WM_VOL_MAX;
3726b8d6e55SJulian Scheel 		nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff;
3736b8d6e55SJulian Scheel 	}
3746b8d6e55SJulian Scheel 
3756b8d6e55SJulian Scheel 	wm8766_spi_write(ice, index, (0x0100 | nvol));
3766b8d6e55SJulian Scheel }
3776b8d6e55SJulian Scheel 
3786b8d6e55SJulian Scheel 
3796b8d6e55SJulian Scheel /*
3806b8d6e55SJulian Scheel  * DAC volume attenuation mixer control (-64dB to 0dB)
3816b8d6e55SJulian Scheel  */
3826b8d6e55SJulian Scheel 
3837cda8ba9STakashi Iwai static int wm_dac_vol_info(struct snd_kcontrol *kcontrol,
3847cda8ba9STakashi Iwai 			   struct snd_ctl_elem_info *uinfo)
3856b8d6e55SJulian Scheel {
3866b8d6e55SJulian Scheel 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3876b8d6e55SJulian Scheel 	uinfo->count = 2;
3886b8d6e55SJulian Scheel 	uinfo->value.integer.min = 0;	/* mute */
3896b8d6e55SJulian Scheel 	uinfo->value.integer.max = DAC_RES;	/* 0dB, 0.5dB step */
3906b8d6e55SJulian Scheel 	return 0;
3916b8d6e55SJulian Scheel }
3926b8d6e55SJulian Scheel 
3937cda8ba9STakashi Iwai static int wm_dac_vol_get(struct snd_kcontrol *kcontrol,
3947cda8ba9STakashi Iwai 			  struct snd_ctl_elem_value *ucontrol)
3956b8d6e55SJulian Scheel {
3966b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
3977cda8ba9STakashi Iwai 	struct prodigy_hifi_spec *spec = ice->spec;
3986b8d6e55SJulian Scheel 	int i;
3996b8d6e55SJulian Scheel 
4007cda8ba9STakashi Iwai 	for (i = 0; i < 2; i++)
4016b8d6e55SJulian Scheel 		ucontrol->value.integer.value[i] =
4027cda8ba9STakashi Iwai 			spec->vol[2 + i] & ~WM_VOL_MUTE;
4036b8d6e55SJulian Scheel 	return 0;
4046b8d6e55SJulian Scheel }
4056b8d6e55SJulian Scheel 
4066b8d6e55SJulian Scheel static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
4076b8d6e55SJulian Scheel {
4086b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
4097cda8ba9STakashi Iwai 	struct prodigy_hifi_spec *spec = ice->spec;
4106b8d6e55SJulian Scheel 	int i, idx, change = 0;
4116b8d6e55SJulian Scheel 
4126b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
4136b8d6e55SJulian Scheel 	for (i = 0; i < 2; i++) {
4147cda8ba9STakashi Iwai 		if (ucontrol->value.integer.value[i] != spec->vol[2 + i]) {
4156b8d6e55SJulian Scheel 			idx = WM_DAC_ATTEN_L + i;
4167cda8ba9STakashi Iwai 			spec->vol[2 + i] &= WM_VOL_MUTE;
4177cda8ba9STakashi Iwai 			spec->vol[2 + i] |= ucontrol->value.integer.value[i];
4187cda8ba9STakashi Iwai 			wm_set_vol(ice, idx, spec->vol[2 + i], spec->master[i]);
4196b8d6e55SJulian Scheel 			change = 1;
4206b8d6e55SJulian Scheel 		}
4216b8d6e55SJulian Scheel 	}
4226b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
4236b8d6e55SJulian Scheel 	return change;
4246b8d6e55SJulian Scheel }
4256b8d6e55SJulian Scheel 
4266b8d6e55SJulian Scheel 
4276b8d6e55SJulian Scheel /*
4286b8d6e55SJulian Scheel  * WM8766 DAC volume attenuation mixer control
4296b8d6e55SJulian Scheel  */
4307cda8ba9STakashi Iwai static int wm8766_vol_info(struct snd_kcontrol *kcontrol,
4317cda8ba9STakashi Iwai 			   struct snd_ctl_elem_info *uinfo)
4326b8d6e55SJulian Scheel {
4336b8d6e55SJulian Scheel 	int voices = kcontrol->private_value >> 8;
4346b8d6e55SJulian Scheel 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4356b8d6e55SJulian Scheel 	uinfo->count = voices;
4366b8d6e55SJulian Scheel 	uinfo->value.integer.min = 0;		/* mute */
4376b8d6e55SJulian Scheel 	uinfo->value.integer.max = DAC_RES;	/* 0dB */
4386b8d6e55SJulian Scheel 	return 0;
4396b8d6e55SJulian Scheel }
4406b8d6e55SJulian Scheel 
4417cda8ba9STakashi Iwai static int wm8766_vol_get(struct snd_kcontrol *kcontrol,
4427cda8ba9STakashi Iwai 			  struct snd_ctl_elem_value *ucontrol)
4436b8d6e55SJulian Scheel {
4446b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
4457cda8ba9STakashi Iwai 	struct prodigy_hifi_spec *spec = ice->spec;
4466b8d6e55SJulian Scheel 	int i, ofs, voices;
4476b8d6e55SJulian Scheel 
4486b8d6e55SJulian Scheel 	voices = kcontrol->private_value >> 8;
4496b8d6e55SJulian Scheel 	ofs = kcontrol->private_value & 0xff;
4506b8d6e55SJulian Scheel 	for (i = 0; i < voices; i++)
4517cda8ba9STakashi Iwai 		ucontrol->value.integer.value[i] = spec->vol[ofs + i];
4526b8d6e55SJulian Scheel 	return 0;
4536b8d6e55SJulian Scheel }
4546b8d6e55SJulian Scheel 
4556b8d6e55SJulian Scheel static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
4566b8d6e55SJulian Scheel {
4576b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
4587cda8ba9STakashi Iwai 	struct prodigy_hifi_spec *spec = ice->spec;
4596b8d6e55SJulian Scheel 	int i, idx, ofs, voices;
4606b8d6e55SJulian Scheel 	int change = 0;
4616b8d6e55SJulian Scheel 
4626b8d6e55SJulian Scheel 	voices = kcontrol->private_value >> 8;
4636b8d6e55SJulian Scheel 	ofs = kcontrol->private_value & 0xff;
4646b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
4656b8d6e55SJulian Scheel 	for (i = 0; i < voices; i++) {
4667cda8ba9STakashi Iwai 		if (ucontrol->value.integer.value[i] != spec->vol[ofs + i]) {
4676b8d6e55SJulian Scheel 			idx = WM8766_LDA1 + ofs + i;
4687cda8ba9STakashi Iwai 			spec->vol[ofs + i] &= WM_VOL_MUTE;
4697cda8ba9STakashi Iwai 			spec->vol[ofs + i] |= ucontrol->value.integer.value[i];
4706b8d6e55SJulian Scheel 			wm8766_set_vol(ice, idx,
4717cda8ba9STakashi Iwai 				       spec->vol[ofs + i], spec->master[i]);
4726b8d6e55SJulian Scheel 			change = 1;
4736b8d6e55SJulian Scheel 		}
4746b8d6e55SJulian Scheel 	}
4756b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
4766b8d6e55SJulian Scheel 	return change;
4776b8d6e55SJulian Scheel }
4786b8d6e55SJulian Scheel 
4796b8d6e55SJulian Scheel /*
4806b8d6e55SJulian Scheel  * Master volume attenuation mixer control / applied to WM8776+WM8766
4816b8d6e55SJulian Scheel  */
4827cda8ba9STakashi Iwai static int wm_master_vol_info(struct snd_kcontrol *kcontrol,
4837cda8ba9STakashi Iwai 			      struct snd_ctl_elem_info *uinfo)
4846b8d6e55SJulian Scheel {
4856b8d6e55SJulian Scheel 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4866b8d6e55SJulian Scheel 	uinfo->count = 2;
4876b8d6e55SJulian Scheel 	uinfo->value.integer.min = 0;
4886b8d6e55SJulian Scheel 	uinfo->value.integer.max = DAC_RES;
4896b8d6e55SJulian Scheel 	return 0;
4906b8d6e55SJulian Scheel }
4916b8d6e55SJulian Scheel 
4927cda8ba9STakashi Iwai static int wm_master_vol_get(struct snd_kcontrol *kcontrol,
4937cda8ba9STakashi Iwai 			     struct snd_ctl_elem_value *ucontrol)
4946b8d6e55SJulian Scheel {
4956b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
4967cda8ba9STakashi Iwai 	struct prodigy_hifi_spec *spec = ice->spec;
4976b8d6e55SJulian Scheel 	int i;
4986b8d6e55SJulian Scheel 	for (i = 0; i < 2; i++)
4997cda8ba9STakashi Iwai 		ucontrol->value.integer.value[i] = spec->master[i];
5006b8d6e55SJulian Scheel 	return 0;
5016b8d6e55SJulian Scheel }
5026b8d6e55SJulian Scheel 
5037cda8ba9STakashi Iwai static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
5047cda8ba9STakashi Iwai 			     struct snd_ctl_elem_value *ucontrol)
5056b8d6e55SJulian Scheel {
5066b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
5077cda8ba9STakashi Iwai 	struct prodigy_hifi_spec *spec = ice->spec;
5086b8d6e55SJulian Scheel 	int ch, change = 0;
5096b8d6e55SJulian Scheel 
5106b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
5116b8d6e55SJulian Scheel 	for (ch = 0; ch < 2; ch++) {
5127cda8ba9STakashi Iwai 		if (ucontrol->value.integer.value[ch] != spec->master[ch]) {
5137cda8ba9STakashi Iwai 			spec->master[ch] = ucontrol->value.integer.value[ch];
5146b8d6e55SJulian Scheel 
5156b8d6e55SJulian Scheel 			/* Apply to front DAC */
5166b8d6e55SJulian Scheel 			wm_set_vol(ice, WM_DAC_ATTEN_L + ch,
5177cda8ba9STakashi Iwai 				   spec->vol[2 + ch], spec->master[ch]);
5186b8d6e55SJulian Scheel 
5196b8d6e55SJulian Scheel 			wm8766_set_vol(ice, WM8766_LDA1 + ch,
5207cda8ba9STakashi Iwai 				       spec->vol[0 + ch], spec->master[ch]);
5216b8d6e55SJulian Scheel 
5226b8d6e55SJulian Scheel 			wm8766_set_vol(ice, WM8766_LDA2 + ch,
5237cda8ba9STakashi Iwai 				       spec->vol[4 + ch], spec->master[ch]);
5246b8d6e55SJulian Scheel 
5256b8d6e55SJulian Scheel 			wm8766_set_vol(ice, WM8766_LDA3 + ch,
5267cda8ba9STakashi Iwai 				       spec->vol[6 + ch], spec->master[ch]);
5276b8d6e55SJulian Scheel 			change = 1;
5286b8d6e55SJulian Scheel 		}
5296b8d6e55SJulian Scheel 	}
5306b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
5316b8d6e55SJulian Scheel 	return change;
5326b8d6e55SJulian Scheel }
5336b8d6e55SJulian Scheel 
5346b8d6e55SJulian Scheel 
5356b8d6e55SJulian Scheel /* KONSTI */
5366b8d6e55SJulian Scheel 
5377cda8ba9STakashi Iwai static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol,
5387cda8ba9STakashi Iwai 				struct snd_ctl_elem_info *uinfo)
5396b8d6e55SJulian Scheel {
5407cda8ba9STakashi Iwai 	static char* texts[32] = {
5417cda8ba9STakashi Iwai 		"NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2,
5426b8d6e55SJulian Scheel 		WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3,
5436b8d6e55SJulian Scheel 		WM_AIN1 "+" WM_AIN2 "+" WM_AIN3,
5446b8d6e55SJulian Scheel 		WM_AIN4, WM_AIN1 "+" WM_AIN4, WM_AIN2 "+" WM_AIN4,
5456b8d6e55SJulian Scheel 		WM_AIN1 "+" WM_AIN2 "+" WM_AIN4,
5466b8d6e55SJulian Scheel 		WM_AIN3 "+" WM_AIN4, WM_AIN1 "+" WM_AIN3 "+" WM_AIN4,
5476b8d6e55SJulian Scheel 		WM_AIN2 "+" WM_AIN3 "+" WM_AIN4,
5486b8d6e55SJulian Scheel 		WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4,
5496b8d6e55SJulian Scheel 		WM_AIN5, WM_AIN1 "+" WM_AIN5, WM_AIN2 "+" WM_AIN5,
5506b8d6e55SJulian Scheel 		WM_AIN1 "+" WM_AIN2 "+" WM_AIN5,
5516b8d6e55SJulian Scheel 		WM_AIN3 "+" WM_AIN5, WM_AIN1 "+" WM_AIN3 "+" WM_AIN5,
5526b8d6e55SJulian Scheel 		WM_AIN2 "+" WM_AIN3 "+" WM_AIN5,
5536b8d6e55SJulian Scheel 		WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN5,
5546b8d6e55SJulian Scheel 		WM_AIN4 "+" WM_AIN5, WM_AIN1 "+" WM_AIN4 "+" WM_AIN5,
5556b8d6e55SJulian Scheel 		WM_AIN2 "+" WM_AIN4 "+" WM_AIN5,
5566b8d6e55SJulian Scheel 		WM_AIN1 "+" WM_AIN2 "+" WM_AIN4 "+" WM_AIN5,
5576b8d6e55SJulian Scheel 		WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
5586b8d6e55SJulian Scheel 		WM_AIN1 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
5596b8d6e55SJulian Scheel 		WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
5607cda8ba9STakashi Iwai 		WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5
5617cda8ba9STakashi Iwai 	};
5626b8d6e55SJulian Scheel 
5636b8d6e55SJulian Scheel 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
5646b8d6e55SJulian Scheel 	uinfo->count = 1;
5656b8d6e55SJulian Scheel 	uinfo->value.enumerated.items = 32;
5666b8d6e55SJulian Scheel 	if (uinfo->value.enumerated.item > 31)
5676b8d6e55SJulian Scheel 		uinfo->value.enumerated.item = 31;
5687cda8ba9STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
5697cda8ba9STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
5706b8d6e55SJulian Scheel 	return 0;
5716b8d6e55SJulian Scheel }
5726b8d6e55SJulian Scheel 
5737cda8ba9STakashi Iwai static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol,
5747cda8ba9STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
5756b8d6e55SJulian Scheel {
5766b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
5776b8d6e55SJulian Scheel 
5786b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
5796b8d6e55SJulian Scheel 	ucontrol->value.integer.value[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
5806b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
5816b8d6e55SJulian Scheel 	return 0;
5826b8d6e55SJulian Scheel }
5836b8d6e55SJulian Scheel 
5847cda8ba9STakashi Iwai static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol,
5857cda8ba9STakashi Iwai 			       struct snd_ctl_elem_value *ucontrol)
5866b8d6e55SJulian Scheel {
5876b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
5886b8d6e55SJulian Scheel 	unsigned short oval, nval;
5897cda8ba9STakashi Iwai 	int change = 0;
5906b8d6e55SJulian Scheel 
5916b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
5926b8d6e55SJulian Scheel 	oval = wm_get(ice, WM_ADC_MUX);
5936b8d6e55SJulian Scheel 	nval = (oval & 0xe0) | ucontrol->value.integer.value[0];
5946b8d6e55SJulian Scheel 	if (nval != oval) {
5956b8d6e55SJulian Scheel 		wm_put(ice, WM_ADC_MUX, nval);
5967cda8ba9STakashi Iwai 		change = 1;
5976b8d6e55SJulian Scheel 	}
5986b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
5997cda8ba9STakashi Iwai 	return change;
6006b8d6e55SJulian Scheel }
6016b8d6e55SJulian Scheel 
6026b8d6e55SJulian Scheel /* KONSTI */
6036b8d6e55SJulian Scheel 
6046b8d6e55SJulian Scheel /*
6056b8d6e55SJulian Scheel  * ADC gain mixer control (-64dB to 0dB)
6066b8d6e55SJulian Scheel  */
6076b8d6e55SJulian Scheel 
6086b8d6e55SJulian Scheel #define ADC_0dB	0xcf
6096b8d6e55SJulian Scheel #define ADC_RES	128
6106b8d6e55SJulian Scheel #define ADC_MIN	(ADC_0dB - ADC_RES)
6116b8d6e55SJulian Scheel 
6127cda8ba9STakashi Iwai static int wm_adc_vol_info(struct snd_kcontrol *kcontrol,
6137cda8ba9STakashi Iwai 			   struct snd_ctl_elem_info *uinfo)
6146b8d6e55SJulian Scheel {
6156b8d6e55SJulian Scheel 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
6166b8d6e55SJulian Scheel 	uinfo->count = 2;
6176b8d6e55SJulian Scheel 	uinfo->value.integer.min = 0;	/* mute (-64dB) */
6186b8d6e55SJulian Scheel 	uinfo->value.integer.max = ADC_RES;	/* 0dB, 0.5dB step */
6196b8d6e55SJulian Scheel 	return 0;
6206b8d6e55SJulian Scheel }
6216b8d6e55SJulian Scheel 
6227cda8ba9STakashi Iwai static int wm_adc_vol_get(struct snd_kcontrol *kcontrol,
6237cda8ba9STakashi Iwai 			  struct snd_ctl_elem_value *ucontrol)
6246b8d6e55SJulian Scheel {
6256b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
6266b8d6e55SJulian Scheel 	unsigned short val;
6276b8d6e55SJulian Scheel 	int i;
6286b8d6e55SJulian Scheel 
6296b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
6306b8d6e55SJulian Scheel 	for (i = 0; i < 2; i++) {
6316b8d6e55SJulian Scheel 		val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff;
6326b8d6e55SJulian Scheel 		val = val > ADC_MIN ? (val - ADC_MIN) : 0;
6336b8d6e55SJulian Scheel 		ucontrol->value.integer.value[i] = val;
6346b8d6e55SJulian Scheel 	}
6356b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
6366b8d6e55SJulian Scheel 	return 0;
6376b8d6e55SJulian Scheel }
6386b8d6e55SJulian Scheel 
6397cda8ba9STakashi Iwai static int wm_adc_vol_put(struct snd_kcontrol *kcontrol,
6407cda8ba9STakashi Iwai 			  struct snd_ctl_elem_value *ucontrol)
6416b8d6e55SJulian Scheel {
6426b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
6436b8d6e55SJulian Scheel 	unsigned short ovol, nvol;
6446b8d6e55SJulian Scheel 	int i, idx, change = 0;
6456b8d6e55SJulian Scheel 
6466b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
6476b8d6e55SJulian Scheel 	for (i = 0; i < 2; i++) {
6486b8d6e55SJulian Scheel 		nvol = ucontrol->value.integer.value[i];
6496b8d6e55SJulian Scheel 		nvol = nvol ? (nvol + ADC_MIN) : 0;
6506b8d6e55SJulian Scheel 		idx  = WM_ADC_ATTEN_L + i;
6516b8d6e55SJulian Scheel 		ovol = wm_get(ice, idx) & 0xff;
6526b8d6e55SJulian Scheel 		if (ovol != nvol) {
6536b8d6e55SJulian Scheel 			wm_put(ice, idx, nvol);
6546b8d6e55SJulian Scheel 			change = 1;
6556b8d6e55SJulian Scheel 		}
6566b8d6e55SJulian Scheel 	}
6576b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
6586b8d6e55SJulian Scheel 	return change;
6596b8d6e55SJulian Scheel }
6606b8d6e55SJulian Scheel 
6616b8d6e55SJulian Scheel /*
6626b8d6e55SJulian Scheel  * ADC input mux mixer control
6636b8d6e55SJulian Scheel  */
6647cda8ba9STakashi Iwai #define wm_adc_mux_info		snd_ctl_boolean_mono_info
6656b8d6e55SJulian Scheel 
6667cda8ba9STakashi Iwai static int wm_adc_mux_get(struct snd_kcontrol *kcontrol,
6677cda8ba9STakashi Iwai 			  struct snd_ctl_elem_value *ucontrol)
6686b8d6e55SJulian Scheel {
6696b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
6706b8d6e55SJulian Scheel 	int bit = kcontrol->private_value;
6716b8d6e55SJulian Scheel 
6726b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
6737cda8ba9STakashi Iwai 	ucontrol->value.integer.value[0] =
6747cda8ba9STakashi Iwai 		(wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0;
6756b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
6766b8d6e55SJulian Scheel 	return 0;
6776b8d6e55SJulian Scheel }
6786b8d6e55SJulian Scheel 
6797cda8ba9STakashi Iwai static int wm_adc_mux_put(struct snd_kcontrol *kcontrol,
6807cda8ba9STakashi Iwai 			  struct snd_ctl_elem_value *ucontrol)
6816b8d6e55SJulian Scheel {
6826b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
6836b8d6e55SJulian Scheel 	int bit = kcontrol->private_value;
6846b8d6e55SJulian Scheel 	unsigned short oval, nval;
6856b8d6e55SJulian Scheel 	int change;
6866b8d6e55SJulian Scheel 
6876b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
6886b8d6e55SJulian Scheel 	nval = oval = wm_get(ice, WM_ADC_MUX);
6896b8d6e55SJulian Scheel 	if (ucontrol->value.integer.value[0])
6906b8d6e55SJulian Scheel 		nval |= (1 << bit);
6916b8d6e55SJulian Scheel 	else
6926b8d6e55SJulian Scheel 		nval &= ~(1 << bit);
6936b8d6e55SJulian Scheel 	change = nval != oval;
6946b8d6e55SJulian Scheel 	if (change) {
6956b8d6e55SJulian Scheel 		wm_put(ice, WM_ADC_MUX, nval);
6966b8d6e55SJulian Scheel 	}
6976b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
6986b8d6e55SJulian Scheel 	return 0;
6996b8d6e55SJulian Scheel }
7006b8d6e55SJulian Scheel 
7016b8d6e55SJulian Scheel /*
7026b8d6e55SJulian Scheel  * Analog bypass (In -> Out)
7036b8d6e55SJulian Scheel  */
7047cda8ba9STakashi Iwai #define wm_bypass_info		snd_ctl_boolean_mono_info
7056b8d6e55SJulian Scheel 
7067cda8ba9STakashi Iwai static int wm_bypass_get(struct snd_kcontrol *kcontrol,
7077cda8ba9STakashi Iwai 			 struct snd_ctl_elem_value *ucontrol)
7086b8d6e55SJulian Scheel {
7096b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
7106b8d6e55SJulian Scheel 
7116b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
7127cda8ba9STakashi Iwai 	ucontrol->value.integer.value[0] =
7137cda8ba9STakashi Iwai 		(wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0;
7146b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
7156b8d6e55SJulian Scheel 	return 0;
7166b8d6e55SJulian Scheel }
7176b8d6e55SJulian Scheel 
7187cda8ba9STakashi Iwai static int wm_bypass_put(struct snd_kcontrol *kcontrol,
7197cda8ba9STakashi Iwai 			 struct snd_ctl_elem_value *ucontrol)
7206b8d6e55SJulian Scheel {
7216b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
7226b8d6e55SJulian Scheel 	unsigned short val, oval;
7236b8d6e55SJulian Scheel 	int change = 0;
7246b8d6e55SJulian Scheel 
7256b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
7266b8d6e55SJulian Scheel 	val = oval = wm_get(ice, WM_OUT_MUX);
7276b8d6e55SJulian Scheel 	if (ucontrol->value.integer.value[0])
7286b8d6e55SJulian Scheel 		val |= 0x04;
7296b8d6e55SJulian Scheel 	else
7306b8d6e55SJulian Scheel 		val &= ~0x04;
7316b8d6e55SJulian Scheel 	if (val != oval) {
7326b8d6e55SJulian Scheel 		wm_put(ice, WM_OUT_MUX, val);
7336b8d6e55SJulian Scheel 		change = 1;
7346b8d6e55SJulian Scheel 	}
7356b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
7366b8d6e55SJulian Scheel 	return change;
7376b8d6e55SJulian Scheel }
7386b8d6e55SJulian Scheel 
7396b8d6e55SJulian Scheel /*
7406b8d6e55SJulian Scheel  * Left/Right swap
7416b8d6e55SJulian Scheel  */
7427cda8ba9STakashi Iwai #define wm_chswap_info		snd_ctl_boolean_mono_info
7436b8d6e55SJulian Scheel 
7447cda8ba9STakashi Iwai static int wm_chswap_get(struct snd_kcontrol *kcontrol,
7457cda8ba9STakashi Iwai 			 struct snd_ctl_elem_value *ucontrol)
7466b8d6e55SJulian Scheel {
7476b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
7486b8d6e55SJulian Scheel 
7496b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
7506b8d6e55SJulian Scheel 	ucontrol->value.integer.value[0] =
7516b8d6e55SJulian Scheel 			(wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90;
7526b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
7536b8d6e55SJulian Scheel 	return 0;
7546b8d6e55SJulian Scheel }
7556b8d6e55SJulian Scheel 
7567cda8ba9STakashi Iwai static int wm_chswap_put(struct snd_kcontrol *kcontrol,
7577cda8ba9STakashi Iwai 			 struct snd_ctl_elem_value *ucontrol)
7586b8d6e55SJulian Scheel {
7596b8d6e55SJulian Scheel 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
7606b8d6e55SJulian Scheel 	unsigned short val, oval;
7616b8d6e55SJulian Scheel 	int change = 0;
7626b8d6e55SJulian Scheel 
7636b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
7646b8d6e55SJulian Scheel 	oval = wm_get(ice, WM_DAC_CTRL1);
7656b8d6e55SJulian Scheel 	val = oval & 0x0f;
7666b8d6e55SJulian Scheel 	if (ucontrol->value.integer.value[0])
7676b8d6e55SJulian Scheel 		val |= 0x60;
7686b8d6e55SJulian Scheel 	else
7696b8d6e55SJulian Scheel 		val |= 0x90;
7706b8d6e55SJulian Scheel 	if (val != oval) {
7716b8d6e55SJulian Scheel 		wm_put(ice, WM_DAC_CTRL1, val);
7726b8d6e55SJulian Scheel 		wm_put_nocache(ice, WM_DAC_CTRL1, val);
7736b8d6e55SJulian Scheel 		change = 1;
7746b8d6e55SJulian Scheel 	}
7756b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
7766b8d6e55SJulian Scheel 	return change;
7776b8d6e55SJulian Scheel }
7786b8d6e55SJulian Scheel 
7796b8d6e55SJulian Scheel 
7806b8d6e55SJulian Scheel /*
7816b8d6e55SJulian Scheel  * mixers
7826b8d6e55SJulian Scheel  */
7836b8d6e55SJulian Scheel 
7846b8d6e55SJulian Scheel static struct snd_kcontrol_new prodigy_hifi_controls[] __devinitdata = {
7856b8d6e55SJulian Scheel 	{
7866b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7876b8d6e55SJulian Scheel 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
7886b8d6e55SJulian Scheel 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
7896b8d6e55SJulian Scheel 		.name = "Master Playback Volume",
7906b8d6e55SJulian Scheel 		.info = wm_master_vol_info,
7916b8d6e55SJulian Scheel 		.get = wm_master_vol_get,
7926b8d6e55SJulian Scheel 		.put = wm_master_vol_put,
7936b8d6e55SJulian Scheel 		.tlv = { .p = db_scale_wm_dac }
7946b8d6e55SJulian Scheel 	},
7956b8d6e55SJulian Scheel 	{
7966b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7976b8d6e55SJulian Scheel 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
7986b8d6e55SJulian Scheel 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
7996b8d6e55SJulian Scheel 		.name = "Front Playback Volume",
8006b8d6e55SJulian Scheel 		.info = wm_dac_vol_info,
8016b8d6e55SJulian Scheel 		.get = wm_dac_vol_get,
8026b8d6e55SJulian Scheel 		.put = wm_dac_vol_put,
8036b8d6e55SJulian Scheel 		.tlv = { .p = db_scale_wm_dac },
8046b8d6e55SJulian Scheel 	},
8056b8d6e55SJulian Scheel 	{
8066b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8076b8d6e55SJulian Scheel 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
8086b8d6e55SJulian Scheel 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
8096b8d6e55SJulian Scheel 		.name = "Rear Playback Volume",
8106b8d6e55SJulian Scheel 		.info = wm8766_vol_info,
8116b8d6e55SJulian Scheel 		.get = wm8766_vol_get,
8126b8d6e55SJulian Scheel 		.put = wm8766_vol_put,
8136b8d6e55SJulian Scheel 		.private_value = (2 << 8) | 0,
8146b8d6e55SJulian Scheel 		.tlv = { .p = db_scale_wm_dac },
8156b8d6e55SJulian Scheel 	},
8166b8d6e55SJulian Scheel 	{
8176b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8186b8d6e55SJulian Scheel 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
8196b8d6e55SJulian Scheel 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
8206b8d6e55SJulian Scheel 		.name = "Center Playback Volume",
8216b8d6e55SJulian Scheel 		.info = wm8766_vol_info,
8226b8d6e55SJulian Scheel 		.get = wm8766_vol_get,
8236b8d6e55SJulian Scheel 		.put = wm8766_vol_put,
8246b8d6e55SJulian Scheel 		.private_value = (1 << 8) | 4,
8256b8d6e55SJulian Scheel 		.tlv = { .p = db_scale_wm_dac }
8266b8d6e55SJulian Scheel 	},
8276b8d6e55SJulian Scheel 	{
8286b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8296b8d6e55SJulian Scheel 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
8306b8d6e55SJulian Scheel 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
8316b8d6e55SJulian Scheel 		.name = "LFE Playback Volume",
8326b8d6e55SJulian Scheel 		.info = wm8766_vol_info,
8336b8d6e55SJulian Scheel 		.get = wm8766_vol_get,
8346b8d6e55SJulian Scheel 		.put = wm8766_vol_put,
8356b8d6e55SJulian Scheel 		.private_value = (1 << 8) | 5,
8366b8d6e55SJulian Scheel 		.tlv = { .p = db_scale_wm_dac }
8376b8d6e55SJulian Scheel 	},
8386b8d6e55SJulian Scheel 	{
8396b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8406b8d6e55SJulian Scheel 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
8416b8d6e55SJulian Scheel 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
8426b8d6e55SJulian Scheel 		.name = "Side Playback Volume",
8436b8d6e55SJulian Scheel 		.info = wm8766_vol_info,
8446b8d6e55SJulian Scheel 		.get = wm8766_vol_get,
8456b8d6e55SJulian Scheel 		.put = wm8766_vol_put,
8466b8d6e55SJulian Scheel 		.private_value = (2 << 8) | 6,
8476b8d6e55SJulian Scheel 		.tlv = { .p = db_scale_wm_dac },
8486b8d6e55SJulian Scheel 	},
8496b8d6e55SJulian Scheel 	{
8506b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8516b8d6e55SJulian Scheel 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
8526b8d6e55SJulian Scheel 			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
8536b8d6e55SJulian Scheel 		.name = "Capture Volume",
8546b8d6e55SJulian Scheel 		.info = wm_adc_vol_info,
8556b8d6e55SJulian Scheel 		.get = wm_adc_vol_get,
8566b8d6e55SJulian Scheel 		.put = wm_adc_vol_put,
8576b8d6e55SJulian Scheel 		.tlv = { .p = db_scale_wm_dac },
8586b8d6e55SJulian Scheel 	},
8596b8d6e55SJulian Scheel 	{
8606b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8616b8d6e55SJulian Scheel 		.name = "CD Capture Switch",
8626b8d6e55SJulian Scheel 		.info = wm_adc_mux_info,
8636b8d6e55SJulian Scheel 		.get = wm_adc_mux_get,
8646b8d6e55SJulian Scheel 		.put = wm_adc_mux_put,
8656b8d6e55SJulian Scheel 		.private_value = 0,
8666b8d6e55SJulian Scheel 	},
8676b8d6e55SJulian Scheel 	{
8686b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8696b8d6e55SJulian Scheel 		.name = "Line Capture Switch",
8706b8d6e55SJulian Scheel 		.info = wm_adc_mux_info,
8716b8d6e55SJulian Scheel 		.get = wm_adc_mux_get,
8726b8d6e55SJulian Scheel 		.put = wm_adc_mux_put,
8736b8d6e55SJulian Scheel 		.private_value = 1,
8746b8d6e55SJulian Scheel 	},
8756b8d6e55SJulian Scheel 	{
8766b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8776b8d6e55SJulian Scheel 		.name = "Analog Bypass Switch",
8786b8d6e55SJulian Scheel 		.info = wm_bypass_info,
8796b8d6e55SJulian Scheel 		.get = wm_bypass_get,
8806b8d6e55SJulian Scheel 		.put = wm_bypass_put,
8816b8d6e55SJulian Scheel 	},
8826b8d6e55SJulian Scheel 	{
8836b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8846b8d6e55SJulian Scheel 		.name = "Swap Output Channels",
8856b8d6e55SJulian Scheel 		.info = wm_chswap_info,
8866b8d6e55SJulian Scheel 		.get = wm_chswap_get,
8876b8d6e55SJulian Scheel 		.put = wm_chswap_put,
8886b8d6e55SJulian Scheel 	},
8896b8d6e55SJulian Scheel 	{
8906b8d6e55SJulian Scheel 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8916b8d6e55SJulian Scheel 		.name = "Analog Capture Source",
8926b8d6e55SJulian Scheel 		.info = wm_adc_mux_enum_info,
8936b8d6e55SJulian Scheel 		.get = wm_adc_mux_enum_get,
8946b8d6e55SJulian Scheel 		.put = wm_adc_mux_enum_put,
8956b8d6e55SJulian Scheel 	},
8966b8d6e55SJulian Scheel };
8976b8d6e55SJulian Scheel 
8986b8d6e55SJulian Scheel /*
8996b8d6e55SJulian Scheel  * WM codec registers
9006b8d6e55SJulian Scheel  */
9017cda8ba9STakashi Iwai static void wm_proc_regs_write(struct snd_info_entry *entry,
9027cda8ba9STakashi Iwai 			       struct snd_info_buffer *buffer)
9036b8d6e55SJulian Scheel {
9047cda8ba9STakashi Iwai 	struct snd_ice1712 *ice = entry->private_data;
9056b8d6e55SJulian Scheel 	char line[64];
9066b8d6e55SJulian Scheel 	unsigned int reg, val;
9076b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
9086b8d6e55SJulian Scheel 	while (!snd_info_get_line(buffer, line, sizeof(line))) {
9096b8d6e55SJulian Scheel 		if (sscanf(line, "%x %x", &reg, &val) != 2)
9106b8d6e55SJulian Scheel 			continue;
9116b8d6e55SJulian Scheel 		if (reg <= 0x17 && val <= 0xffff)
9126b8d6e55SJulian Scheel 			wm_put(ice, reg, val);
9136b8d6e55SJulian Scheel 	}
9146b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
9156b8d6e55SJulian Scheel }
9166b8d6e55SJulian Scheel 
9177cda8ba9STakashi Iwai static void wm_proc_regs_read(struct snd_info_entry *entry,
9187cda8ba9STakashi Iwai 			      struct snd_info_buffer *buffer)
9196b8d6e55SJulian Scheel {
9207cda8ba9STakashi Iwai 	struct snd_ice1712 *ice = entry->private_data;
9216b8d6e55SJulian Scheel 	int reg, val;
9226b8d6e55SJulian Scheel 
9236b8d6e55SJulian Scheel 	mutex_lock(&ice->gpio_mutex);
9246b8d6e55SJulian Scheel 	for (reg = 0; reg <= 0x17; reg++) {
9256b8d6e55SJulian Scheel 		val = wm_get(ice, reg);
9266b8d6e55SJulian Scheel 		snd_iprintf(buffer, "%02x = %04x\n", reg, val);
9276b8d6e55SJulian Scheel 	}
9286b8d6e55SJulian Scheel 	mutex_unlock(&ice->gpio_mutex);
9296b8d6e55SJulian Scheel }
9306b8d6e55SJulian Scheel 
9316b8d6e55SJulian Scheel static void wm_proc_init(struct snd_ice1712 *ice)
9326b8d6e55SJulian Scheel {
9336b8d6e55SJulian Scheel 	struct snd_info_entry *entry;
9346b8d6e55SJulian Scheel 	if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) {
9356b8d6e55SJulian Scheel 		snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
9366b8d6e55SJulian Scheel 		entry->mode |= S_IWUSR;
9376b8d6e55SJulian Scheel 		entry->c.text.write = wm_proc_regs_write;
9386b8d6e55SJulian Scheel 	}
9396b8d6e55SJulian Scheel }
9406b8d6e55SJulian Scheel 
9416b8d6e55SJulian Scheel static int __devinit prodigy_hifi_add_controls(struct snd_ice1712 *ice)
9426b8d6e55SJulian Scheel {
9436b8d6e55SJulian Scheel 	unsigned int i;
9446b8d6e55SJulian Scheel 	int err;
9456b8d6e55SJulian Scheel 
9466b8d6e55SJulian Scheel 	for (i = 0; i < ARRAY_SIZE(prodigy_hifi_controls); i++) {
9477cda8ba9STakashi Iwai 		err = snd_ctl_add(ice->card,
9487cda8ba9STakashi Iwai 				  snd_ctl_new1(&prodigy_hifi_controls[i], ice));
9496b8d6e55SJulian Scheel 		if (err < 0)
9506b8d6e55SJulian Scheel 			return err;
9516b8d6e55SJulian Scheel 	}
9526b8d6e55SJulian Scheel 
9536b8d6e55SJulian Scheel 	wm_proc_init(ice);
9546b8d6e55SJulian Scheel 
9556b8d6e55SJulian Scheel 	return 0;
9566b8d6e55SJulian Scheel }
9576b8d6e55SJulian Scheel 
9586b8d6e55SJulian Scheel static int __devinit prodigy_hd2_add_controls(struct snd_ice1712 *ice)
9596b8d6e55SJulian Scheel {
9606b8d6e55SJulian Scheel 	unsigned int i;
9616b8d6e55SJulian Scheel 	int err;
9626b8d6e55SJulian Scheel 
9636b8d6e55SJulian Scheel 	for (i = 0; i < ARRAY_SIZE(prodigy_hd2_controls); i++) {
9647cda8ba9STakashi Iwai 		err = snd_ctl_add(ice->card,
9657cda8ba9STakashi Iwai 				  snd_ctl_new1(&prodigy_hd2_controls[i], ice));
9666b8d6e55SJulian Scheel 		if (err < 0)
9676b8d6e55SJulian Scheel 			return err;
9686b8d6e55SJulian Scheel 	}
9696b8d6e55SJulian Scheel 
9706b8d6e55SJulian Scheel 	wm_proc_init(ice);
9716b8d6e55SJulian Scheel 
9726b8d6e55SJulian Scheel 	return 0;
9736b8d6e55SJulian Scheel }
9746b8d6e55SJulian Scheel 
9756b8d6e55SJulian Scheel 
9766b8d6e55SJulian Scheel /*
9776b8d6e55SJulian Scheel  * initialize the chip
9786b8d6e55SJulian Scheel  */
9796b8d6e55SJulian Scheel static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
9806b8d6e55SJulian Scheel {
9816b8d6e55SJulian Scheel 	static unsigned short wm_inits[] = {
9826b8d6e55SJulian Scheel 		/* These come first to reduce init pop noise */
9836b8d6e55SJulian Scheel 		WM_ADC_MUX,	0x0003,	/* ADC mute */
9846b8d6e55SJulian Scheel 		/* 0x00c0 replaced by 0x0003 */
9856b8d6e55SJulian Scheel 
9866b8d6e55SJulian Scheel 		WM_DAC_MUTE,	0x0001,	/* DAC softmute */
9876b8d6e55SJulian Scheel 		WM_DAC_CTRL1,	0x0000,	/* DAC mute */
9886b8d6e55SJulian Scheel 
9896b8d6e55SJulian Scheel 		WM_POWERDOWN,	0x0008,	/* All power-up except HP */
9906b8d6e55SJulian Scheel 		WM_RESET,	0x0000,	/* reset */
9916b8d6e55SJulian Scheel 	};
9926b8d6e55SJulian Scheel 	static unsigned short wm_inits2[] = {
9936b8d6e55SJulian Scheel 		WM_MASTER_CTRL,  0x0022, /* 256fs, slave mode */
9946b8d6e55SJulian Scheel 		WM_DAC_INT,	0x0022,	/* I2S, normal polarity, 24bit */
9956b8d6e55SJulian Scheel 		WM_ADC_INT,	0x0022,	/* I2S, normal polarity, 24bit */
9966b8d6e55SJulian Scheel 		WM_DAC_CTRL1,	0x0090,	/* DAC L/R */
9976b8d6e55SJulian Scheel 		WM_OUT_MUX,	0x0001,	/* OUT DAC */
9986b8d6e55SJulian Scheel 		WM_HP_ATTEN_L,	0x0179,	/* HP 0dB */
9996b8d6e55SJulian Scheel 		WM_HP_ATTEN_R,	0x0179,	/* HP 0dB */
10006b8d6e55SJulian Scheel 		WM_DAC_ATTEN_L,	0x0000,	/* DAC 0dB */
10016b8d6e55SJulian Scheel 		WM_DAC_ATTEN_L,	0x0100,	/* DAC 0dB */
10026b8d6e55SJulian Scheel 		WM_DAC_ATTEN_R,	0x0000,	/* DAC 0dB */
10036b8d6e55SJulian Scheel 		WM_DAC_ATTEN_R,	0x0100,	/* DAC 0dB */
10046b8d6e55SJulian Scheel 		WM_PHASE_SWAP,	0x0000,	/* phase normal */
10056b8d6e55SJulian Scheel #if 0
10066b8d6e55SJulian Scheel 		WM_DAC_MASTER,	0x0100,	/* DAC master muted */
10076b8d6e55SJulian Scheel #endif
10086b8d6e55SJulian Scheel 		WM_DAC_CTRL2,	0x0000,	/* no deemphasis, no ZFLG */
10096b8d6e55SJulian Scheel 		WM_ADC_ATTEN_L,	0x0000,	/* ADC muted */
10106b8d6e55SJulian Scheel 		WM_ADC_ATTEN_R,	0x0000,	/* ADC muted */
10116b8d6e55SJulian Scheel #if 1
10126b8d6e55SJulian Scheel 		WM_ALC_CTRL1,	0x007b,	/* */
10136b8d6e55SJulian Scheel 		WM_ALC_CTRL2,	0x0000,	/* */
10146b8d6e55SJulian Scheel 		WM_ALC_CTRL3,	0x0000,	/* */
10156b8d6e55SJulian Scheel 		WM_NOISE_GATE,	0x0000,	/* */
10166b8d6e55SJulian Scheel #endif
10176b8d6e55SJulian Scheel 		WM_DAC_MUTE,	0x0000,	/* DAC unmute */
10186b8d6e55SJulian Scheel 		WM_ADC_MUX,	0x0003,	/* ADC unmute, both CD/Line On */
10196b8d6e55SJulian Scheel 	};
10206b8d6e55SJulian Scheel 	static unsigned short wm8766_inits[] = {
10216b8d6e55SJulian Scheel 		WM8766_RESET,	   0x0000,
10226b8d6e55SJulian Scheel 		WM8766_DAC_CTRL,	0x0120,
10236b8d6e55SJulian Scheel 		WM8766_INT_CTRL,	0x0022, /* I2S Normal Mode, 24 bit */
10246b8d6e55SJulian Scheel 		WM8766_DAC_CTRL2,       0x0001,
10256b8d6e55SJulian Scheel 		WM8766_DAC_CTRL3,       0x0080,
10266b8d6e55SJulian Scheel 		WM8766_LDA1,	    0x0100,
10276b8d6e55SJulian Scheel 		WM8766_LDA2,	    0x0100,
10286b8d6e55SJulian Scheel 		WM8766_LDA3,	    0x0100,
10296b8d6e55SJulian Scheel 		WM8766_RDA1,	    0x0100,
10306b8d6e55SJulian Scheel 		WM8766_RDA2,	    0x0100,
10316b8d6e55SJulian Scheel 		WM8766_RDA3,	    0x0100,
10326b8d6e55SJulian Scheel 		WM8766_MUTE1,	   0x0000,
10336b8d6e55SJulian Scheel 		WM8766_MUTE2,	   0x0000,
10346b8d6e55SJulian Scheel 	};
10356b8d6e55SJulian Scheel 
10367cda8ba9STakashi Iwai 	struct prodigy_hifi_spec *spec;
10376b8d6e55SJulian Scheel 	unsigned int i;
10386b8d6e55SJulian Scheel 
10396b8d6e55SJulian Scheel 	ice->vt1720 = 0;
10406b8d6e55SJulian Scheel 	ice->vt1724 = 1;
10416b8d6e55SJulian Scheel 
10426b8d6e55SJulian Scheel 	ice->num_total_dacs = 8;
10436b8d6e55SJulian Scheel 	ice->num_total_adcs = 1;
10446b8d6e55SJulian Scheel 
10456b8d6e55SJulian Scheel 	/* HACK - use this as the SPDIF source.
10466b8d6e55SJulian Scheel 	* don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
10476b8d6e55SJulian Scheel 	*/
10486b8d6e55SJulian Scheel 	ice->gpio.saved[0] = 0;
104925985edcSLucas De Marchi 	/* to remember the register values */
10506b8d6e55SJulian Scheel 
10516b8d6e55SJulian Scheel 	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
10526b8d6e55SJulian Scheel 	if (! ice->akm)
10536b8d6e55SJulian Scheel 		return -ENOMEM;
10546b8d6e55SJulian Scheel 	ice->akm_codecs = 1;
10556b8d6e55SJulian Scheel 
10567cda8ba9STakashi Iwai 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
10577cda8ba9STakashi Iwai 	if (!spec)
10587cda8ba9STakashi Iwai 		return -ENOMEM;
10597cda8ba9STakashi Iwai 	ice->spec = spec;
10607cda8ba9STakashi Iwai 
10616b8d6e55SJulian Scheel 	/* initialize WM8776 codec */
10626b8d6e55SJulian Scheel 	for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
10636b8d6e55SJulian Scheel 		wm_put(ice, wm_inits[i], wm_inits[i+1]);
10646b8d6e55SJulian Scheel 	schedule_timeout_uninterruptible(1);
10656b8d6e55SJulian Scheel 	for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2)
10666b8d6e55SJulian Scheel 		wm_put(ice, wm_inits2[i], wm_inits2[i+1]);
10676b8d6e55SJulian Scheel 
10686b8d6e55SJulian Scheel 	/* initialize WM8766 codec */
10696b8d6e55SJulian Scheel 	for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2)
10706b8d6e55SJulian Scheel 		wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i+1]);
10716b8d6e55SJulian Scheel 
10726b8d6e55SJulian Scheel 
10736b8d6e55SJulian Scheel 	return 0;
10746b8d6e55SJulian Scheel }
10756b8d6e55SJulian Scheel 
10766b8d6e55SJulian Scheel 
10776b8d6e55SJulian Scheel /*
10786b8d6e55SJulian Scheel  * initialize the chip
10796b8d6e55SJulian Scheel  */
1080b40e9538SIgor Chernyshev static void ak4396_init(struct snd_ice1712 *ice)
10816b8d6e55SJulian Scheel {
10826b8d6e55SJulian Scheel 	static unsigned short ak4396_inits[] = {
10836b8d6e55SJulian Scheel 		AK4396_CTRL1,	   0x87,   /* I2S Normal Mode, 24 bit */
10846b8d6e55SJulian Scheel 		AK4396_CTRL2,	   0x02,
10856b8d6e55SJulian Scheel 		AK4396_CTRL3,	   0x00,
10866b8d6e55SJulian Scheel 		AK4396_LCH_ATT,	 0x00,
10876b8d6e55SJulian Scheel 		AK4396_RCH_ATT,	 0x00,
10886b8d6e55SJulian Scheel 	};
10896b8d6e55SJulian Scheel 
10906b8d6e55SJulian Scheel 	unsigned int i;
10916b8d6e55SJulian Scheel 
1092b40e9538SIgor Chernyshev 	/* initialize ak4396 codec */
1093b40e9538SIgor Chernyshev 	/* reset codec */
1094b40e9538SIgor Chernyshev 	ak4396_write(ice, AK4396_CTRL1, 0x86);
1095b40e9538SIgor Chernyshev 	msleep(100);
1096b40e9538SIgor Chernyshev 	ak4396_write(ice, AK4396_CTRL1, 0x87);
1097b40e9538SIgor Chernyshev 
1098b40e9538SIgor Chernyshev 	for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2)
1099b40e9538SIgor Chernyshev 		ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
1100b40e9538SIgor Chernyshev }
1101b40e9538SIgor Chernyshev 
1102*c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP
11035e08fe57STakashi Iwai static int prodigy_hd2_resume(struct snd_ice1712 *ice)
1104b40e9538SIgor Chernyshev {
1105b40e9538SIgor Chernyshev 	/* initialize ak4396 codec and restore previous mixer volumes */
1106b40e9538SIgor Chernyshev 	struct prodigy_hifi_spec *spec = ice->spec;
1107b40e9538SIgor Chernyshev 	int i;
1108b40e9538SIgor Chernyshev 	mutex_lock(&ice->gpio_mutex);
1109b40e9538SIgor Chernyshev 	ak4396_init(ice);
1110b40e9538SIgor Chernyshev 	for (i = 0; i < 2; i++)
1111b40e9538SIgor Chernyshev 		ak4396_write(ice, AK4396_LCH_ATT + i, spec->vol[i] & 0xff);
1112b40e9538SIgor Chernyshev 	mutex_unlock(&ice->gpio_mutex);
1113b40e9538SIgor Chernyshev 	return 0;
1114b40e9538SIgor Chernyshev }
1115b40e9538SIgor Chernyshev #endif
1116b40e9538SIgor Chernyshev 
1117b40e9538SIgor Chernyshev static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
1118b40e9538SIgor Chernyshev {
1119b40e9538SIgor Chernyshev 	struct prodigy_hifi_spec *spec;
1120b40e9538SIgor Chernyshev 
11216b8d6e55SJulian Scheel 	ice->vt1720 = 0;
11226b8d6e55SJulian Scheel 	ice->vt1724 = 1;
11236b8d6e55SJulian Scheel 
11246b8d6e55SJulian Scheel 	ice->num_total_dacs = 1;
11256b8d6e55SJulian Scheel 	ice->num_total_adcs = 1;
11266b8d6e55SJulian Scheel 
11276b8d6e55SJulian Scheel 	/* HACK - use this as the SPDIF source.
11286b8d6e55SJulian Scheel 	* don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
11296b8d6e55SJulian Scheel 	*/
11306b8d6e55SJulian Scheel 	ice->gpio.saved[0] = 0;
113125985edcSLucas De Marchi 	/* to remember the register values */
11326b8d6e55SJulian Scheel 
11336b8d6e55SJulian Scheel 	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
11346b8d6e55SJulian Scheel 	if (! ice->akm)
11356b8d6e55SJulian Scheel 		return -ENOMEM;
11366b8d6e55SJulian Scheel 	ice->akm_codecs = 1;
11376b8d6e55SJulian Scheel 
11387cda8ba9STakashi Iwai 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
11397cda8ba9STakashi Iwai 	if (!spec)
11407cda8ba9STakashi Iwai 		return -ENOMEM;
11417cda8ba9STakashi Iwai 	ice->spec = spec;
11427cda8ba9STakashi Iwai 
1143*c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP
1144b40e9538SIgor Chernyshev 	ice->pm_resume = &prodigy_hd2_resume;
1145b40e9538SIgor Chernyshev 	ice->pm_suspend_enabled = 1;
1146b40e9538SIgor Chernyshev #endif
11476b8d6e55SJulian Scheel 
1148b40e9538SIgor Chernyshev 	ak4396_init(ice);
11496b8d6e55SJulian Scheel 
11506b8d6e55SJulian Scheel 	return 0;
11516b8d6e55SJulian Scheel }
11526b8d6e55SJulian Scheel 
11536b8d6e55SJulian Scheel 
11546b8d6e55SJulian Scheel static unsigned char prodigy71hifi_eeprom[] __devinitdata = {
11556b8d6e55SJulian Scheel 	0x4b,   /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
11566b8d6e55SJulian Scheel 	0x80,   /* ACLINK: I2S */
11576b8d6e55SJulian Scheel 	0xfc,   /* I2S: vol, 96k, 24bit, 192k */
11586b8d6e55SJulian Scheel 	0xc3,   /* SPDIF: out-en, out-int, spdif-in */
11596b8d6e55SJulian Scheel 	0xff,   /* GPIO_DIR */
11606b8d6e55SJulian Scheel 	0xff,   /* GPIO_DIR1 */
11616b8d6e55SJulian Scheel 	0x5f,   /* GPIO_DIR2 */
11626b8d6e55SJulian Scheel 	0x00,   /* GPIO_MASK */
11636b8d6e55SJulian Scheel 	0x00,   /* GPIO_MASK1 */
11646b8d6e55SJulian Scheel 	0x00,   /* GPIO_MASK2 */
11656b8d6e55SJulian Scheel 	0x00,   /* GPIO_STATE */
11666b8d6e55SJulian Scheel 	0x00,   /* GPIO_STATE1 */
11676b8d6e55SJulian Scheel 	0x00,   /* GPIO_STATE2 */
11686b8d6e55SJulian Scheel };
11696b8d6e55SJulian Scheel 
11706b8d6e55SJulian Scheel static unsigned char prodigyhd2_eeprom[] __devinitdata = {
11716b8d6e55SJulian Scheel 	0x4b,   /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
11726b8d6e55SJulian Scheel 	0x80,   /* ACLINK: I2S */
11736b8d6e55SJulian Scheel 	0xfc,   /* I2S: vol, 96k, 24bit, 192k */
11746b8d6e55SJulian Scheel 	0xc3,   /* SPDIF: out-en, out-int, spdif-in */
11756b8d6e55SJulian Scheel 	0xff,   /* GPIO_DIR */
11766b8d6e55SJulian Scheel 	0xff,   /* GPIO_DIR1 */
11776b8d6e55SJulian Scheel 	0x5f,   /* GPIO_DIR2 */
11786b8d6e55SJulian Scheel 	0x00,   /* GPIO_MASK */
11796b8d6e55SJulian Scheel 	0x00,   /* GPIO_MASK1 */
11806b8d6e55SJulian Scheel 	0x00,   /* GPIO_MASK2 */
11816b8d6e55SJulian Scheel 	0x00,   /* GPIO_STATE */
11826b8d6e55SJulian Scheel 	0x00,   /* GPIO_STATE1 */
11836b8d6e55SJulian Scheel 	0x00,   /* GPIO_STATE2 */
11846b8d6e55SJulian Scheel };
11856b8d6e55SJulian Scheel 
11866b8d6e55SJulian Scheel static unsigned char fortissimo4_eeprom[] __devinitdata = {
11876b8d6e55SJulian Scheel 	0x43,   /* SYSCONF: clock 512, ADC, 4DACs */
11886b8d6e55SJulian Scheel 	0x80,   /* ACLINK: I2S */
11896b8d6e55SJulian Scheel 	0xfc,   /* I2S: vol, 96k, 24bit, 192k */
11906b8d6e55SJulian Scheel 	0xc1,   /* SPDIF: out-en, out-int */
11916b8d6e55SJulian Scheel 	0xff,   /* GPIO_DIR */
11926b8d6e55SJulian Scheel 	0xff,   /* GPIO_DIR1 */
11936b8d6e55SJulian Scheel 	0x5f,   /* GPIO_DIR2 */
11946b8d6e55SJulian Scheel 	0x00,   /* GPIO_MASK */
11956b8d6e55SJulian Scheel 	0x00,   /* GPIO_MASK1 */
11966b8d6e55SJulian Scheel 	0x00,   /* GPIO_MASK2 */
11976b8d6e55SJulian Scheel 	0x00,   /* GPIO_STATE */
11986b8d6e55SJulian Scheel 	0x00,   /* GPIO_STATE1 */
11996b8d6e55SJulian Scheel 	0x00,   /* GPIO_STATE2 */
12006b8d6e55SJulian Scheel };
12016b8d6e55SJulian Scheel 
12026b8d6e55SJulian Scheel /* entry point */
12036b8d6e55SJulian Scheel struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] __devinitdata = {
12046b8d6e55SJulian Scheel 	{
12056b8d6e55SJulian Scheel 		.subvendor = VT1724_SUBDEVICE_PRODIGY_HIFI,
12066b8d6e55SJulian Scheel 		.name = "Audiotrak Prodigy 7.1 HiFi",
12076b8d6e55SJulian Scheel 		.model = "prodigy71hifi",
12086b8d6e55SJulian Scheel 		.chip_init = prodigy_hifi_init,
12096b8d6e55SJulian Scheel 		.build_controls = prodigy_hifi_add_controls,
12106b8d6e55SJulian Scheel 		.eeprom_size = sizeof(prodigy71hifi_eeprom),
12116b8d6e55SJulian Scheel 		.eeprom_data = prodigy71hifi_eeprom,
12126b8d6e55SJulian Scheel 		.driver = "Prodigy71HIFI",
12136b8d6e55SJulian Scheel 	},
12146b8d6e55SJulian Scheel 	{
12156b8d6e55SJulian Scheel 	.subvendor = VT1724_SUBDEVICE_PRODIGY_HD2,
12166b8d6e55SJulian Scheel 	.name = "Audiotrak Prodigy HD2",
12176b8d6e55SJulian Scheel 	.model = "prodigyhd2",
12186b8d6e55SJulian Scheel 	.chip_init = prodigy_hd2_init,
12196b8d6e55SJulian Scheel 	.build_controls = prodigy_hd2_add_controls,
12206b8d6e55SJulian Scheel 	.eeprom_size = sizeof(prodigyhd2_eeprom),
12216b8d6e55SJulian Scheel 	.eeprom_data = prodigyhd2_eeprom,
12226b8d6e55SJulian Scheel 	.driver = "Prodigy71HD2",
12236b8d6e55SJulian Scheel 	},
12246b8d6e55SJulian Scheel 	{
12256b8d6e55SJulian Scheel 		.subvendor = VT1724_SUBDEVICE_FORTISSIMO4,
12266b8d6e55SJulian Scheel 		.name = "Hercules Fortissimo IV",
12276b8d6e55SJulian Scheel 		.model = "fortissimo4",
12286b8d6e55SJulian Scheel 		.chip_init = prodigy_hifi_init,
12296b8d6e55SJulian Scheel 		.build_controls = prodigy_hifi_add_controls,
12306b8d6e55SJulian Scheel 		.eeprom_size = sizeof(fortissimo4_eeprom),
12316b8d6e55SJulian Scheel 		.eeprom_data = fortissimo4_eeprom,
12326b8d6e55SJulian Scheel 		.driver = "Fortissimo4",
12336b8d6e55SJulian Scheel 	},
12346b8d6e55SJulian Scheel 	{ } /* terminator */
12356b8d6e55SJulian Scheel };
12366b8d6e55SJulian Scheel 
1237