11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 26b8d6e55SJulian Scheel /* 36b8d6e55SJulian Scheel * ALSA driver for ICEnsemble VT1724 (Envy24HT) 46b8d6e55SJulian Scheel * 56b8d6e55SJulian Scheel * Lowlevel functions for Audiotrak Prodigy 7.1 Hifi 66b8d6e55SJulian Scheel * based on pontis.c 76b8d6e55SJulian Scheel * 86b8d6e55SJulian Scheel * Copyright (c) 2007 Julian Scheel <julian@jusst.de> 96b8d6e55SJulian Scheel * Copyright (c) 2007 allank 106b8d6e55SJulian Scheel * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> 116b8d6e55SJulian Scheel */ 126b8d6e55SJulian Scheel 136b8d6e55SJulian Scheel 146b8d6e55SJulian Scheel #include <linux/delay.h> 156b8d6e55SJulian Scheel #include <linux/interrupt.h> 166b8d6e55SJulian Scheel #include <linux/init.h> 176b8d6e55SJulian Scheel #include <linux/slab.h> 186b8d6e55SJulian Scheel #include <linux/mutex.h> 196b8d6e55SJulian Scheel 206b8d6e55SJulian Scheel #include <sound/core.h> 216b8d6e55SJulian Scheel #include <sound/info.h> 226b8d6e55SJulian Scheel #include <sound/tlv.h> 236b8d6e55SJulian Scheel 246b8d6e55SJulian Scheel #include "ice1712.h" 256b8d6e55SJulian Scheel #include "envy24ht.h" 266b8d6e55SJulian Scheel #include "prodigy_hifi.h" 276b8d6e55SJulian Scheel 287cda8ba9STakashi Iwai struct prodigy_hifi_spec { 297cda8ba9STakashi Iwai unsigned short master[2]; 307cda8ba9STakashi Iwai unsigned short vol[8]; 317cda8ba9STakashi Iwai }; 327cda8ba9STakashi Iwai 336b8d6e55SJulian Scheel /* I2C addresses */ 346b8d6e55SJulian Scheel #define WM_DEV 0x34 356b8d6e55SJulian Scheel 366b8d6e55SJulian Scheel /* WM8776 registers */ 376b8d6e55SJulian Scheel #define WM_HP_ATTEN_L 0x00 /* headphone left attenuation */ 386b8d6e55SJulian Scheel #define WM_HP_ATTEN_R 0x01 /* headphone left attenuation */ 396b8d6e55SJulian Scheel #define WM_HP_MASTER 0x02 /* headphone master (both channels), 406b8d6e55SJulian Scheel override LLR */ 416b8d6e55SJulian Scheel #define WM_DAC_ATTEN_L 0x03 /* digital left attenuation */ 426b8d6e55SJulian Scheel #define WM_DAC_ATTEN_R 0x04 436b8d6e55SJulian Scheel #define WM_DAC_MASTER 0x05 446b8d6e55SJulian Scheel #define WM_PHASE_SWAP 0x06 /* DAC phase swap */ 456b8d6e55SJulian Scheel #define WM_DAC_CTRL1 0x07 466b8d6e55SJulian Scheel #define WM_DAC_MUTE 0x08 476b8d6e55SJulian Scheel #define WM_DAC_CTRL2 0x09 486b8d6e55SJulian Scheel #define WM_DAC_INT 0x0a 496b8d6e55SJulian Scheel #define WM_ADC_INT 0x0b 506b8d6e55SJulian Scheel #define WM_MASTER_CTRL 0x0c 516b8d6e55SJulian Scheel #define WM_POWERDOWN 0x0d 526b8d6e55SJulian Scheel #define WM_ADC_ATTEN_L 0x0e 536b8d6e55SJulian Scheel #define WM_ADC_ATTEN_R 0x0f 546b8d6e55SJulian Scheel #define WM_ALC_CTRL1 0x10 556b8d6e55SJulian Scheel #define WM_ALC_CTRL2 0x11 566b8d6e55SJulian Scheel #define WM_ALC_CTRL3 0x12 576b8d6e55SJulian Scheel #define WM_NOISE_GATE 0x13 586b8d6e55SJulian Scheel #define WM_LIMITER 0x14 596b8d6e55SJulian Scheel #define WM_ADC_MUX 0x15 606b8d6e55SJulian Scheel #define WM_OUT_MUX 0x16 616b8d6e55SJulian Scheel #define WM_RESET 0x17 626b8d6e55SJulian Scheel 636b8d6e55SJulian Scheel /* Analog Recording Source :- Mic, LineIn, CD/Video, */ 646b8d6e55SJulian Scheel 656b8d6e55SJulian Scheel /* implement capture source select control for WM8776 */ 666b8d6e55SJulian Scheel 676b8d6e55SJulian Scheel #define WM_AIN1 "AIN1" 686b8d6e55SJulian Scheel #define WM_AIN2 "AIN2" 696b8d6e55SJulian Scheel #define WM_AIN3 "AIN3" 706b8d6e55SJulian Scheel #define WM_AIN4 "AIN4" 716b8d6e55SJulian Scheel #define WM_AIN5 "AIN5" 726b8d6e55SJulian Scheel 736b8d6e55SJulian Scheel /* GPIO pins of envy24ht connected to wm8766 */ 746b8d6e55SJulian Scheel #define WM8766_SPI_CLK (1<<17) /* CLK, Pin97 on ICE1724 */ 756b8d6e55SJulian Scheel #define WM8766_SPI_MD (1<<16) /* DATA VT1724 -> WM8766, Pin96 */ 766b8d6e55SJulian Scheel #define WM8766_SPI_ML (1<<18) /* Latch, Pin98 */ 776b8d6e55SJulian Scheel 786b8d6e55SJulian Scheel /* WM8766 registers */ 796b8d6e55SJulian Scheel #define WM8766_DAC_CTRL 0x02 /* DAC Control */ 806b8d6e55SJulian Scheel #define WM8766_INT_CTRL 0x03 /* Interface Control */ 816b8d6e55SJulian Scheel #define WM8766_DAC_CTRL2 0x09 826b8d6e55SJulian Scheel #define WM8766_DAC_CTRL3 0x0a 836b8d6e55SJulian Scheel #define WM8766_RESET 0x1f 846b8d6e55SJulian Scheel #define WM8766_LDA1 0x00 856b8d6e55SJulian Scheel #define WM8766_LDA2 0x04 866b8d6e55SJulian Scheel #define WM8766_LDA3 0x06 876b8d6e55SJulian Scheel #define WM8766_RDA1 0x01 886b8d6e55SJulian Scheel #define WM8766_RDA2 0x05 896b8d6e55SJulian Scheel #define WM8766_RDA3 0x07 906b8d6e55SJulian Scheel #define WM8766_MUTE1 0x0C 916b8d6e55SJulian Scheel #define WM8766_MUTE2 0x0F 926b8d6e55SJulian Scheel 936b8d6e55SJulian Scheel 946b8d6e55SJulian Scheel /* 956b8d6e55SJulian Scheel * Prodigy HD2 966b8d6e55SJulian Scheel */ 976b8d6e55SJulian Scheel #define AK4396_ADDR 0x00 986b8d6e55SJulian Scheel #define AK4396_CSN (1 << 8) /* CSN->GPIO8, pin 75 */ 996b8d6e55SJulian Scheel #define AK4396_CCLK (1 << 9) /* CCLK->GPIO9, pin 76 */ 1006b8d6e55SJulian Scheel #define AK4396_CDTI (1 << 10) /* CDTI->GPIO10, pin 77 */ 1016b8d6e55SJulian Scheel 1026b8d6e55SJulian Scheel /* ak4396 registers */ 1036b8d6e55SJulian Scheel #define AK4396_CTRL1 0x00 1046b8d6e55SJulian Scheel #define AK4396_CTRL2 0x01 1056b8d6e55SJulian Scheel #define AK4396_CTRL3 0x02 1066b8d6e55SJulian Scheel #define AK4396_LCH_ATT 0x03 1076b8d6e55SJulian Scheel #define AK4396_RCH_ATT 0x04 1086b8d6e55SJulian Scheel 1096b8d6e55SJulian Scheel 1106b8d6e55SJulian Scheel /* 1116b8d6e55SJulian Scheel * get the current register value of WM codec 1126b8d6e55SJulian Scheel */ 1136b8d6e55SJulian Scheel static unsigned short wm_get(struct snd_ice1712 *ice, int reg) 1146b8d6e55SJulian Scheel { 1156b8d6e55SJulian Scheel reg <<= 1; 1166b8d6e55SJulian Scheel return ((unsigned short)ice->akm[0].images[reg] << 8) | 1176b8d6e55SJulian Scheel ice->akm[0].images[reg + 1]; 1186b8d6e55SJulian Scheel } 1196b8d6e55SJulian Scheel 1206b8d6e55SJulian Scheel /* 1216b8d6e55SJulian Scheel * set the register value of WM codec and remember it 1226b8d6e55SJulian Scheel */ 1236b8d6e55SJulian Scheel static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val) 1246b8d6e55SJulian Scheel { 1256b8d6e55SJulian Scheel unsigned short cval; 1266b8d6e55SJulian Scheel cval = (reg << 9) | val; 1276b8d6e55SJulian Scheel snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff); 1286b8d6e55SJulian Scheel } 1296b8d6e55SJulian Scheel 1306b8d6e55SJulian Scheel static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val) 1316b8d6e55SJulian Scheel { 1326b8d6e55SJulian Scheel wm_put_nocache(ice, reg, val); 1336b8d6e55SJulian Scheel reg <<= 1; 1346b8d6e55SJulian Scheel ice->akm[0].images[reg] = val >> 8; 1356b8d6e55SJulian Scheel ice->akm[0].images[reg + 1] = val; 1366b8d6e55SJulian Scheel } 1376b8d6e55SJulian Scheel 1386b8d6e55SJulian Scheel /* 1396b8d6e55SJulian Scheel * write data in the SPI mode 1406b8d6e55SJulian Scheel */ 1416b8d6e55SJulian Scheel 1426b8d6e55SJulian Scheel static void set_gpio_bit(struct snd_ice1712 *ice, unsigned int bit, int val) 1436b8d6e55SJulian Scheel { 1446b8d6e55SJulian Scheel unsigned int tmp = snd_ice1712_gpio_read(ice); 1456b8d6e55SJulian Scheel if (val) 1466b8d6e55SJulian Scheel tmp |= bit; 1476b8d6e55SJulian Scheel else 1486b8d6e55SJulian Scheel tmp &= ~bit; 1496b8d6e55SJulian Scheel snd_ice1712_gpio_write(ice, tmp); 1506b8d6e55SJulian Scheel } 1516b8d6e55SJulian Scheel 1526b8d6e55SJulian Scheel /* 1536b8d6e55SJulian Scheel * SPI implementation for WM8766 codec - only writing supported, no readback 1546b8d6e55SJulian Scheel */ 1556b8d6e55SJulian Scheel 1566b8d6e55SJulian Scheel static void wm8766_spi_send_word(struct snd_ice1712 *ice, unsigned int data) 1576b8d6e55SJulian Scheel { 1586b8d6e55SJulian Scheel int i; 1596b8d6e55SJulian Scheel for (i = 0; i < 16; i++) { 1606b8d6e55SJulian Scheel set_gpio_bit(ice, WM8766_SPI_CLK, 0); 1616b8d6e55SJulian Scheel udelay(1); 1626b8d6e55SJulian Scheel set_gpio_bit(ice, WM8766_SPI_MD, data & 0x8000); 1636b8d6e55SJulian Scheel udelay(1); 1646b8d6e55SJulian Scheel set_gpio_bit(ice, WM8766_SPI_CLK, 1); 1656b8d6e55SJulian Scheel udelay(1); 1666b8d6e55SJulian Scheel data <<= 1; 1676b8d6e55SJulian Scheel } 1686b8d6e55SJulian Scheel } 1696b8d6e55SJulian Scheel 1707cda8ba9STakashi Iwai static void wm8766_spi_write(struct snd_ice1712 *ice, unsigned int reg, 1717cda8ba9STakashi Iwai unsigned int data) 1726b8d6e55SJulian Scheel { 1736b8d6e55SJulian Scheel unsigned int block; 1746b8d6e55SJulian Scheel 1756b8d6e55SJulian Scheel snd_ice1712_gpio_set_dir(ice, WM8766_SPI_MD| 1766b8d6e55SJulian Scheel WM8766_SPI_CLK|WM8766_SPI_ML); 1776b8d6e55SJulian Scheel snd_ice1712_gpio_set_mask(ice, ~(WM8766_SPI_MD| 1786b8d6e55SJulian Scheel WM8766_SPI_CLK|WM8766_SPI_ML)); 1796b8d6e55SJulian Scheel /* latch must be low when writing */ 1806b8d6e55SJulian Scheel set_gpio_bit(ice, WM8766_SPI_ML, 0); 1816b8d6e55SJulian Scheel block = (reg << 9) | (data & 0x1ff); 1826b8d6e55SJulian Scheel wm8766_spi_send_word(ice, block); /* REGISTER ADDRESS */ 1836b8d6e55SJulian Scheel /* release latch */ 1846b8d6e55SJulian Scheel set_gpio_bit(ice, WM8766_SPI_ML, 1); 1856b8d6e55SJulian Scheel udelay(1); 1866b8d6e55SJulian Scheel /* restore */ 1876b8d6e55SJulian Scheel snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); 1886b8d6e55SJulian Scheel snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); 1896b8d6e55SJulian Scheel } 1906b8d6e55SJulian Scheel 1916b8d6e55SJulian Scheel 1926b8d6e55SJulian Scheel /* 1936b8d6e55SJulian Scheel * serial interface for ak4396 - only writing supported, no readback 1946b8d6e55SJulian Scheel */ 1956b8d6e55SJulian Scheel 1966b8d6e55SJulian Scheel static void ak4396_send_word(struct snd_ice1712 *ice, unsigned int data) 1976b8d6e55SJulian Scheel { 1986b8d6e55SJulian Scheel int i; 1996b8d6e55SJulian Scheel for (i = 0; i < 16; i++) { 2006b8d6e55SJulian Scheel set_gpio_bit(ice, AK4396_CCLK, 0); 2016b8d6e55SJulian Scheel udelay(1); 2026b8d6e55SJulian Scheel set_gpio_bit(ice, AK4396_CDTI, data & 0x8000); 2036b8d6e55SJulian Scheel udelay(1); 2046b8d6e55SJulian Scheel set_gpio_bit(ice, AK4396_CCLK, 1); 2056b8d6e55SJulian Scheel udelay(1); 2066b8d6e55SJulian Scheel data <<= 1; 2076b8d6e55SJulian Scheel } 2086b8d6e55SJulian Scheel } 2096b8d6e55SJulian Scheel 2107cda8ba9STakashi Iwai static void ak4396_write(struct snd_ice1712 *ice, unsigned int reg, 2117cda8ba9STakashi Iwai unsigned int data) 2126b8d6e55SJulian Scheel { 2136b8d6e55SJulian Scheel unsigned int block; 2146b8d6e55SJulian Scheel 2156b8d6e55SJulian Scheel snd_ice1712_gpio_set_dir(ice, AK4396_CSN|AK4396_CCLK|AK4396_CDTI); 2166b8d6e55SJulian Scheel snd_ice1712_gpio_set_mask(ice, ~(AK4396_CSN|AK4396_CCLK|AK4396_CDTI)); 2176b8d6e55SJulian Scheel /* latch must be low when writing */ 2186b8d6e55SJulian Scheel set_gpio_bit(ice, AK4396_CSN, 0); 2196b8d6e55SJulian Scheel block = ((AK4396_ADDR & 0x03) << 14) | (1 << 13) | 2206b8d6e55SJulian Scheel ((reg & 0x1f) << 8) | (data & 0xff); 2216b8d6e55SJulian Scheel ak4396_send_word(ice, block); /* REGISTER ADDRESS */ 2226b8d6e55SJulian Scheel /* release latch */ 2236b8d6e55SJulian Scheel set_gpio_bit(ice, AK4396_CSN, 1); 2246b8d6e55SJulian Scheel udelay(1); 2256b8d6e55SJulian Scheel /* restore */ 2266b8d6e55SJulian Scheel snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); 2276b8d6e55SJulian Scheel snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); 2286b8d6e55SJulian Scheel } 2296b8d6e55SJulian Scheel 2306b8d6e55SJulian Scheel 2316b8d6e55SJulian Scheel /* 2326b8d6e55SJulian Scheel * ak4396 mixers 2336b8d6e55SJulian Scheel */ 2346b8d6e55SJulian Scheel 2356b8d6e55SJulian Scheel 2366b8d6e55SJulian Scheel 2376b8d6e55SJulian Scheel /* 2386b8d6e55SJulian Scheel * DAC volume attenuation mixer control (-64dB to 0dB) 2396b8d6e55SJulian Scheel */ 2406b8d6e55SJulian Scheel 2417cda8ba9STakashi Iwai static int ak4396_dac_vol_info(struct snd_kcontrol *kcontrol, 2427cda8ba9STakashi Iwai struct snd_ctl_elem_info *uinfo) 2436b8d6e55SJulian Scheel { 2446b8d6e55SJulian Scheel uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 2456b8d6e55SJulian Scheel uinfo->count = 2; 2466b8d6e55SJulian Scheel uinfo->value.integer.min = 0; /* mute */ 2476b8d6e55SJulian Scheel uinfo->value.integer.max = 0xFF; /* linear */ 2486b8d6e55SJulian Scheel return 0; 2496b8d6e55SJulian Scheel } 2506b8d6e55SJulian Scheel 2517cda8ba9STakashi Iwai static int ak4396_dac_vol_get(struct snd_kcontrol *kcontrol, 2527cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2536b8d6e55SJulian Scheel { 2546b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 2557cda8ba9STakashi Iwai struct prodigy_hifi_spec *spec = ice->spec; 2566b8d6e55SJulian Scheel int i; 2576b8d6e55SJulian Scheel 2587cda8ba9STakashi Iwai for (i = 0; i < 2; i++) 2597cda8ba9STakashi Iwai ucontrol->value.integer.value[i] = spec->vol[i]; 2607cda8ba9STakashi Iwai 2616b8d6e55SJulian Scheel return 0; 2626b8d6e55SJulian Scheel } 2636b8d6e55SJulian Scheel 2646b8d6e55SJulian Scheel static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 2656b8d6e55SJulian Scheel { 2666b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 2677cda8ba9STakashi Iwai struct prodigy_hifi_spec *spec = ice->spec; 2686b8d6e55SJulian Scheel int i; 2696b8d6e55SJulian Scheel int change = 0; 2706b8d6e55SJulian Scheel 2716b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 2726b8d6e55SJulian Scheel for (i = 0; i < 2; i++) { 2737cda8ba9STakashi Iwai if (ucontrol->value.integer.value[i] != spec->vol[i]) { 2747cda8ba9STakashi Iwai spec->vol[i] = ucontrol->value.integer.value[i]; 2756b8d6e55SJulian Scheel ak4396_write(ice, AK4396_LCH_ATT + i, 2767cda8ba9STakashi Iwai spec->vol[i] & 0xff); 2776b8d6e55SJulian Scheel change = 1; 2786b8d6e55SJulian Scheel } 2796b8d6e55SJulian Scheel } 2806b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 2816b8d6e55SJulian Scheel return change; 2826b8d6e55SJulian Scheel } 2836b8d6e55SJulian Scheel 2846b8d6e55SJulian Scheel static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); 2853737e2beSMatteo Frigo static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); 2866b8d6e55SJulian Scheel 287b4e5e707STakashi Iwai static const struct snd_kcontrol_new prodigy_hd2_controls[] = { 2886b8d6e55SJulian Scheel { 2896b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2906b8d6e55SJulian Scheel .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 2916b8d6e55SJulian Scheel SNDRV_CTL_ELEM_ACCESS_TLV_READ), 2926b8d6e55SJulian Scheel .name = "Front Playback Volume", 2936b8d6e55SJulian Scheel .info = ak4396_dac_vol_info, 2946b8d6e55SJulian Scheel .get = ak4396_dac_vol_get, 2956b8d6e55SJulian Scheel .put = ak4396_dac_vol_put, 2963737e2beSMatteo Frigo .tlv = { .p = ak4396_db_scale }, 2976b8d6e55SJulian Scheel }, 2986b8d6e55SJulian Scheel }; 2996b8d6e55SJulian Scheel 3006b8d6e55SJulian Scheel 3016b8d6e55SJulian Scheel /* --------------- */ 3026b8d6e55SJulian Scheel 30301655193SStefan Agner #define WM_VOL_MAX 255 3046b8d6e55SJulian Scheel #define WM_VOL_MUTE 0x8000 3056b8d6e55SJulian Scheel 3066b8d6e55SJulian Scheel 3076b8d6e55SJulian Scheel #define DAC_0dB 0xff 3086b8d6e55SJulian Scheel #define DAC_RES 128 3096b8d6e55SJulian Scheel #define DAC_MIN (DAC_0dB - DAC_RES) 3106b8d6e55SJulian Scheel 3116b8d6e55SJulian Scheel 3127cda8ba9STakashi Iwai static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, 3137cda8ba9STakashi Iwai unsigned short vol, unsigned short master) 3146b8d6e55SJulian Scheel { 3156b8d6e55SJulian Scheel unsigned char nvol; 3166b8d6e55SJulian Scheel 3176b8d6e55SJulian Scheel if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE)) 3186b8d6e55SJulian Scheel nvol = 0; 3196b8d6e55SJulian Scheel else { 3206b8d6e55SJulian Scheel nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128) 3216b8d6e55SJulian Scheel & WM_VOL_MAX; 3226b8d6e55SJulian Scheel nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff; 3236b8d6e55SJulian Scheel } 3246b8d6e55SJulian Scheel 3256b8d6e55SJulian Scheel wm_put(ice, index, nvol); 3266b8d6e55SJulian Scheel wm_put_nocache(ice, index, 0x100 | nvol); 3276b8d6e55SJulian Scheel } 3286b8d6e55SJulian Scheel 3297cda8ba9STakashi Iwai static void wm8766_set_vol(struct snd_ice1712 *ice, unsigned int index, 3307cda8ba9STakashi Iwai unsigned short vol, unsigned short master) 3316b8d6e55SJulian Scheel { 3326b8d6e55SJulian Scheel unsigned char nvol; 3336b8d6e55SJulian Scheel 3346b8d6e55SJulian Scheel if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE)) 3356b8d6e55SJulian Scheel nvol = 0; 3366b8d6e55SJulian Scheel else { 3376b8d6e55SJulian Scheel nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128) 3386b8d6e55SJulian Scheel & WM_VOL_MAX; 3396b8d6e55SJulian Scheel nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff; 3406b8d6e55SJulian Scheel } 3416b8d6e55SJulian Scheel 3426b8d6e55SJulian Scheel wm8766_spi_write(ice, index, (0x0100 | nvol)); 3436b8d6e55SJulian Scheel } 3446b8d6e55SJulian Scheel 3456b8d6e55SJulian Scheel 3466b8d6e55SJulian Scheel /* 3476b8d6e55SJulian Scheel * DAC volume attenuation mixer control (-64dB to 0dB) 3486b8d6e55SJulian Scheel */ 3496b8d6e55SJulian Scheel 3507cda8ba9STakashi Iwai static int wm_dac_vol_info(struct snd_kcontrol *kcontrol, 3517cda8ba9STakashi Iwai struct snd_ctl_elem_info *uinfo) 3526b8d6e55SJulian Scheel { 3536b8d6e55SJulian Scheel uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3546b8d6e55SJulian Scheel uinfo->count = 2; 3556b8d6e55SJulian Scheel uinfo->value.integer.min = 0; /* mute */ 3566b8d6e55SJulian Scheel uinfo->value.integer.max = DAC_RES; /* 0dB, 0.5dB step */ 3576b8d6e55SJulian Scheel return 0; 3586b8d6e55SJulian Scheel } 3596b8d6e55SJulian Scheel 3607cda8ba9STakashi Iwai static int wm_dac_vol_get(struct snd_kcontrol *kcontrol, 3617cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 3626b8d6e55SJulian Scheel { 3636b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 3647cda8ba9STakashi Iwai struct prodigy_hifi_spec *spec = ice->spec; 3656b8d6e55SJulian Scheel int i; 3666b8d6e55SJulian Scheel 3677cda8ba9STakashi Iwai for (i = 0; i < 2; i++) 3686b8d6e55SJulian Scheel ucontrol->value.integer.value[i] = 3697cda8ba9STakashi Iwai spec->vol[2 + i] & ~WM_VOL_MUTE; 3706b8d6e55SJulian Scheel return 0; 3716b8d6e55SJulian Scheel } 3726b8d6e55SJulian Scheel 3736b8d6e55SJulian Scheel static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 3746b8d6e55SJulian Scheel { 3756b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 3767cda8ba9STakashi Iwai struct prodigy_hifi_spec *spec = ice->spec; 3776b8d6e55SJulian Scheel int i, idx, change = 0; 3786b8d6e55SJulian Scheel 3796b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 3806b8d6e55SJulian Scheel for (i = 0; i < 2; i++) { 3817cda8ba9STakashi Iwai if (ucontrol->value.integer.value[i] != spec->vol[2 + i]) { 3826b8d6e55SJulian Scheel idx = WM_DAC_ATTEN_L + i; 3837cda8ba9STakashi Iwai spec->vol[2 + i] &= WM_VOL_MUTE; 3847cda8ba9STakashi Iwai spec->vol[2 + i] |= ucontrol->value.integer.value[i]; 3857cda8ba9STakashi Iwai wm_set_vol(ice, idx, spec->vol[2 + i], spec->master[i]); 3866b8d6e55SJulian Scheel change = 1; 3876b8d6e55SJulian Scheel } 3886b8d6e55SJulian Scheel } 3896b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 3906b8d6e55SJulian Scheel return change; 3916b8d6e55SJulian Scheel } 3926b8d6e55SJulian Scheel 3936b8d6e55SJulian Scheel 3946b8d6e55SJulian Scheel /* 3956b8d6e55SJulian Scheel * WM8766 DAC volume attenuation mixer control 3966b8d6e55SJulian Scheel */ 3977cda8ba9STakashi Iwai static int wm8766_vol_info(struct snd_kcontrol *kcontrol, 3987cda8ba9STakashi Iwai struct snd_ctl_elem_info *uinfo) 3996b8d6e55SJulian Scheel { 4006b8d6e55SJulian Scheel int voices = kcontrol->private_value >> 8; 4016b8d6e55SJulian Scheel uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4026b8d6e55SJulian Scheel uinfo->count = voices; 4036b8d6e55SJulian Scheel uinfo->value.integer.min = 0; /* mute */ 4046b8d6e55SJulian Scheel uinfo->value.integer.max = DAC_RES; /* 0dB */ 4056b8d6e55SJulian Scheel return 0; 4066b8d6e55SJulian Scheel } 4076b8d6e55SJulian Scheel 4087cda8ba9STakashi Iwai static int wm8766_vol_get(struct snd_kcontrol *kcontrol, 4097cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4106b8d6e55SJulian Scheel { 4116b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 4127cda8ba9STakashi Iwai struct prodigy_hifi_spec *spec = ice->spec; 4136b8d6e55SJulian Scheel int i, ofs, voices; 4146b8d6e55SJulian Scheel 4156b8d6e55SJulian Scheel voices = kcontrol->private_value >> 8; 4166b8d6e55SJulian Scheel ofs = kcontrol->private_value & 0xff; 4176b8d6e55SJulian Scheel for (i = 0; i < voices; i++) 4187cda8ba9STakashi Iwai ucontrol->value.integer.value[i] = spec->vol[ofs + i]; 4196b8d6e55SJulian Scheel return 0; 4206b8d6e55SJulian Scheel } 4216b8d6e55SJulian Scheel 4226b8d6e55SJulian Scheel static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 4236b8d6e55SJulian Scheel { 4246b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 4257cda8ba9STakashi Iwai struct prodigy_hifi_spec *spec = ice->spec; 4266b8d6e55SJulian Scheel int i, idx, ofs, voices; 4276b8d6e55SJulian Scheel int change = 0; 4286b8d6e55SJulian Scheel 4296b8d6e55SJulian Scheel voices = kcontrol->private_value >> 8; 4306b8d6e55SJulian Scheel ofs = kcontrol->private_value & 0xff; 4316b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 4326b8d6e55SJulian Scheel for (i = 0; i < voices; i++) { 4337cda8ba9STakashi Iwai if (ucontrol->value.integer.value[i] != spec->vol[ofs + i]) { 4346b8d6e55SJulian Scheel idx = WM8766_LDA1 + ofs + i; 4357cda8ba9STakashi Iwai spec->vol[ofs + i] &= WM_VOL_MUTE; 4367cda8ba9STakashi Iwai spec->vol[ofs + i] |= ucontrol->value.integer.value[i]; 4376b8d6e55SJulian Scheel wm8766_set_vol(ice, idx, 4387cda8ba9STakashi Iwai spec->vol[ofs + i], spec->master[i]); 4396b8d6e55SJulian Scheel change = 1; 4406b8d6e55SJulian Scheel } 4416b8d6e55SJulian Scheel } 4426b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 4436b8d6e55SJulian Scheel return change; 4446b8d6e55SJulian Scheel } 4456b8d6e55SJulian Scheel 4466b8d6e55SJulian Scheel /* 4476b8d6e55SJulian Scheel * Master volume attenuation mixer control / applied to WM8776+WM8766 4486b8d6e55SJulian Scheel */ 4497cda8ba9STakashi Iwai static int wm_master_vol_info(struct snd_kcontrol *kcontrol, 4507cda8ba9STakashi Iwai struct snd_ctl_elem_info *uinfo) 4516b8d6e55SJulian Scheel { 4526b8d6e55SJulian Scheel uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4536b8d6e55SJulian Scheel uinfo->count = 2; 4546b8d6e55SJulian Scheel uinfo->value.integer.min = 0; 4556b8d6e55SJulian Scheel uinfo->value.integer.max = DAC_RES; 4566b8d6e55SJulian Scheel return 0; 4576b8d6e55SJulian Scheel } 4586b8d6e55SJulian Scheel 4597cda8ba9STakashi Iwai static int wm_master_vol_get(struct snd_kcontrol *kcontrol, 4607cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4616b8d6e55SJulian Scheel { 4626b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 4637cda8ba9STakashi Iwai struct prodigy_hifi_spec *spec = ice->spec; 4646b8d6e55SJulian Scheel int i; 4656b8d6e55SJulian Scheel for (i = 0; i < 2; i++) 4667cda8ba9STakashi Iwai ucontrol->value.integer.value[i] = spec->master[i]; 4676b8d6e55SJulian Scheel return 0; 4686b8d6e55SJulian Scheel } 4696b8d6e55SJulian Scheel 4707cda8ba9STakashi Iwai static int wm_master_vol_put(struct snd_kcontrol *kcontrol, 4717cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 4726b8d6e55SJulian Scheel { 4736b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 4747cda8ba9STakashi Iwai struct prodigy_hifi_spec *spec = ice->spec; 4756b8d6e55SJulian Scheel int ch, change = 0; 4766b8d6e55SJulian Scheel 4776b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 4786b8d6e55SJulian Scheel for (ch = 0; ch < 2; ch++) { 4797cda8ba9STakashi Iwai if (ucontrol->value.integer.value[ch] != spec->master[ch]) { 4807cda8ba9STakashi Iwai spec->master[ch] = ucontrol->value.integer.value[ch]; 4816b8d6e55SJulian Scheel 4826b8d6e55SJulian Scheel /* Apply to front DAC */ 4836b8d6e55SJulian Scheel wm_set_vol(ice, WM_DAC_ATTEN_L + ch, 4847cda8ba9STakashi Iwai spec->vol[2 + ch], spec->master[ch]); 4856b8d6e55SJulian Scheel 4866b8d6e55SJulian Scheel wm8766_set_vol(ice, WM8766_LDA1 + ch, 4877cda8ba9STakashi Iwai spec->vol[0 + ch], spec->master[ch]); 4886b8d6e55SJulian Scheel 4896b8d6e55SJulian Scheel wm8766_set_vol(ice, WM8766_LDA2 + ch, 4907cda8ba9STakashi Iwai spec->vol[4 + ch], spec->master[ch]); 4916b8d6e55SJulian Scheel 4926b8d6e55SJulian Scheel wm8766_set_vol(ice, WM8766_LDA3 + ch, 4937cda8ba9STakashi Iwai spec->vol[6 + ch], spec->master[ch]); 4946b8d6e55SJulian Scheel change = 1; 4956b8d6e55SJulian Scheel } 4966b8d6e55SJulian Scheel } 4976b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 4986b8d6e55SJulian Scheel return change; 4996b8d6e55SJulian Scheel } 5006b8d6e55SJulian Scheel 5016b8d6e55SJulian Scheel 5026b8d6e55SJulian Scheel /* KONSTI */ 5036b8d6e55SJulian Scheel 5047cda8ba9STakashi Iwai static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol, 5057cda8ba9STakashi Iwai struct snd_ctl_elem_info *uinfo) 5066b8d6e55SJulian Scheel { 507597da2e4STakashi Iwai static const char * const texts[32] = { 5087cda8ba9STakashi Iwai "NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2, 5096b8d6e55SJulian Scheel WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3, 5106b8d6e55SJulian Scheel WM_AIN1 "+" WM_AIN2 "+" WM_AIN3, 5116b8d6e55SJulian Scheel WM_AIN4, WM_AIN1 "+" WM_AIN4, WM_AIN2 "+" WM_AIN4, 5126b8d6e55SJulian Scheel WM_AIN1 "+" WM_AIN2 "+" WM_AIN4, 5136b8d6e55SJulian Scheel WM_AIN3 "+" WM_AIN4, WM_AIN1 "+" WM_AIN3 "+" WM_AIN4, 5146b8d6e55SJulian Scheel WM_AIN2 "+" WM_AIN3 "+" WM_AIN4, 5156b8d6e55SJulian Scheel WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4, 5166b8d6e55SJulian Scheel WM_AIN5, WM_AIN1 "+" WM_AIN5, WM_AIN2 "+" WM_AIN5, 5176b8d6e55SJulian Scheel WM_AIN1 "+" WM_AIN2 "+" WM_AIN5, 5186b8d6e55SJulian Scheel WM_AIN3 "+" WM_AIN5, WM_AIN1 "+" WM_AIN3 "+" WM_AIN5, 5196b8d6e55SJulian Scheel WM_AIN2 "+" WM_AIN3 "+" WM_AIN5, 5206b8d6e55SJulian Scheel WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN5, 5216b8d6e55SJulian Scheel WM_AIN4 "+" WM_AIN5, WM_AIN1 "+" WM_AIN4 "+" WM_AIN5, 5226b8d6e55SJulian Scheel WM_AIN2 "+" WM_AIN4 "+" WM_AIN5, 5236b8d6e55SJulian Scheel WM_AIN1 "+" WM_AIN2 "+" WM_AIN4 "+" WM_AIN5, 5246b8d6e55SJulian Scheel WM_AIN3 "+" WM_AIN4 "+" WM_AIN5, 5256b8d6e55SJulian Scheel WM_AIN1 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5, 5266b8d6e55SJulian Scheel WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5, 5277cda8ba9STakashi Iwai WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5 5287cda8ba9STakashi Iwai }; 5296b8d6e55SJulian Scheel 530597da2e4STakashi Iwai return snd_ctl_enum_info(uinfo, 1, 32, texts); 5316b8d6e55SJulian Scheel } 5326b8d6e55SJulian Scheel 5337cda8ba9STakashi Iwai static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol, 5347cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 5356b8d6e55SJulian Scheel { 5366b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 5376b8d6e55SJulian Scheel 5386b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 5396b8d6e55SJulian Scheel ucontrol->value.integer.value[0] = wm_get(ice, WM_ADC_MUX) & 0x1f; 5406b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 5416b8d6e55SJulian Scheel return 0; 5426b8d6e55SJulian Scheel } 5436b8d6e55SJulian Scheel 5447cda8ba9STakashi Iwai static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol, 5457cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 5466b8d6e55SJulian Scheel { 5476b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 5486b8d6e55SJulian Scheel unsigned short oval, nval; 5497cda8ba9STakashi Iwai int change = 0; 5506b8d6e55SJulian Scheel 5516b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 5526b8d6e55SJulian Scheel oval = wm_get(ice, WM_ADC_MUX); 5536b8d6e55SJulian Scheel nval = (oval & 0xe0) | ucontrol->value.integer.value[0]; 5546b8d6e55SJulian Scheel if (nval != oval) { 5556b8d6e55SJulian Scheel wm_put(ice, WM_ADC_MUX, nval); 5567cda8ba9STakashi Iwai change = 1; 5576b8d6e55SJulian Scheel } 5586b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 5597cda8ba9STakashi Iwai return change; 5606b8d6e55SJulian Scheel } 5616b8d6e55SJulian Scheel 5626b8d6e55SJulian Scheel /* KONSTI */ 5636b8d6e55SJulian Scheel 5646b8d6e55SJulian Scheel /* 5656b8d6e55SJulian Scheel * ADC gain mixer control (-64dB to 0dB) 5666b8d6e55SJulian Scheel */ 5676b8d6e55SJulian Scheel 5686b8d6e55SJulian Scheel #define ADC_0dB 0xcf 5696b8d6e55SJulian Scheel #define ADC_RES 128 5706b8d6e55SJulian Scheel #define ADC_MIN (ADC_0dB - ADC_RES) 5716b8d6e55SJulian Scheel 5727cda8ba9STakashi Iwai static int wm_adc_vol_info(struct snd_kcontrol *kcontrol, 5737cda8ba9STakashi Iwai struct snd_ctl_elem_info *uinfo) 5746b8d6e55SJulian Scheel { 5756b8d6e55SJulian Scheel uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 5766b8d6e55SJulian Scheel uinfo->count = 2; 5776b8d6e55SJulian Scheel uinfo->value.integer.min = 0; /* mute (-64dB) */ 5786b8d6e55SJulian Scheel uinfo->value.integer.max = ADC_RES; /* 0dB, 0.5dB step */ 5796b8d6e55SJulian Scheel return 0; 5806b8d6e55SJulian Scheel } 5816b8d6e55SJulian Scheel 5827cda8ba9STakashi Iwai static int wm_adc_vol_get(struct snd_kcontrol *kcontrol, 5837cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 5846b8d6e55SJulian Scheel { 5856b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 5866b8d6e55SJulian Scheel unsigned short val; 5876b8d6e55SJulian Scheel int i; 5886b8d6e55SJulian Scheel 5896b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 5906b8d6e55SJulian Scheel for (i = 0; i < 2; i++) { 5916b8d6e55SJulian Scheel val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff; 5926b8d6e55SJulian Scheel val = val > ADC_MIN ? (val - ADC_MIN) : 0; 5936b8d6e55SJulian Scheel ucontrol->value.integer.value[i] = val; 5946b8d6e55SJulian Scheel } 5956b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 5966b8d6e55SJulian Scheel return 0; 5976b8d6e55SJulian Scheel } 5986b8d6e55SJulian Scheel 5997cda8ba9STakashi Iwai static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, 6007cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 6016b8d6e55SJulian Scheel { 6026b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 6036b8d6e55SJulian Scheel unsigned short ovol, nvol; 6046b8d6e55SJulian Scheel int i, idx, change = 0; 6056b8d6e55SJulian Scheel 6066b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 6076b8d6e55SJulian Scheel for (i = 0; i < 2; i++) { 6086b8d6e55SJulian Scheel nvol = ucontrol->value.integer.value[i]; 6096b8d6e55SJulian Scheel nvol = nvol ? (nvol + ADC_MIN) : 0; 6106b8d6e55SJulian Scheel idx = WM_ADC_ATTEN_L + i; 6116b8d6e55SJulian Scheel ovol = wm_get(ice, idx) & 0xff; 6126b8d6e55SJulian Scheel if (ovol != nvol) { 6136b8d6e55SJulian Scheel wm_put(ice, idx, nvol); 6146b8d6e55SJulian Scheel change = 1; 6156b8d6e55SJulian Scheel } 6166b8d6e55SJulian Scheel } 6176b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 6186b8d6e55SJulian Scheel return change; 6196b8d6e55SJulian Scheel } 6206b8d6e55SJulian Scheel 6216b8d6e55SJulian Scheel /* 6226b8d6e55SJulian Scheel * ADC input mux mixer control 6236b8d6e55SJulian Scheel */ 6247cda8ba9STakashi Iwai #define wm_adc_mux_info snd_ctl_boolean_mono_info 6256b8d6e55SJulian Scheel 6267cda8ba9STakashi Iwai static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, 6277cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 6286b8d6e55SJulian Scheel { 6296b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 6306b8d6e55SJulian Scheel int bit = kcontrol->private_value; 6316b8d6e55SJulian Scheel 6326b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 6337cda8ba9STakashi Iwai ucontrol->value.integer.value[0] = 6347cda8ba9STakashi Iwai (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0; 6356b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 6366b8d6e55SJulian Scheel return 0; 6376b8d6e55SJulian Scheel } 6386b8d6e55SJulian Scheel 6397cda8ba9STakashi Iwai static int wm_adc_mux_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 int bit = kcontrol->private_value; 6446b8d6e55SJulian Scheel unsigned short oval, nval; 6456b8d6e55SJulian Scheel int change; 6466b8d6e55SJulian Scheel 6476b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 6486b8d6e55SJulian Scheel nval = oval = wm_get(ice, WM_ADC_MUX); 6496b8d6e55SJulian Scheel if (ucontrol->value.integer.value[0]) 6506b8d6e55SJulian Scheel nval |= (1 << bit); 6516b8d6e55SJulian Scheel else 6526b8d6e55SJulian Scheel nval &= ~(1 << bit); 6536b8d6e55SJulian Scheel change = nval != oval; 6546b8d6e55SJulian Scheel if (change) { 6556b8d6e55SJulian Scheel wm_put(ice, WM_ADC_MUX, nval); 6566b8d6e55SJulian Scheel } 6576b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 6586b8d6e55SJulian Scheel return 0; 6596b8d6e55SJulian Scheel } 6606b8d6e55SJulian Scheel 6616b8d6e55SJulian Scheel /* 6626b8d6e55SJulian Scheel * Analog bypass (In -> Out) 6636b8d6e55SJulian Scheel */ 6647cda8ba9STakashi Iwai #define wm_bypass_info snd_ctl_boolean_mono_info 6656b8d6e55SJulian Scheel 6667cda8ba9STakashi Iwai static int wm_bypass_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 6716b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 6727cda8ba9STakashi Iwai ucontrol->value.integer.value[0] = 6737cda8ba9STakashi Iwai (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0; 6746b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 6756b8d6e55SJulian Scheel return 0; 6766b8d6e55SJulian Scheel } 6776b8d6e55SJulian Scheel 6787cda8ba9STakashi Iwai static int wm_bypass_put(struct snd_kcontrol *kcontrol, 6797cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 6806b8d6e55SJulian Scheel { 6816b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 6826b8d6e55SJulian Scheel unsigned short val, oval; 6836b8d6e55SJulian Scheel int change = 0; 6846b8d6e55SJulian Scheel 6856b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 6866b8d6e55SJulian Scheel val = oval = wm_get(ice, WM_OUT_MUX); 6876b8d6e55SJulian Scheel if (ucontrol->value.integer.value[0]) 6886b8d6e55SJulian Scheel val |= 0x04; 6896b8d6e55SJulian Scheel else 6906b8d6e55SJulian Scheel val &= ~0x04; 6916b8d6e55SJulian Scheel if (val != oval) { 6926b8d6e55SJulian Scheel wm_put(ice, WM_OUT_MUX, val); 6936b8d6e55SJulian Scheel change = 1; 6946b8d6e55SJulian Scheel } 6956b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 6966b8d6e55SJulian Scheel return change; 6976b8d6e55SJulian Scheel } 6986b8d6e55SJulian Scheel 6996b8d6e55SJulian Scheel /* 7006b8d6e55SJulian Scheel * Left/Right swap 7016b8d6e55SJulian Scheel */ 7027cda8ba9STakashi Iwai #define wm_chswap_info snd_ctl_boolean_mono_info 7036b8d6e55SJulian Scheel 7047cda8ba9STakashi Iwai static int wm_chswap_get(struct snd_kcontrol *kcontrol, 7057cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 7066b8d6e55SJulian Scheel { 7076b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 7086b8d6e55SJulian Scheel 7096b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 7106b8d6e55SJulian Scheel ucontrol->value.integer.value[0] = 7116b8d6e55SJulian Scheel (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90; 7126b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 7136b8d6e55SJulian Scheel return 0; 7146b8d6e55SJulian Scheel } 7156b8d6e55SJulian Scheel 7167cda8ba9STakashi Iwai static int wm_chswap_put(struct snd_kcontrol *kcontrol, 7177cda8ba9STakashi Iwai struct snd_ctl_elem_value *ucontrol) 7186b8d6e55SJulian Scheel { 7196b8d6e55SJulian Scheel struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); 7206b8d6e55SJulian Scheel unsigned short val, oval; 7216b8d6e55SJulian Scheel int change = 0; 7226b8d6e55SJulian Scheel 7236b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 7246b8d6e55SJulian Scheel oval = wm_get(ice, WM_DAC_CTRL1); 7256b8d6e55SJulian Scheel val = oval & 0x0f; 7266b8d6e55SJulian Scheel if (ucontrol->value.integer.value[0]) 7276b8d6e55SJulian Scheel val |= 0x60; 7286b8d6e55SJulian Scheel else 7296b8d6e55SJulian Scheel val |= 0x90; 7306b8d6e55SJulian Scheel if (val != oval) { 7316b8d6e55SJulian Scheel wm_put(ice, WM_DAC_CTRL1, val); 7326b8d6e55SJulian Scheel wm_put_nocache(ice, WM_DAC_CTRL1, 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 /* 7416b8d6e55SJulian Scheel * mixers 7426b8d6e55SJulian Scheel */ 7436b8d6e55SJulian Scheel 744b4e5e707STakashi Iwai static const struct snd_kcontrol_new prodigy_hifi_controls[] = { 7456b8d6e55SJulian Scheel { 7466b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7476b8d6e55SJulian Scheel .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 7486b8d6e55SJulian Scheel SNDRV_CTL_ELEM_ACCESS_TLV_READ), 7496b8d6e55SJulian Scheel .name = "Master Playback Volume", 7506b8d6e55SJulian Scheel .info = wm_master_vol_info, 7516b8d6e55SJulian Scheel .get = wm_master_vol_get, 7526b8d6e55SJulian Scheel .put = wm_master_vol_put, 7536b8d6e55SJulian Scheel .tlv = { .p = db_scale_wm_dac } 7546b8d6e55SJulian Scheel }, 7556b8d6e55SJulian Scheel { 7566b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7576b8d6e55SJulian Scheel .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 7586b8d6e55SJulian Scheel SNDRV_CTL_ELEM_ACCESS_TLV_READ), 7596b8d6e55SJulian Scheel .name = "Front Playback Volume", 7606b8d6e55SJulian Scheel .info = wm_dac_vol_info, 7616b8d6e55SJulian Scheel .get = wm_dac_vol_get, 7626b8d6e55SJulian Scheel .put = wm_dac_vol_put, 7636b8d6e55SJulian Scheel .tlv = { .p = db_scale_wm_dac }, 7646b8d6e55SJulian Scheel }, 7656b8d6e55SJulian Scheel { 7666b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7676b8d6e55SJulian Scheel .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 7686b8d6e55SJulian Scheel SNDRV_CTL_ELEM_ACCESS_TLV_READ), 7696b8d6e55SJulian Scheel .name = "Rear Playback Volume", 7706b8d6e55SJulian Scheel .info = wm8766_vol_info, 7716b8d6e55SJulian Scheel .get = wm8766_vol_get, 7726b8d6e55SJulian Scheel .put = wm8766_vol_put, 7736b8d6e55SJulian Scheel .private_value = (2 << 8) | 0, 7746b8d6e55SJulian Scheel .tlv = { .p = db_scale_wm_dac }, 7756b8d6e55SJulian Scheel }, 7766b8d6e55SJulian Scheel { 7776b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7786b8d6e55SJulian Scheel .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 7796b8d6e55SJulian Scheel SNDRV_CTL_ELEM_ACCESS_TLV_READ), 7806b8d6e55SJulian Scheel .name = "Center Playback Volume", 7816b8d6e55SJulian Scheel .info = wm8766_vol_info, 7826b8d6e55SJulian Scheel .get = wm8766_vol_get, 7836b8d6e55SJulian Scheel .put = wm8766_vol_put, 7846b8d6e55SJulian Scheel .private_value = (1 << 8) | 4, 7856b8d6e55SJulian Scheel .tlv = { .p = db_scale_wm_dac } 7866b8d6e55SJulian Scheel }, 7876b8d6e55SJulian Scheel { 7886b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7896b8d6e55SJulian Scheel .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 7906b8d6e55SJulian Scheel SNDRV_CTL_ELEM_ACCESS_TLV_READ), 7916b8d6e55SJulian Scheel .name = "LFE Playback Volume", 7926b8d6e55SJulian Scheel .info = wm8766_vol_info, 7936b8d6e55SJulian Scheel .get = wm8766_vol_get, 7946b8d6e55SJulian Scheel .put = wm8766_vol_put, 7956b8d6e55SJulian Scheel .private_value = (1 << 8) | 5, 7966b8d6e55SJulian Scheel .tlv = { .p = db_scale_wm_dac } 7976b8d6e55SJulian Scheel }, 7986b8d6e55SJulian Scheel { 7996b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8006b8d6e55SJulian Scheel .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 8016b8d6e55SJulian Scheel SNDRV_CTL_ELEM_ACCESS_TLV_READ), 8026b8d6e55SJulian Scheel .name = "Side Playback Volume", 8036b8d6e55SJulian Scheel .info = wm8766_vol_info, 8046b8d6e55SJulian Scheel .get = wm8766_vol_get, 8056b8d6e55SJulian Scheel .put = wm8766_vol_put, 8066b8d6e55SJulian Scheel .private_value = (2 << 8) | 6, 8076b8d6e55SJulian Scheel .tlv = { .p = db_scale_wm_dac }, 8086b8d6e55SJulian Scheel }, 8096b8d6e55SJulian Scheel { 8106b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8116b8d6e55SJulian Scheel .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | 8126b8d6e55SJulian Scheel SNDRV_CTL_ELEM_ACCESS_TLV_READ), 8136b8d6e55SJulian Scheel .name = "Capture Volume", 8146b8d6e55SJulian Scheel .info = wm_adc_vol_info, 8156b8d6e55SJulian Scheel .get = wm_adc_vol_get, 8166b8d6e55SJulian Scheel .put = wm_adc_vol_put, 8176b8d6e55SJulian Scheel .tlv = { .p = db_scale_wm_dac }, 8186b8d6e55SJulian Scheel }, 8196b8d6e55SJulian Scheel { 8206b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8216b8d6e55SJulian Scheel .name = "CD Capture Switch", 8226b8d6e55SJulian Scheel .info = wm_adc_mux_info, 8236b8d6e55SJulian Scheel .get = wm_adc_mux_get, 8246b8d6e55SJulian Scheel .put = wm_adc_mux_put, 8256b8d6e55SJulian Scheel .private_value = 0, 8266b8d6e55SJulian Scheel }, 8276b8d6e55SJulian Scheel { 8286b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8296b8d6e55SJulian Scheel .name = "Line Capture Switch", 8306b8d6e55SJulian Scheel .info = wm_adc_mux_info, 8316b8d6e55SJulian Scheel .get = wm_adc_mux_get, 8326b8d6e55SJulian Scheel .put = wm_adc_mux_put, 8336b8d6e55SJulian Scheel .private_value = 1, 8346b8d6e55SJulian Scheel }, 8356b8d6e55SJulian Scheel { 8366b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8376b8d6e55SJulian Scheel .name = "Analog Bypass Switch", 8386b8d6e55SJulian Scheel .info = wm_bypass_info, 8396b8d6e55SJulian Scheel .get = wm_bypass_get, 8406b8d6e55SJulian Scheel .put = wm_bypass_put, 8416b8d6e55SJulian Scheel }, 8426b8d6e55SJulian Scheel { 8436b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8446b8d6e55SJulian Scheel .name = "Swap Output Channels", 8456b8d6e55SJulian Scheel .info = wm_chswap_info, 8466b8d6e55SJulian Scheel .get = wm_chswap_get, 8476b8d6e55SJulian Scheel .put = wm_chswap_put, 8486b8d6e55SJulian Scheel }, 8496b8d6e55SJulian Scheel { 8506b8d6e55SJulian Scheel .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8516b8d6e55SJulian Scheel .name = "Analog Capture Source", 8526b8d6e55SJulian Scheel .info = wm_adc_mux_enum_info, 8536b8d6e55SJulian Scheel .get = wm_adc_mux_enum_get, 8546b8d6e55SJulian Scheel .put = wm_adc_mux_enum_put, 8556b8d6e55SJulian Scheel }, 8566b8d6e55SJulian Scheel }; 8576b8d6e55SJulian Scheel 8586b8d6e55SJulian Scheel /* 8596b8d6e55SJulian Scheel * WM codec registers 8606b8d6e55SJulian Scheel */ 8617cda8ba9STakashi Iwai static void wm_proc_regs_write(struct snd_info_entry *entry, 8627cda8ba9STakashi Iwai struct snd_info_buffer *buffer) 8636b8d6e55SJulian Scheel { 8647cda8ba9STakashi Iwai struct snd_ice1712 *ice = entry->private_data; 8656b8d6e55SJulian Scheel char line[64]; 8666b8d6e55SJulian Scheel unsigned int reg, val; 8676b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 8686b8d6e55SJulian Scheel while (!snd_info_get_line(buffer, line, sizeof(line))) { 8696b8d6e55SJulian Scheel if (sscanf(line, "%x %x", ®, &val) != 2) 8706b8d6e55SJulian Scheel continue; 8716b8d6e55SJulian Scheel if (reg <= 0x17 && val <= 0xffff) 8726b8d6e55SJulian Scheel wm_put(ice, reg, val); 8736b8d6e55SJulian Scheel } 8746b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 8756b8d6e55SJulian Scheel } 8766b8d6e55SJulian Scheel 8777cda8ba9STakashi Iwai static void wm_proc_regs_read(struct snd_info_entry *entry, 8787cda8ba9STakashi Iwai struct snd_info_buffer *buffer) 8796b8d6e55SJulian Scheel { 8807cda8ba9STakashi Iwai struct snd_ice1712 *ice = entry->private_data; 8816b8d6e55SJulian Scheel int reg, val; 8826b8d6e55SJulian Scheel 8836b8d6e55SJulian Scheel mutex_lock(&ice->gpio_mutex); 8846b8d6e55SJulian Scheel for (reg = 0; reg <= 0x17; reg++) { 8856b8d6e55SJulian Scheel val = wm_get(ice, reg); 8866b8d6e55SJulian Scheel snd_iprintf(buffer, "%02x = %04x\n", reg, val); 8876b8d6e55SJulian Scheel } 8886b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex); 8896b8d6e55SJulian Scheel } 8906b8d6e55SJulian Scheel 8916b8d6e55SJulian Scheel static void wm_proc_init(struct snd_ice1712 *ice) 8926b8d6e55SJulian Scheel { 89347f2769bSTakashi Iwai snd_card_rw_proc_new(ice->card, "wm_codec", ice, wm_proc_regs_read, 89447f2769bSTakashi Iwai wm_proc_regs_write); 8956b8d6e55SJulian Scheel } 8966b8d6e55SJulian Scheel 897e23e7a14SBill Pemberton static int prodigy_hifi_add_controls(struct snd_ice1712 *ice) 8986b8d6e55SJulian Scheel { 8996b8d6e55SJulian Scheel unsigned int i; 9006b8d6e55SJulian Scheel int err; 9016b8d6e55SJulian Scheel 9026b8d6e55SJulian Scheel for (i = 0; i < ARRAY_SIZE(prodigy_hifi_controls); i++) { 9037cda8ba9STakashi Iwai err = snd_ctl_add(ice->card, 9047cda8ba9STakashi Iwai snd_ctl_new1(&prodigy_hifi_controls[i], ice)); 9056b8d6e55SJulian Scheel if (err < 0) 9066b8d6e55SJulian Scheel return err; 9076b8d6e55SJulian Scheel } 9086b8d6e55SJulian Scheel 9096b8d6e55SJulian Scheel wm_proc_init(ice); 9106b8d6e55SJulian Scheel 9116b8d6e55SJulian Scheel return 0; 9126b8d6e55SJulian Scheel } 9136b8d6e55SJulian Scheel 914e23e7a14SBill Pemberton static int prodigy_hd2_add_controls(struct snd_ice1712 *ice) 9156b8d6e55SJulian Scheel { 9166b8d6e55SJulian Scheel unsigned int i; 9176b8d6e55SJulian Scheel int err; 9186b8d6e55SJulian Scheel 9196b8d6e55SJulian Scheel for (i = 0; i < ARRAY_SIZE(prodigy_hd2_controls); i++) { 9207cda8ba9STakashi Iwai err = snd_ctl_add(ice->card, 9217cda8ba9STakashi Iwai snd_ctl_new1(&prodigy_hd2_controls[i], ice)); 9226b8d6e55SJulian Scheel if (err < 0) 9236b8d6e55SJulian Scheel return err; 9246b8d6e55SJulian Scheel } 9256b8d6e55SJulian Scheel 9266b8d6e55SJulian Scheel wm_proc_init(ice); 9276b8d6e55SJulian Scheel 9286b8d6e55SJulian Scheel return 0; 9296b8d6e55SJulian Scheel } 9306b8d6e55SJulian Scheel 9316dbc6cafSYussuf Khalil static void wm8766_init(struct snd_ice1712 *ice) 9326b8d6e55SJulian Scheel { 933*f16a4e96STakashi Iwai static const unsigned short wm8766_inits[] = { 9346dbc6cafSYussuf Khalil WM8766_RESET, 0x0000, 9356dbc6cafSYussuf Khalil WM8766_DAC_CTRL, 0x0120, 9366dbc6cafSYussuf Khalil WM8766_INT_CTRL, 0x0022, /* I2S Normal Mode, 24 bit */ 9376dbc6cafSYussuf Khalil WM8766_DAC_CTRL2, 0x0001, 9386dbc6cafSYussuf Khalil WM8766_DAC_CTRL3, 0x0080, 9396dbc6cafSYussuf Khalil WM8766_LDA1, 0x0100, 9406dbc6cafSYussuf Khalil WM8766_LDA2, 0x0100, 9416dbc6cafSYussuf Khalil WM8766_LDA3, 0x0100, 9426dbc6cafSYussuf Khalil WM8766_RDA1, 0x0100, 9436dbc6cafSYussuf Khalil WM8766_RDA2, 0x0100, 9446dbc6cafSYussuf Khalil WM8766_RDA3, 0x0100, 9456dbc6cafSYussuf Khalil WM8766_MUTE1, 0x0000, 9466dbc6cafSYussuf Khalil WM8766_MUTE2, 0x0000, 9476dbc6cafSYussuf Khalil }; 9486dbc6cafSYussuf Khalil unsigned int i; 9496dbc6cafSYussuf Khalil 9506dbc6cafSYussuf Khalil for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2) 9516dbc6cafSYussuf Khalil wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i + 1]); 9526dbc6cafSYussuf Khalil } 9536dbc6cafSYussuf Khalil 9546dbc6cafSYussuf Khalil static void wm8776_init(struct snd_ice1712 *ice) 9556dbc6cafSYussuf Khalil { 956*f16a4e96STakashi Iwai static const unsigned short wm8776_inits[] = { 9576b8d6e55SJulian Scheel /* These come first to reduce init pop noise */ 9586b8d6e55SJulian Scheel WM_ADC_MUX, 0x0003, /* ADC mute */ 9596b8d6e55SJulian Scheel /* 0x00c0 replaced by 0x0003 */ 9606b8d6e55SJulian Scheel 9616b8d6e55SJulian Scheel WM_DAC_MUTE, 0x0001, /* DAC softmute */ 9626b8d6e55SJulian Scheel WM_DAC_CTRL1, 0x0000, /* DAC mute */ 9636b8d6e55SJulian Scheel 9646b8d6e55SJulian Scheel WM_POWERDOWN, 0x0008, /* All power-up except HP */ 9656b8d6e55SJulian Scheel WM_RESET, 0x0000, /* reset */ 9666b8d6e55SJulian Scheel }; 9676dbc6cafSYussuf Khalil unsigned int i; 9686dbc6cafSYussuf Khalil 9696dbc6cafSYussuf Khalil for (i = 0; i < ARRAY_SIZE(wm8776_inits); i += 2) 9706dbc6cafSYussuf Khalil wm_put(ice, wm8776_inits[i], wm8776_inits[i + 1]); 9716dbc6cafSYussuf Khalil } 9726dbc6cafSYussuf Khalil 9736dbc6cafSYussuf Khalil #ifdef CONFIG_PM_SLEEP 9746dbc6cafSYussuf Khalil static int prodigy_hifi_resume(struct snd_ice1712 *ice) 9756dbc6cafSYussuf Khalil { 976*f16a4e96STakashi Iwai static const unsigned short wm8776_reinit_registers[] = { 9776dbc6cafSYussuf Khalil WM_MASTER_CTRL, 9786dbc6cafSYussuf Khalil WM_DAC_INT, 9796dbc6cafSYussuf Khalil WM_ADC_INT, 9806dbc6cafSYussuf Khalil WM_OUT_MUX, 9816dbc6cafSYussuf Khalil WM_HP_ATTEN_L, 9826dbc6cafSYussuf Khalil WM_HP_ATTEN_R, 9836dbc6cafSYussuf Khalil WM_PHASE_SWAP, 9846dbc6cafSYussuf Khalil WM_DAC_CTRL2, 9856dbc6cafSYussuf Khalil WM_ADC_ATTEN_L, 9866dbc6cafSYussuf Khalil WM_ADC_ATTEN_R, 9876dbc6cafSYussuf Khalil WM_ALC_CTRL1, 9886dbc6cafSYussuf Khalil WM_ALC_CTRL2, 9896dbc6cafSYussuf Khalil WM_ALC_CTRL3, 9906dbc6cafSYussuf Khalil WM_NOISE_GATE, 9916dbc6cafSYussuf Khalil WM_ADC_MUX, 9926dbc6cafSYussuf Khalil /* no DAC attenuation here */ 9936dbc6cafSYussuf Khalil }; 9946dbc6cafSYussuf Khalil struct prodigy_hifi_spec *spec = ice->spec; 9956dbc6cafSYussuf Khalil int i, ch; 9966dbc6cafSYussuf Khalil 9976dbc6cafSYussuf Khalil mutex_lock(&ice->gpio_mutex); 9986dbc6cafSYussuf Khalil 9996dbc6cafSYussuf Khalil /* reinitialize WM8776 and re-apply old register values */ 10006dbc6cafSYussuf Khalil wm8776_init(ice); 10016dbc6cafSYussuf Khalil schedule_timeout_uninterruptible(1); 10026dbc6cafSYussuf Khalil for (i = 0; i < ARRAY_SIZE(wm8776_reinit_registers); i++) 10036dbc6cafSYussuf Khalil wm_put(ice, wm8776_reinit_registers[i], 10046dbc6cafSYussuf Khalil wm_get(ice, wm8776_reinit_registers[i])); 10056dbc6cafSYussuf Khalil 10066dbc6cafSYussuf Khalil /* reinitialize WM8766 and re-apply volumes for all DACs */ 10076dbc6cafSYussuf Khalil wm8766_init(ice); 10086dbc6cafSYussuf Khalil for (ch = 0; ch < 2; ch++) { 10096dbc6cafSYussuf Khalil wm_set_vol(ice, WM_DAC_ATTEN_L + ch, 10106dbc6cafSYussuf Khalil spec->vol[2 + ch], spec->master[ch]); 10116dbc6cafSYussuf Khalil 10126dbc6cafSYussuf Khalil wm8766_set_vol(ice, WM8766_LDA1 + ch, 10136dbc6cafSYussuf Khalil spec->vol[0 + ch], spec->master[ch]); 10146dbc6cafSYussuf Khalil 10156dbc6cafSYussuf Khalil wm8766_set_vol(ice, WM8766_LDA2 + ch, 10166dbc6cafSYussuf Khalil spec->vol[4 + ch], spec->master[ch]); 10176dbc6cafSYussuf Khalil 10186dbc6cafSYussuf Khalil wm8766_set_vol(ice, WM8766_LDA3 + ch, 10196dbc6cafSYussuf Khalil spec->vol[6 + ch], spec->master[ch]); 10206dbc6cafSYussuf Khalil } 10216dbc6cafSYussuf Khalil 10226dbc6cafSYussuf Khalil /* unmute WM8776 DAC */ 10236dbc6cafSYussuf Khalil wm_put(ice, WM_DAC_MUTE, 0x00); 10246dbc6cafSYussuf Khalil wm_put(ice, WM_DAC_CTRL1, 0x90); 10256dbc6cafSYussuf Khalil 10266dbc6cafSYussuf Khalil mutex_unlock(&ice->gpio_mutex); 10276dbc6cafSYussuf Khalil return 0; 10286dbc6cafSYussuf Khalil } 10296dbc6cafSYussuf Khalil #endif 10306dbc6cafSYussuf Khalil 10316dbc6cafSYussuf Khalil /* 10326dbc6cafSYussuf Khalil * initialize the chip 10336dbc6cafSYussuf Khalil */ 10346dbc6cafSYussuf Khalil static int prodigy_hifi_init(struct snd_ice1712 *ice) 10356dbc6cafSYussuf Khalil { 1036*f16a4e96STakashi Iwai static const unsigned short wm8776_defaults[] = { 10376b8d6e55SJulian Scheel WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */ 10386b8d6e55SJulian Scheel WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */ 10396b8d6e55SJulian Scheel WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */ 10406b8d6e55SJulian Scheel WM_DAC_CTRL1, 0x0090, /* DAC L/R */ 10416b8d6e55SJulian Scheel WM_OUT_MUX, 0x0001, /* OUT DAC */ 10426b8d6e55SJulian Scheel WM_HP_ATTEN_L, 0x0179, /* HP 0dB */ 10436b8d6e55SJulian Scheel WM_HP_ATTEN_R, 0x0179, /* HP 0dB */ 10446b8d6e55SJulian Scheel WM_DAC_ATTEN_L, 0x0000, /* DAC 0dB */ 10456b8d6e55SJulian Scheel WM_DAC_ATTEN_L, 0x0100, /* DAC 0dB */ 10466b8d6e55SJulian Scheel WM_DAC_ATTEN_R, 0x0000, /* DAC 0dB */ 10476b8d6e55SJulian Scheel WM_DAC_ATTEN_R, 0x0100, /* DAC 0dB */ 10486b8d6e55SJulian Scheel WM_PHASE_SWAP, 0x0000, /* phase normal */ 10496b8d6e55SJulian Scheel #if 0 10506b8d6e55SJulian Scheel WM_DAC_MASTER, 0x0100, /* DAC master muted */ 10516b8d6e55SJulian Scheel #endif 10526b8d6e55SJulian Scheel WM_DAC_CTRL2, 0x0000, /* no deemphasis, no ZFLG */ 10536b8d6e55SJulian Scheel WM_ADC_ATTEN_L, 0x0000, /* ADC muted */ 10546b8d6e55SJulian Scheel WM_ADC_ATTEN_R, 0x0000, /* ADC muted */ 10556b8d6e55SJulian Scheel #if 1 10566b8d6e55SJulian Scheel WM_ALC_CTRL1, 0x007b, /* */ 10576b8d6e55SJulian Scheel WM_ALC_CTRL2, 0x0000, /* */ 10586b8d6e55SJulian Scheel WM_ALC_CTRL3, 0x0000, /* */ 10596b8d6e55SJulian Scheel WM_NOISE_GATE, 0x0000, /* */ 10606b8d6e55SJulian Scheel #endif 10616b8d6e55SJulian Scheel WM_DAC_MUTE, 0x0000, /* DAC unmute */ 10626b8d6e55SJulian Scheel WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */ 10636b8d6e55SJulian Scheel }; 10647cda8ba9STakashi Iwai struct prodigy_hifi_spec *spec; 10656b8d6e55SJulian Scheel unsigned int i; 10666b8d6e55SJulian Scheel 10676b8d6e55SJulian Scheel ice->vt1720 = 0; 10686b8d6e55SJulian Scheel ice->vt1724 = 1; 10696b8d6e55SJulian Scheel 10706b8d6e55SJulian Scheel ice->num_total_dacs = 8; 10716b8d6e55SJulian Scheel ice->num_total_adcs = 1; 10726b8d6e55SJulian Scheel 10736b8d6e55SJulian Scheel /* HACK - use this as the SPDIF source. 10746b8d6e55SJulian Scheel * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten 10756b8d6e55SJulian Scheel */ 10766b8d6e55SJulian Scheel ice->gpio.saved[0] = 0; 107725985edcSLucas De Marchi /* to remember the register values */ 10786b8d6e55SJulian Scheel 10796b8d6e55SJulian Scheel ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); 10806b8d6e55SJulian Scheel if (! ice->akm) 10816b8d6e55SJulian Scheel return -ENOMEM; 10826b8d6e55SJulian Scheel ice->akm_codecs = 1; 10836b8d6e55SJulian Scheel 10847cda8ba9STakashi Iwai spec = kzalloc(sizeof(*spec), GFP_KERNEL); 10857cda8ba9STakashi Iwai if (!spec) 10867cda8ba9STakashi Iwai return -ENOMEM; 10877cda8ba9STakashi Iwai ice->spec = spec; 10887cda8ba9STakashi Iwai 10896b8d6e55SJulian Scheel /* initialize WM8776 codec */ 10906dbc6cafSYussuf Khalil wm8776_init(ice); 10916b8d6e55SJulian Scheel schedule_timeout_uninterruptible(1); 10926dbc6cafSYussuf Khalil for (i = 0; i < ARRAY_SIZE(wm8776_defaults); i += 2) 10936dbc6cafSYussuf Khalil wm_put(ice, wm8776_defaults[i], wm8776_defaults[i + 1]); 10946b8d6e55SJulian Scheel 10956dbc6cafSYussuf Khalil wm8766_init(ice); 10966b8d6e55SJulian Scheel 10976dbc6cafSYussuf Khalil #ifdef CONFIG_PM_SLEEP 10986dbc6cafSYussuf Khalil ice->pm_resume = &prodigy_hifi_resume; 10996dbc6cafSYussuf Khalil ice->pm_suspend_enabled = 1; 11006dbc6cafSYussuf Khalil #endif 11016b8d6e55SJulian Scheel 11026b8d6e55SJulian Scheel return 0; 11036b8d6e55SJulian Scheel } 11046b8d6e55SJulian Scheel 11056b8d6e55SJulian Scheel 11066b8d6e55SJulian Scheel /* 11076b8d6e55SJulian Scheel * initialize the chip 11086b8d6e55SJulian Scheel */ 1109b40e9538SIgor Chernyshev static void ak4396_init(struct snd_ice1712 *ice) 11106b8d6e55SJulian Scheel { 1111*f16a4e96STakashi Iwai static const unsigned short ak4396_inits[] = { 11126b8d6e55SJulian Scheel AK4396_CTRL1, 0x87, /* I2S Normal Mode, 24 bit */ 11136b8d6e55SJulian Scheel AK4396_CTRL2, 0x02, 11146b8d6e55SJulian Scheel AK4396_CTRL3, 0x00, 11156b8d6e55SJulian Scheel AK4396_LCH_ATT, 0x00, 11166b8d6e55SJulian Scheel AK4396_RCH_ATT, 0x00, 11176b8d6e55SJulian Scheel }; 11186b8d6e55SJulian Scheel 11196b8d6e55SJulian Scheel unsigned int i; 11206b8d6e55SJulian Scheel 1121b40e9538SIgor Chernyshev /* initialize ak4396 codec */ 1122b40e9538SIgor Chernyshev /* reset codec */ 1123b40e9538SIgor Chernyshev ak4396_write(ice, AK4396_CTRL1, 0x86); 1124b40e9538SIgor Chernyshev msleep(100); 1125b40e9538SIgor Chernyshev ak4396_write(ice, AK4396_CTRL1, 0x87); 1126b40e9538SIgor Chernyshev 1127b40e9538SIgor Chernyshev for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2) 1128b40e9538SIgor Chernyshev ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]); 1129b40e9538SIgor Chernyshev } 1130b40e9538SIgor Chernyshev 1131c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP 11325e08fe57STakashi Iwai static int prodigy_hd2_resume(struct snd_ice1712 *ice) 1133b40e9538SIgor Chernyshev { 1134b40e9538SIgor Chernyshev /* initialize ak4396 codec and restore previous mixer volumes */ 1135b40e9538SIgor Chernyshev struct prodigy_hifi_spec *spec = ice->spec; 1136b40e9538SIgor Chernyshev int i; 1137b40e9538SIgor Chernyshev mutex_lock(&ice->gpio_mutex); 1138b40e9538SIgor Chernyshev ak4396_init(ice); 1139b40e9538SIgor Chernyshev for (i = 0; i < 2; i++) 1140b40e9538SIgor Chernyshev ak4396_write(ice, AK4396_LCH_ATT + i, spec->vol[i] & 0xff); 1141b40e9538SIgor Chernyshev mutex_unlock(&ice->gpio_mutex); 1142b40e9538SIgor Chernyshev return 0; 1143b40e9538SIgor Chernyshev } 1144b40e9538SIgor Chernyshev #endif 1145b40e9538SIgor Chernyshev 1146e23e7a14SBill Pemberton static int prodigy_hd2_init(struct snd_ice1712 *ice) 1147b40e9538SIgor Chernyshev { 1148b40e9538SIgor Chernyshev struct prodigy_hifi_spec *spec; 1149b40e9538SIgor Chernyshev 11506b8d6e55SJulian Scheel ice->vt1720 = 0; 11516b8d6e55SJulian Scheel ice->vt1724 = 1; 11526b8d6e55SJulian Scheel 11536b8d6e55SJulian Scheel ice->num_total_dacs = 1; 11546b8d6e55SJulian Scheel ice->num_total_adcs = 1; 11556b8d6e55SJulian Scheel 11566b8d6e55SJulian Scheel /* HACK - use this as the SPDIF source. 11576b8d6e55SJulian Scheel * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten 11586b8d6e55SJulian Scheel */ 11596b8d6e55SJulian Scheel ice->gpio.saved[0] = 0; 116025985edcSLucas De Marchi /* to remember the register values */ 11616b8d6e55SJulian Scheel 11626b8d6e55SJulian Scheel ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); 11636b8d6e55SJulian Scheel if (! ice->akm) 11646b8d6e55SJulian Scheel return -ENOMEM; 11656b8d6e55SJulian Scheel ice->akm_codecs = 1; 11666b8d6e55SJulian Scheel 11677cda8ba9STakashi Iwai spec = kzalloc(sizeof(*spec), GFP_KERNEL); 11687cda8ba9STakashi Iwai if (!spec) 11697cda8ba9STakashi Iwai return -ENOMEM; 11707cda8ba9STakashi Iwai ice->spec = spec; 11717cda8ba9STakashi Iwai 1172c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP 1173b40e9538SIgor Chernyshev ice->pm_resume = &prodigy_hd2_resume; 1174b40e9538SIgor Chernyshev ice->pm_suspend_enabled = 1; 1175b40e9538SIgor Chernyshev #endif 11766b8d6e55SJulian Scheel 1177b40e9538SIgor Chernyshev ak4396_init(ice); 11786b8d6e55SJulian Scheel 11796b8d6e55SJulian Scheel return 0; 11806b8d6e55SJulian Scheel } 11816b8d6e55SJulian Scheel 11826b8d6e55SJulian Scheel 1183*f16a4e96STakashi Iwai static const unsigned char prodigy71hifi_eeprom[] = { 11846b8d6e55SJulian Scheel 0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ 11856b8d6e55SJulian Scheel 0x80, /* ACLINK: I2S */ 11866b8d6e55SJulian Scheel 0xfc, /* I2S: vol, 96k, 24bit, 192k */ 11876b8d6e55SJulian Scheel 0xc3, /* SPDIF: out-en, out-int, spdif-in */ 11886b8d6e55SJulian Scheel 0xff, /* GPIO_DIR */ 11896b8d6e55SJulian Scheel 0xff, /* GPIO_DIR1 */ 11906b8d6e55SJulian Scheel 0x5f, /* GPIO_DIR2 */ 11916b8d6e55SJulian Scheel 0x00, /* GPIO_MASK */ 11926b8d6e55SJulian Scheel 0x00, /* GPIO_MASK1 */ 11936b8d6e55SJulian Scheel 0x00, /* GPIO_MASK2 */ 11946b8d6e55SJulian Scheel 0x00, /* GPIO_STATE */ 11956b8d6e55SJulian Scheel 0x00, /* GPIO_STATE1 */ 11966b8d6e55SJulian Scheel 0x00, /* GPIO_STATE2 */ 11976b8d6e55SJulian Scheel }; 11986b8d6e55SJulian Scheel 1199*f16a4e96STakashi Iwai static const unsigned char prodigyhd2_eeprom[] = { 12006b8d6e55SJulian Scheel 0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ 12016b8d6e55SJulian Scheel 0x80, /* ACLINK: I2S */ 12026b8d6e55SJulian Scheel 0xfc, /* I2S: vol, 96k, 24bit, 192k */ 12036b8d6e55SJulian Scheel 0xc3, /* SPDIF: out-en, out-int, spdif-in */ 12046b8d6e55SJulian Scheel 0xff, /* GPIO_DIR */ 12056b8d6e55SJulian Scheel 0xff, /* GPIO_DIR1 */ 12066b8d6e55SJulian Scheel 0x5f, /* GPIO_DIR2 */ 12076b8d6e55SJulian Scheel 0x00, /* GPIO_MASK */ 12086b8d6e55SJulian Scheel 0x00, /* GPIO_MASK1 */ 12096b8d6e55SJulian Scheel 0x00, /* GPIO_MASK2 */ 12106b8d6e55SJulian Scheel 0x00, /* GPIO_STATE */ 12116b8d6e55SJulian Scheel 0x00, /* GPIO_STATE1 */ 12126b8d6e55SJulian Scheel 0x00, /* GPIO_STATE2 */ 12136b8d6e55SJulian Scheel }; 12146b8d6e55SJulian Scheel 1215*f16a4e96STakashi Iwai static const unsigned char fortissimo4_eeprom[] = { 12166b8d6e55SJulian Scheel 0x43, /* SYSCONF: clock 512, ADC, 4DACs */ 12176b8d6e55SJulian Scheel 0x80, /* ACLINK: I2S */ 12186b8d6e55SJulian Scheel 0xfc, /* I2S: vol, 96k, 24bit, 192k */ 12196b8d6e55SJulian Scheel 0xc1, /* SPDIF: out-en, out-int */ 12206b8d6e55SJulian Scheel 0xff, /* GPIO_DIR */ 12216b8d6e55SJulian Scheel 0xff, /* GPIO_DIR1 */ 12226b8d6e55SJulian Scheel 0x5f, /* GPIO_DIR2 */ 12236b8d6e55SJulian Scheel 0x00, /* GPIO_MASK */ 12246b8d6e55SJulian Scheel 0x00, /* GPIO_MASK1 */ 12256b8d6e55SJulian Scheel 0x00, /* GPIO_MASK2 */ 12266b8d6e55SJulian Scheel 0x00, /* GPIO_STATE */ 12276b8d6e55SJulian Scheel 0x00, /* GPIO_STATE1 */ 12286b8d6e55SJulian Scheel 0x00, /* GPIO_STATE2 */ 12296b8d6e55SJulian Scheel }; 12306b8d6e55SJulian Scheel 12316b8d6e55SJulian Scheel /* entry point */ 1232e23e7a14SBill Pemberton struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] = { 12336b8d6e55SJulian Scheel { 12346b8d6e55SJulian Scheel .subvendor = VT1724_SUBDEVICE_PRODIGY_HIFI, 12356b8d6e55SJulian Scheel .name = "Audiotrak Prodigy 7.1 HiFi", 12366b8d6e55SJulian Scheel .model = "prodigy71hifi", 12376b8d6e55SJulian Scheel .chip_init = prodigy_hifi_init, 12386b8d6e55SJulian Scheel .build_controls = prodigy_hifi_add_controls, 12396b8d6e55SJulian Scheel .eeprom_size = sizeof(prodigy71hifi_eeprom), 12406b8d6e55SJulian Scheel .eeprom_data = prodigy71hifi_eeprom, 12416b8d6e55SJulian Scheel .driver = "Prodigy71HIFI", 12426b8d6e55SJulian Scheel }, 12436b8d6e55SJulian Scheel { 12446b8d6e55SJulian Scheel .subvendor = VT1724_SUBDEVICE_PRODIGY_HD2, 12456b8d6e55SJulian Scheel .name = "Audiotrak Prodigy HD2", 12466b8d6e55SJulian Scheel .model = "prodigyhd2", 12476b8d6e55SJulian Scheel .chip_init = prodigy_hd2_init, 12486b8d6e55SJulian Scheel .build_controls = prodigy_hd2_add_controls, 12496b8d6e55SJulian Scheel .eeprom_size = sizeof(prodigyhd2_eeprom), 12506b8d6e55SJulian Scheel .eeprom_data = prodigyhd2_eeprom, 12516b8d6e55SJulian Scheel .driver = "Prodigy71HD2", 12526b8d6e55SJulian Scheel }, 12536b8d6e55SJulian Scheel { 12546b8d6e55SJulian Scheel .subvendor = VT1724_SUBDEVICE_FORTISSIMO4, 12556b8d6e55SJulian Scheel .name = "Hercules Fortissimo IV", 12566b8d6e55SJulian Scheel .model = "fortissimo4", 12576b8d6e55SJulian Scheel .chip_init = prodigy_hifi_init, 12586b8d6e55SJulian Scheel .build_controls = prodigy_hifi_add_controls, 12596b8d6e55SJulian Scheel .eeprom_size = sizeof(fortissimo4_eeprom), 12606b8d6e55SJulian Scheel .eeprom_data = fortissimo4_eeprom, 12616b8d6e55SJulian Scheel .driver = "Fortissimo4", 12626b8d6e55SJulian Scheel }, 12636b8d6e55SJulian Scheel { } /* terminator */ 12646b8d6e55SJulian Scheel }; 12656b8d6e55SJulian Scheel 1266