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 */
wm_get(struct snd_ice1712 * ice,int reg)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 */
wm_put_nocache(struct snd_ice1712 * ice,int reg,unsigned short val)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
wm_put(struct snd_ice1712 * ice,int reg,unsigned short val)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
set_gpio_bit(struct snd_ice1712 * ice,unsigned int bit,int val)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
wm8766_spi_send_word(struct snd_ice1712 * ice,unsigned int data)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
wm8766_spi_write(struct snd_ice1712 * ice,unsigned int reg,unsigned int data)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
ak4396_send_word(struct snd_ice1712 * ice,unsigned int data)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
ak4396_write(struct snd_ice1712 * ice,unsigned int reg,unsigned int data)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
ak4396_dac_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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
ak4396_dac_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
ak4396_dac_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_set_vol(struct snd_ice1712 * ice,unsigned int index,unsigned short vol,unsigned short master)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
wm8766_set_vol(struct snd_ice1712 * ice,unsigned int index,unsigned short vol,unsigned short master)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
wm_dac_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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
wm_dac_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_dac_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
wm8766_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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
wm8766_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm8766_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
wm_master_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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
wm_master_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_master_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_adc_mux_enum_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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
wm_adc_mux_enum_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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);
539*c47914c0STakashi Iwai ucontrol->value.enumerated.item[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
5406b8d6e55SJulian Scheel mutex_unlock(&ice->gpio_mutex);
5416b8d6e55SJulian Scheel return 0;
5426b8d6e55SJulian Scheel }
5436b8d6e55SJulian Scheel
wm_adc_mux_enum_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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);
553*c47914c0STakashi Iwai nval = (oval & 0xe0) | ucontrol->value.enumerated.item[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
wm_adc_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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
wm_adc_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_adc_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_adc_mux_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_adc_mux_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_bypass_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_bypass_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_chswap_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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
wm_chswap_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
wm_proc_regs_write(struct snd_info_entry * entry,struct snd_info_buffer * buffer)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
wm_proc_regs_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)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
wm_proc_init(struct snd_ice1712 * ice)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
prodigy_hifi_add_controls(struct snd_ice1712 * ice)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
prodigy_hd2_add_controls(struct snd_ice1712 * ice)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
wm8766_init(struct snd_ice1712 * ice)9316dbc6cafSYussuf Khalil static void wm8766_init(struct snd_ice1712 *ice)
9326b8d6e55SJulian Scheel {
933f16a4e96STakashi 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
wm8776_init(struct snd_ice1712 * ice)9546dbc6cafSYussuf Khalil static void wm8776_init(struct snd_ice1712 *ice)
9556dbc6cafSYussuf Khalil {
956f16a4e96STakashi 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
prodigy_hifi_resume(struct snd_ice1712 * ice)9746dbc6cafSYussuf Khalil static int prodigy_hifi_resume(struct snd_ice1712 *ice)
9756dbc6cafSYussuf Khalil {
976f16a4e96STakashi 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 */
prodigy_hifi_init(struct snd_ice1712 * ice)10346dbc6cafSYussuf Khalil static int prodigy_hifi_init(struct snd_ice1712 *ice)
10356dbc6cafSYussuf Khalil {
1036f16a4e96STakashi 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 */
ak4396_init(struct snd_ice1712 * ice)1109b40e9538SIgor Chernyshev static void ak4396_init(struct snd_ice1712 *ice)
11106b8d6e55SJulian Scheel {
1111f16a4e96STakashi 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
prodigy_hd2_resume(struct snd_ice1712 * ice)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
prodigy_hd2_init(struct snd_ice1712 * ice)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
1183f16a4e96STakashi 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
1199f16a4e96STakashi 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
1215f16a4e96STakashi 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