1b67eb152SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d1db38c0SClemens Ladisch /*
3b532d6b8SClemens Ladisch * card driver for models with WM8776/WM8766 DACs (Xonar DS/HDAV1.3 Slim)
4d1db38c0SClemens Ladisch *
5d1db38c0SClemens Ladisch * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
6d1db38c0SClemens Ladisch */
7d1db38c0SClemens Ladisch
8d1db38c0SClemens Ladisch /*
9d1db38c0SClemens Ladisch * Xonar DS
10d1db38c0SClemens Ladisch * --------
11d1db38c0SClemens Ladisch *
12d1db38c0SClemens Ladisch * CMI8788:
13d1db38c0SClemens Ladisch *
14d1db38c0SClemens Ladisch * SPI 0 -> WM8766 (surround, center/LFE, back)
15d1db38c0SClemens Ladisch * SPI 1 -> WM8776 (front, input)
16d1db38c0SClemens Ladisch *
17435feac6SClemens Ladisch * GPIO 4 <- headphone detect, 0 = plugged
18435feac6SClemens Ladisch * GPIO 6 -> route input jack to mic-in (0) or line-in (1)
1984cf83a2SClemens Ladisch * GPIO 7 -> enable output to front L/R speaker channels
2084cf83a2SClemens Ladisch * GPIO 8 -> enable output to other speaker channels and front panel headphone
219bac84edSClemens Ladisch *
22de664936SClemens Ladisch * WM8776:
239bac84edSClemens Ladisch *
249bac84edSClemens Ladisch * input 1 <- line
259bac84edSClemens Ladisch * input 2 <- mic
269bac84edSClemens Ladisch * input 3 <- front mic
279bac84edSClemens Ladisch * input 4 <- aux
28d1db38c0SClemens Ladisch */
29d1db38c0SClemens Ladisch
30de664936SClemens Ladisch /*
31de664936SClemens Ladisch * Xonar HDAV1.3 Slim
32de664936SClemens Ladisch * ------------------
33de664936SClemens Ladisch *
34de664936SClemens Ladisch * CMI8788:
35de664936SClemens Ladisch *
36de664936SClemens Ladisch * I²C <-> WM8776 (addr 0011010)
37de664936SClemens Ladisch *
38de664936SClemens Ladisch * GPIO 0 -> disable HDMI output
39de664936SClemens Ladisch * GPIO 1 -> enable HP output
40de664936SClemens Ladisch * GPIO 6 -> firmware EEPROM I²C clock
41de664936SClemens Ladisch * GPIO 7 <-> firmware EEPROM I²C data
42de664936SClemens Ladisch *
43de664936SClemens Ladisch * UART <-> HDMI controller
44de664936SClemens Ladisch *
45de664936SClemens Ladisch * WM8776:
46de664936SClemens Ladisch *
47de664936SClemens Ladisch * input 1 <- mic
48de664936SClemens Ladisch * input 2 <- aux
49de664936SClemens Ladisch */
50de664936SClemens Ladisch
51d1db38c0SClemens Ladisch #include <linux/pci.h>
52d1db38c0SClemens Ladisch #include <linux/delay.h>
53d1db38c0SClemens Ladisch #include <sound/control.h>
54d1db38c0SClemens Ladisch #include <sound/core.h>
559719fcaaSClemens Ladisch #include <sound/info.h>
56435feac6SClemens Ladisch #include <sound/jack.h>
57d1db38c0SClemens Ladisch #include <sound/pcm.h>
58d1db38c0SClemens Ladisch #include <sound/pcm_params.h>
59d1db38c0SClemens Ladisch #include <sound/tlv.h>
60d1db38c0SClemens Ladisch #include "xonar.h"
61d1db38c0SClemens Ladisch #include "wm8776.h"
62d1db38c0SClemens Ladisch #include "wm8766.h"
63d1db38c0SClemens Ladisch
64d1db38c0SClemens Ladisch #define GPIO_DS_HP_DETECT 0x0010
65d1db38c0SClemens Ladisch #define GPIO_DS_INPUT_ROUTE 0x0040
6684cf83a2SClemens Ladisch #define GPIO_DS_OUTPUT_FRONTLR 0x0080
6784cf83a2SClemens Ladisch #define GPIO_DS_OUTPUT_ENABLE 0x0100
68d1db38c0SClemens Ladisch
69b532d6b8SClemens Ladisch #define GPIO_SLIM_HDMI_DISABLE 0x0001
70b532d6b8SClemens Ladisch #define GPIO_SLIM_OUTPUT_ENABLE 0x0002
71b532d6b8SClemens Ladisch #define GPIO_SLIM_FIRMWARE_CLK 0x0040
72b532d6b8SClemens Ladisch #define GPIO_SLIM_FIRMWARE_DATA 0x0080
73b532d6b8SClemens Ladisch
74b532d6b8SClemens Ladisch #define I2C_DEVICE_WM8776 0x34 /* 001101, 0, /W=0 */
75b532d6b8SClemens Ladisch
76d1db38c0SClemens Ladisch #define LC_CONTROL_LIMITER 0x40000000
77d1db38c0SClemens Ladisch #define LC_CONTROL_ALC 0x20000000
78d1db38c0SClemens Ladisch
79d1db38c0SClemens Ladisch struct xonar_wm87x6 {
80d1db38c0SClemens Ladisch struct xonar_generic generic;
81d1db38c0SClemens Ladisch u16 wm8776_regs[0x17];
82d1db38c0SClemens Ladisch u16 wm8766_regs[0x10];
83fe6ce80aSClemens Ladisch struct snd_kcontrol *line_adcmux_control;
84fe6ce80aSClemens Ladisch struct snd_kcontrol *mic_adcmux_control;
85d1db38c0SClemens Ladisch struct snd_kcontrol *lc_controls[13];
86435feac6SClemens Ladisch struct snd_jack *hp_jack;
87b532d6b8SClemens Ladisch struct xonar_hdmi hdmi;
88d1db38c0SClemens Ladisch };
89d1db38c0SClemens Ladisch
wm8776_write_spi(struct oxygen * chip,unsigned int reg,unsigned int value)90b532d6b8SClemens Ladisch static void wm8776_write_spi(struct oxygen *chip,
91d1db38c0SClemens Ladisch unsigned int reg, unsigned int value)
92d1db38c0SClemens Ladisch {
93d1db38c0SClemens Ladisch oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
94d1db38c0SClemens Ladisch OXYGEN_SPI_DATA_LENGTH_2 |
95d1db38c0SClemens Ladisch OXYGEN_SPI_CLOCK_160 |
96d1db38c0SClemens Ladisch (1 << OXYGEN_SPI_CODEC_SHIFT) |
97d1db38c0SClemens Ladisch OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
98d1db38c0SClemens Ladisch (reg << 9) | value);
99b532d6b8SClemens Ladisch }
100b532d6b8SClemens Ladisch
wm8776_write_i2c(struct oxygen * chip,unsigned int reg,unsigned int value)101b532d6b8SClemens Ladisch static void wm8776_write_i2c(struct oxygen *chip,
102b532d6b8SClemens Ladisch unsigned int reg, unsigned int value)
103b532d6b8SClemens Ladisch {
104b532d6b8SClemens Ladisch oxygen_write_i2c(chip, I2C_DEVICE_WM8776,
105b532d6b8SClemens Ladisch (reg << 1) | (value >> 8), value);
106b532d6b8SClemens Ladisch }
107b532d6b8SClemens Ladisch
wm8776_write(struct oxygen * chip,unsigned int reg,unsigned int value)108b532d6b8SClemens Ladisch static void wm8776_write(struct oxygen *chip,
109b532d6b8SClemens Ladisch unsigned int reg, unsigned int value)
110b532d6b8SClemens Ladisch {
111b532d6b8SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
112b532d6b8SClemens Ladisch
113b532d6b8SClemens Ladisch if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
114b532d6b8SClemens Ladisch OXYGEN_FUNCTION_SPI)
115b532d6b8SClemens Ladisch wm8776_write_spi(chip, reg, value);
116b532d6b8SClemens Ladisch else
117b532d6b8SClemens Ladisch wm8776_write_i2c(chip, reg, value);
118d1db38c0SClemens Ladisch if (reg < ARRAY_SIZE(data->wm8776_regs)) {
119*6add6b02SPierre-Louis Bossart /* reg >= WM8776_HPLVOL is always true */
120*6add6b02SPierre-Louis Bossart if (reg <= WM8776_DACMASTER)
121d1db38c0SClemens Ladisch value &= ~WM8776_UPDATE;
122d1db38c0SClemens Ladisch data->wm8776_regs[reg] = value;
123d1db38c0SClemens Ladisch }
124d1db38c0SClemens Ladisch }
125d1db38c0SClemens Ladisch
wm8776_write_cached(struct oxygen * chip,unsigned int reg,unsigned int value)126d1db38c0SClemens Ladisch static void wm8776_write_cached(struct oxygen *chip,
127d1db38c0SClemens Ladisch unsigned int reg, unsigned int value)
128d1db38c0SClemens Ladisch {
129d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
130d1db38c0SClemens Ladisch
131d1db38c0SClemens Ladisch if (reg >= ARRAY_SIZE(data->wm8776_regs) ||
132d1db38c0SClemens Ladisch value != data->wm8776_regs[reg])
133d1db38c0SClemens Ladisch wm8776_write(chip, reg, value);
134d1db38c0SClemens Ladisch }
135d1db38c0SClemens Ladisch
wm8766_write(struct oxygen * chip,unsigned int reg,unsigned int value)136d1db38c0SClemens Ladisch static void wm8766_write(struct oxygen *chip,
137d1db38c0SClemens Ladisch unsigned int reg, unsigned int value)
138d1db38c0SClemens Ladisch {
139d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
140d1db38c0SClemens Ladisch
141d1db38c0SClemens Ladisch oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
142d1db38c0SClemens Ladisch OXYGEN_SPI_DATA_LENGTH_2 |
143d1db38c0SClemens Ladisch OXYGEN_SPI_CLOCK_160 |
144d1db38c0SClemens Ladisch (0 << OXYGEN_SPI_CODEC_SHIFT) |
145d1db38c0SClemens Ladisch OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
146d1db38c0SClemens Ladisch (reg << 9) | value);
147da0dab5eSClemens Ladisch if (reg < ARRAY_SIZE(data->wm8766_regs)) {
148*6add6b02SPierre-Louis Bossart /* reg >= WM8766_LDA1 is always true */
149*6add6b02SPierre-Louis Bossart if (reg <= WM8766_RDA1 ||
150da0dab5eSClemens Ladisch (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
151da0dab5eSClemens Ladisch value &= ~WM8766_UPDATE;
152d1db38c0SClemens Ladisch data->wm8766_regs[reg] = value;
153d1db38c0SClemens Ladisch }
154da0dab5eSClemens Ladisch }
155d1db38c0SClemens Ladisch
wm8766_write_cached(struct oxygen * chip,unsigned int reg,unsigned int value)156d1db38c0SClemens Ladisch static void wm8766_write_cached(struct oxygen *chip,
157d1db38c0SClemens Ladisch unsigned int reg, unsigned int value)
158d1db38c0SClemens Ladisch {
159d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
160d1db38c0SClemens Ladisch
161d1db38c0SClemens Ladisch if (reg >= ARRAY_SIZE(data->wm8766_regs) ||
162da0dab5eSClemens Ladisch value != data->wm8766_regs[reg])
163d1db38c0SClemens Ladisch wm8766_write(chip, reg, value);
164d1db38c0SClemens Ladisch }
165d1db38c0SClemens Ladisch
wm8776_registers_init(struct oxygen * chip)166d1db38c0SClemens Ladisch static void wm8776_registers_init(struct oxygen *chip)
167d1db38c0SClemens Ladisch {
168d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
169d1db38c0SClemens Ladisch
170d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_RESET, 0);
171f0e48b6bSClemens Ladisch wm8776_write(chip, WM8776_PHASESWAP, WM8776_PH_MASK);
172d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_DACCTRL1, WM8776_DZCEN |
173d1db38c0SClemens Ladisch WM8776_PL_LEFT_LEFT | WM8776_PL_RIGHT_RIGHT);
174d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_DACMUTE, chip->dac_mute ? WM8776_DMUTE : 0);
175d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_DACIFCTRL,
176d1db38c0SClemens Ladisch WM8776_DACFMT_LJUST | WM8776_DACWL_24);
177d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_ADCIFCTRL,
178d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_ADCIFCTRL]);
179d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_MSTRCTRL, data->wm8776_regs[WM8776_MSTRCTRL]);
180d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_PWRDOWN, data->wm8776_regs[WM8776_PWRDOWN]);
181d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_HPLVOL, data->wm8776_regs[WM8776_HPLVOL]);
182d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_HPRVOL, data->wm8776_regs[WM8776_HPRVOL] |
183d1db38c0SClemens Ladisch WM8776_UPDATE);
184d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_ADCLVOL, data->wm8776_regs[WM8776_ADCLVOL]);
185d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_ADCRVOL, data->wm8776_regs[WM8776_ADCRVOL]);
186d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_ADCMUX, data->wm8776_regs[WM8776_ADCMUX]);
187d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_DACLVOL, chip->dac_volume[0]);
188d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_DACRVOL, chip->dac_volume[1] | WM8776_UPDATE);
189d1db38c0SClemens Ladisch }
190d1db38c0SClemens Ladisch
wm8766_registers_init(struct oxygen * chip)191d1db38c0SClemens Ladisch static void wm8766_registers_init(struct oxygen *chip)
192d1db38c0SClemens Ladisch {
19384cf83a2SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
19484cf83a2SClemens Ladisch
195d1db38c0SClemens Ladisch wm8766_write(chip, WM8766_RESET, 0);
19684cf83a2SClemens Ladisch wm8766_write(chip, WM8766_DAC_CTRL, data->wm8766_regs[WM8766_DAC_CTRL]);
197d1db38c0SClemens Ladisch wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24);
198d1db38c0SClemens Ladisch wm8766_write(chip, WM8766_DAC_CTRL2,
199d1db38c0SClemens Ladisch WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0));
200d1db38c0SClemens Ladisch wm8766_write(chip, WM8766_LDA1, chip->dac_volume[2]);
201d1db38c0SClemens Ladisch wm8766_write(chip, WM8766_RDA1, chip->dac_volume[3]);
202d1db38c0SClemens Ladisch wm8766_write(chip, WM8766_LDA2, chip->dac_volume[4]);
203d1db38c0SClemens Ladisch wm8766_write(chip, WM8766_RDA2, chip->dac_volume[5]);
204d1db38c0SClemens Ladisch wm8766_write(chip, WM8766_LDA3, chip->dac_volume[6]);
205d1db38c0SClemens Ladisch wm8766_write(chip, WM8766_RDA3, chip->dac_volume[7] | WM8766_UPDATE);
206d1db38c0SClemens Ladisch }
207d1db38c0SClemens Ladisch
wm8776_init(struct oxygen * chip)208d1db38c0SClemens Ladisch static void wm8776_init(struct oxygen *chip)
209d1db38c0SClemens Ladisch {
210d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
211d1db38c0SClemens Ladisch
212d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_HPLVOL] = (0x79 - 60) | WM8776_HPZCEN;
213d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_HPRVOL] = (0x79 - 60) | WM8776_HPZCEN;
214d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_ADCIFCTRL] =
215d1db38c0SClemens Ladisch WM8776_ADCFMT_LJUST | WM8776_ADCWL_24 | WM8776_ADCMCLK;
216d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_MSTRCTRL] =
217d1db38c0SClemens Ladisch WM8776_ADCRATE_256 | WM8776_DACRATE_256;
218d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_PWRDOWN] = WM8776_HPPD;
219d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_ADCLVOL] = 0xa5 | WM8776_ZCA;
220d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_ADCRVOL] = 0xa5 | WM8776_ZCA;
221d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_ADCMUX] = 0x001;
222d1db38c0SClemens Ladisch wm8776_registers_init(chip);
223d1db38c0SClemens Ladisch }
224d1db38c0SClemens Ladisch
wm8766_init(struct oxygen * chip)22584cf83a2SClemens Ladisch static void wm8766_init(struct oxygen *chip)
226435feac6SClemens Ladisch {
227435feac6SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
228435feac6SClemens Ladisch
22984cf83a2SClemens Ladisch data->wm8766_regs[WM8766_DAC_CTRL] =
23084cf83a2SClemens Ladisch WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT;
23184cf83a2SClemens Ladisch wm8766_registers_init(chip);
23284cf83a2SClemens Ladisch }
23384cf83a2SClemens Ladisch
xonar_ds_handle_hp_jack(struct oxygen * chip)23484cf83a2SClemens Ladisch static void xonar_ds_handle_hp_jack(struct oxygen *chip)
23584cf83a2SClemens Ladisch {
23684cf83a2SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
23784cf83a2SClemens Ladisch bool hp_plugged;
23884cf83a2SClemens Ladisch unsigned int reg;
23984cf83a2SClemens Ladisch
24084cf83a2SClemens Ladisch mutex_lock(&chip->mutex);
24184cf83a2SClemens Ladisch
24284cf83a2SClemens Ladisch hp_plugged = !(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
24384cf83a2SClemens Ladisch GPIO_DS_HP_DETECT);
24484cf83a2SClemens Ladisch
24584cf83a2SClemens Ladisch oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
24684cf83a2SClemens Ladisch hp_plugged ? 0 : GPIO_DS_OUTPUT_FRONTLR,
24784cf83a2SClemens Ladisch GPIO_DS_OUTPUT_FRONTLR);
24884cf83a2SClemens Ladisch
24984cf83a2SClemens Ladisch reg = data->wm8766_regs[WM8766_DAC_CTRL] & ~WM8766_MUTEALL;
25084cf83a2SClemens Ladisch if (hp_plugged)
25184cf83a2SClemens Ladisch reg |= WM8766_MUTEALL;
25284cf83a2SClemens Ladisch wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
25384cf83a2SClemens Ladisch
25484cf83a2SClemens Ladisch snd_jack_report(data->hp_jack, hp_plugged ? SND_JACK_HEADPHONE : 0);
25584cf83a2SClemens Ladisch
25684cf83a2SClemens Ladisch mutex_unlock(&chip->mutex);
257435feac6SClemens Ladisch }
258435feac6SClemens Ladisch
xonar_ds_init(struct oxygen * chip)259d1db38c0SClemens Ladisch static void xonar_ds_init(struct oxygen *chip)
260d1db38c0SClemens Ladisch {
261d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
262d1db38c0SClemens Ladisch
263d1db38c0SClemens Ladisch data->generic.anti_pop_delay = 300;
264d1db38c0SClemens Ladisch data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE;
265d1db38c0SClemens Ladisch
266d1db38c0SClemens Ladisch wm8776_init(chip);
26784cf83a2SClemens Ladisch wm8766_init(chip);
268d1db38c0SClemens Ladisch
26984cf83a2SClemens Ladisch oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
27084cf83a2SClemens Ladisch GPIO_DS_INPUT_ROUTE | GPIO_DS_OUTPUT_FRONTLR);
27184cf83a2SClemens Ladisch oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
27284cf83a2SClemens Ladisch GPIO_DS_HP_DETECT);
273d1db38c0SClemens Ladisch oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE);
274d1db38c0SClemens Ladisch oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT);
275d1db38c0SClemens Ladisch chip->interrupt_mask |= OXYGEN_INT_GPIO;
276d1db38c0SClemens Ladisch
277d1db38c0SClemens Ladisch xonar_enable_output(chip);
278d1db38c0SClemens Ladisch
279435feac6SClemens Ladisch snd_jack_new(chip->card, "Headphone",
2804e3f0dc6SJie Yang SND_JACK_HEADPHONE, &data->hp_jack, false, false);
28184cf83a2SClemens Ladisch xonar_ds_handle_hp_jack(chip);
282435feac6SClemens Ladisch
283d1db38c0SClemens Ladisch snd_component_add(chip->card, "WM8776");
284d1db38c0SClemens Ladisch snd_component_add(chip->card, "WM8766");
285d1db38c0SClemens Ladisch }
286d1db38c0SClemens Ladisch
xonar_hdav_slim_init(struct oxygen * chip)287b532d6b8SClemens Ladisch static void xonar_hdav_slim_init(struct oxygen *chip)
288b532d6b8SClemens Ladisch {
289b532d6b8SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
290b532d6b8SClemens Ladisch
291b532d6b8SClemens Ladisch data->generic.anti_pop_delay = 300;
292b532d6b8SClemens Ladisch data->generic.output_enable_bit = GPIO_SLIM_OUTPUT_ENABLE;
293b532d6b8SClemens Ladisch
294b532d6b8SClemens Ladisch wm8776_init(chip);
295b532d6b8SClemens Ladisch
296b532d6b8SClemens Ladisch oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
297b532d6b8SClemens Ladisch GPIO_SLIM_HDMI_DISABLE |
298b532d6b8SClemens Ladisch GPIO_SLIM_FIRMWARE_CLK |
299b532d6b8SClemens Ladisch GPIO_SLIM_FIRMWARE_DATA);
300b532d6b8SClemens Ladisch
301b532d6b8SClemens Ladisch xonar_hdmi_init(chip, &data->hdmi);
302b532d6b8SClemens Ladisch xonar_enable_output(chip);
303b532d6b8SClemens Ladisch
304b532d6b8SClemens Ladisch snd_component_add(chip->card, "WM8776");
305b532d6b8SClemens Ladisch }
306b532d6b8SClemens Ladisch
xonar_ds_cleanup(struct oxygen * chip)307d1db38c0SClemens Ladisch static void xonar_ds_cleanup(struct oxygen *chip)
308d1db38c0SClemens Ladisch {
309d1db38c0SClemens Ladisch xonar_disable_output(chip);
3104c25b932SClemens Ladisch wm8776_write(chip, WM8776_RESET, 0);
311d1db38c0SClemens Ladisch }
312d1db38c0SClemens Ladisch
xonar_hdav_slim_cleanup(struct oxygen * chip)313b532d6b8SClemens Ladisch static void xonar_hdav_slim_cleanup(struct oxygen *chip)
314b532d6b8SClemens Ladisch {
315b532d6b8SClemens Ladisch xonar_hdmi_cleanup(chip);
316b532d6b8SClemens Ladisch xonar_disable_output(chip);
317b532d6b8SClemens Ladisch wm8776_write(chip, WM8776_RESET, 0);
318b532d6b8SClemens Ladisch msleep(2);
319b532d6b8SClemens Ladisch }
320b532d6b8SClemens Ladisch
xonar_ds_suspend(struct oxygen * chip)321d1db38c0SClemens Ladisch static void xonar_ds_suspend(struct oxygen *chip)
322d1db38c0SClemens Ladisch {
323d1db38c0SClemens Ladisch xonar_ds_cleanup(chip);
324d1db38c0SClemens Ladisch }
325d1db38c0SClemens Ladisch
xonar_hdav_slim_suspend(struct oxygen * chip)326b532d6b8SClemens Ladisch static void xonar_hdav_slim_suspend(struct oxygen *chip)
327b532d6b8SClemens Ladisch {
328b532d6b8SClemens Ladisch xonar_hdav_slim_cleanup(chip);
329b532d6b8SClemens Ladisch }
330b532d6b8SClemens Ladisch
xonar_ds_resume(struct oxygen * chip)331d1db38c0SClemens Ladisch static void xonar_ds_resume(struct oxygen *chip)
332d1db38c0SClemens Ladisch {
333d1db38c0SClemens Ladisch wm8776_registers_init(chip);
334d1db38c0SClemens Ladisch wm8766_registers_init(chip);
335d1db38c0SClemens Ladisch xonar_enable_output(chip);
33684cf83a2SClemens Ladisch xonar_ds_handle_hp_jack(chip);
337d1db38c0SClemens Ladisch }
338d1db38c0SClemens Ladisch
xonar_hdav_slim_resume(struct oxygen * chip)339b532d6b8SClemens Ladisch static void xonar_hdav_slim_resume(struct oxygen *chip)
340b532d6b8SClemens Ladisch {
341b532d6b8SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
342b532d6b8SClemens Ladisch
343b532d6b8SClemens Ladisch wm8776_registers_init(chip);
344b532d6b8SClemens Ladisch xonar_hdmi_resume(chip, &data->hdmi);
345b532d6b8SClemens Ladisch xonar_enable_output(chip);
346b532d6b8SClemens Ladisch }
347b532d6b8SClemens Ladisch
wm8776_adc_hardware_filter(unsigned int channel,struct snd_pcm_hardware * hardware)348d1db38c0SClemens Ladisch static void wm8776_adc_hardware_filter(unsigned int channel,
349d1db38c0SClemens Ladisch struct snd_pcm_hardware *hardware)
350d1db38c0SClemens Ladisch {
351d1db38c0SClemens Ladisch if (channel == PCM_A) {
352d1db38c0SClemens Ladisch hardware->rates = SNDRV_PCM_RATE_32000 |
353d1db38c0SClemens Ladisch SNDRV_PCM_RATE_44100 |
354d1db38c0SClemens Ladisch SNDRV_PCM_RATE_48000 |
355d1db38c0SClemens Ladisch SNDRV_PCM_RATE_64000 |
356d1db38c0SClemens Ladisch SNDRV_PCM_RATE_88200 |
357d1db38c0SClemens Ladisch SNDRV_PCM_RATE_96000;
358d1db38c0SClemens Ladisch hardware->rate_max = 96000;
359d1db38c0SClemens Ladisch }
360d1db38c0SClemens Ladisch }
361d1db38c0SClemens Ladisch
xonar_hdav_slim_hardware_filter(unsigned int channel,struct snd_pcm_hardware * hardware)362b532d6b8SClemens Ladisch static void xonar_hdav_slim_hardware_filter(unsigned int channel,
363b532d6b8SClemens Ladisch struct snd_pcm_hardware *hardware)
364b532d6b8SClemens Ladisch {
365b532d6b8SClemens Ladisch wm8776_adc_hardware_filter(channel, hardware);
366b532d6b8SClemens Ladisch xonar_hdmi_pcm_hardware_filter(channel, hardware);
367b532d6b8SClemens Ladisch }
368b532d6b8SClemens Ladisch
set_wm87x6_dac_params(struct oxygen * chip,struct snd_pcm_hw_params * params)369d1db38c0SClemens Ladisch static void set_wm87x6_dac_params(struct oxygen *chip,
370d1db38c0SClemens Ladisch struct snd_pcm_hw_params *params)
371d1db38c0SClemens Ladisch {
372d1db38c0SClemens Ladisch }
373d1db38c0SClemens Ladisch
set_wm8776_adc_params(struct oxygen * chip,struct snd_pcm_hw_params * params)374d1db38c0SClemens Ladisch static void set_wm8776_adc_params(struct oxygen *chip,
375d1db38c0SClemens Ladisch struct snd_pcm_hw_params *params)
376d1db38c0SClemens Ladisch {
377d1db38c0SClemens Ladisch u16 reg;
378d1db38c0SClemens Ladisch
379d1db38c0SClemens Ladisch reg = WM8776_ADCRATE_256 | WM8776_DACRATE_256;
380d1db38c0SClemens Ladisch if (params_rate(params) > 48000)
381d1db38c0SClemens Ladisch reg |= WM8776_ADCOSR;
382d1db38c0SClemens Ladisch wm8776_write_cached(chip, WM8776_MSTRCTRL, reg);
383d1db38c0SClemens Ladisch }
384d1db38c0SClemens Ladisch
set_hdav_slim_dac_params(struct oxygen * chip,struct snd_pcm_hw_params * params)385b532d6b8SClemens Ladisch static void set_hdav_slim_dac_params(struct oxygen *chip,
386b532d6b8SClemens Ladisch struct snd_pcm_hw_params *params)
387b532d6b8SClemens Ladisch {
388b532d6b8SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
389b532d6b8SClemens Ladisch
390b532d6b8SClemens Ladisch xonar_set_hdmi_params(chip, &data->hdmi, params);
391b532d6b8SClemens Ladisch }
392b532d6b8SClemens Ladisch
update_wm8776_volume(struct oxygen * chip)393d1db38c0SClemens Ladisch static void update_wm8776_volume(struct oxygen *chip)
394d1db38c0SClemens Ladisch {
395d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
396d1db38c0SClemens Ladisch u8 to_change;
397d1db38c0SClemens Ladisch
398d1db38c0SClemens Ladisch if (chip->dac_volume[0] == chip->dac_volume[1]) {
399d1db38c0SClemens Ladisch if (chip->dac_volume[0] != data->wm8776_regs[WM8776_DACLVOL] ||
400d1db38c0SClemens Ladisch chip->dac_volume[1] != data->wm8776_regs[WM8776_DACRVOL]) {
401d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_DACMASTER,
402d1db38c0SClemens Ladisch chip->dac_volume[0] | WM8776_UPDATE);
403d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_DACLVOL] = chip->dac_volume[0];
404d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_DACRVOL] = chip->dac_volume[0];
405d1db38c0SClemens Ladisch }
406d1db38c0SClemens Ladisch } else {
407d1db38c0SClemens Ladisch to_change = (chip->dac_volume[0] !=
408d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_DACLVOL]) << 0;
409d1db38c0SClemens Ladisch to_change |= (chip->dac_volume[1] !=
410d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_DACLVOL]) << 1;
411d1db38c0SClemens Ladisch if (to_change & 1)
412d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_DACLVOL, chip->dac_volume[0] |
413d1db38c0SClemens Ladisch ((to_change & 2) ? 0 : WM8776_UPDATE));
414d1db38c0SClemens Ladisch if (to_change & 2)
415d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_DACRVOL,
416d1db38c0SClemens Ladisch chip->dac_volume[1] | WM8776_UPDATE);
417d1db38c0SClemens Ladisch }
418d1db38c0SClemens Ladisch }
419d1db38c0SClemens Ladisch
update_wm87x6_volume(struct oxygen * chip)420d1db38c0SClemens Ladisch static void update_wm87x6_volume(struct oxygen *chip)
421d1db38c0SClemens Ladisch {
422d1db38c0SClemens Ladisch static const u8 wm8766_regs[6] = {
423d1db38c0SClemens Ladisch WM8766_LDA1, WM8766_RDA1,
424d1db38c0SClemens Ladisch WM8766_LDA2, WM8766_RDA2,
425d1db38c0SClemens Ladisch WM8766_LDA3, WM8766_RDA3,
426d1db38c0SClemens Ladisch };
427d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
428d1db38c0SClemens Ladisch unsigned int i;
429d1db38c0SClemens Ladisch u8 to_change;
430d1db38c0SClemens Ladisch
431d1db38c0SClemens Ladisch update_wm8776_volume(chip);
432d1db38c0SClemens Ladisch if (chip->dac_volume[2] == chip->dac_volume[3] &&
433d1db38c0SClemens Ladisch chip->dac_volume[2] == chip->dac_volume[4] &&
434d1db38c0SClemens Ladisch chip->dac_volume[2] == chip->dac_volume[5] &&
435d1db38c0SClemens Ladisch chip->dac_volume[2] == chip->dac_volume[6] &&
436d1db38c0SClemens Ladisch chip->dac_volume[2] == chip->dac_volume[7]) {
437d1db38c0SClemens Ladisch to_change = 0;
438d1db38c0SClemens Ladisch for (i = 0; i < 6; ++i)
439d1db38c0SClemens Ladisch if (chip->dac_volume[2] !=
440d1db38c0SClemens Ladisch data->wm8766_regs[wm8766_regs[i]])
441d1db38c0SClemens Ladisch to_change = 1;
442d1db38c0SClemens Ladisch if (to_change) {
443d1db38c0SClemens Ladisch wm8766_write(chip, WM8766_MASTDA,
444d1db38c0SClemens Ladisch chip->dac_volume[2] | WM8766_UPDATE);
445d1db38c0SClemens Ladisch for (i = 0; i < 6; ++i)
446d1db38c0SClemens Ladisch data->wm8766_regs[wm8766_regs[i]] =
447d1db38c0SClemens Ladisch chip->dac_volume[2];
448d1db38c0SClemens Ladisch }
449d1db38c0SClemens Ladisch } else {
450d1db38c0SClemens Ladisch to_change = 0;
451d1db38c0SClemens Ladisch for (i = 0; i < 6; ++i)
452d1db38c0SClemens Ladisch to_change |= (chip->dac_volume[2 + i] !=
453d1db38c0SClemens Ladisch data->wm8766_regs[wm8766_regs[i]]) << i;
454d1db38c0SClemens Ladisch for (i = 0; i < 6; ++i)
455d1db38c0SClemens Ladisch if (to_change & (1 << i))
456d1db38c0SClemens Ladisch wm8766_write(chip, wm8766_regs[i],
457d1db38c0SClemens Ladisch chip->dac_volume[2 + i] |
458d1db38c0SClemens Ladisch ((to_change & (0x3e << i))
459d1db38c0SClemens Ladisch ? 0 : WM8766_UPDATE));
460d1db38c0SClemens Ladisch }
461d1db38c0SClemens Ladisch }
462d1db38c0SClemens Ladisch
update_wm8776_mute(struct oxygen * chip)463d1db38c0SClemens Ladisch static void update_wm8776_mute(struct oxygen *chip)
464d1db38c0SClemens Ladisch {
465d1db38c0SClemens Ladisch wm8776_write_cached(chip, WM8776_DACMUTE,
466d1db38c0SClemens Ladisch chip->dac_mute ? WM8776_DMUTE : 0);
467d1db38c0SClemens Ladisch }
468d1db38c0SClemens Ladisch
update_wm87x6_mute(struct oxygen * chip)469d1db38c0SClemens Ladisch static void update_wm87x6_mute(struct oxygen *chip)
470d1db38c0SClemens Ladisch {
471d1db38c0SClemens Ladisch update_wm8776_mute(chip);
472d1db38c0SClemens Ladisch wm8766_write_cached(chip, WM8766_DAC_CTRL2, WM8766_ZCD |
473d1db38c0SClemens Ladisch (chip->dac_mute ? WM8766_DMUTE_MASK : 0));
474d1db38c0SClemens Ladisch }
475d1db38c0SClemens Ladisch
update_wm8766_center_lfe_mix(struct oxygen * chip,bool mixed)4762dbf0ea2SClemens Ladisch static void update_wm8766_center_lfe_mix(struct oxygen *chip, bool mixed)
4772dbf0ea2SClemens Ladisch {
4782dbf0ea2SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
4792dbf0ea2SClemens Ladisch unsigned int reg;
4802dbf0ea2SClemens Ladisch
4812dbf0ea2SClemens Ladisch /*
4822dbf0ea2SClemens Ladisch * The WM8766 can mix left and right channels, but this setting
4832dbf0ea2SClemens Ladisch * applies to all three stereo pairs.
4842dbf0ea2SClemens Ladisch */
4852dbf0ea2SClemens Ladisch reg = data->wm8766_regs[WM8766_DAC_CTRL] &
4862dbf0ea2SClemens Ladisch ~(WM8766_PL_LEFT_MASK | WM8766_PL_RIGHT_MASK);
4872dbf0ea2SClemens Ladisch if (mixed)
4882dbf0ea2SClemens Ladisch reg |= WM8766_PL_LEFT_LRMIX | WM8766_PL_RIGHT_LRMIX;
4892dbf0ea2SClemens Ladisch else
4902dbf0ea2SClemens Ladisch reg |= WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT;
4912dbf0ea2SClemens Ladisch wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
4922dbf0ea2SClemens Ladisch }
4932dbf0ea2SClemens Ladisch
xonar_ds_gpio_changed(struct oxygen * chip)494d1db38c0SClemens Ladisch static void xonar_ds_gpio_changed(struct oxygen *chip)
495d1db38c0SClemens Ladisch {
49684cf83a2SClemens Ladisch xonar_ds_handle_hp_jack(chip);
497d1db38c0SClemens Ladisch }
498d1db38c0SClemens Ladisch
wm8776_bit_switch_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)499d1db38c0SClemens Ladisch static int wm8776_bit_switch_get(struct snd_kcontrol *ctl,
500d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
501d1db38c0SClemens Ladisch {
502d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
503d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
504d1db38c0SClemens Ladisch u16 bit = ctl->private_value & 0xffff;
505d1db38c0SClemens Ladisch unsigned int reg_index = (ctl->private_value >> 16) & 0xff;
506d1db38c0SClemens Ladisch bool invert = (ctl->private_value >> 24) & 1;
507d1db38c0SClemens Ladisch
508d1db38c0SClemens Ladisch value->value.integer.value[0] =
509d1db38c0SClemens Ladisch ((data->wm8776_regs[reg_index] & bit) != 0) ^ invert;
510d1db38c0SClemens Ladisch return 0;
511d1db38c0SClemens Ladisch }
512d1db38c0SClemens Ladisch
wm8776_bit_switch_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)513d1db38c0SClemens Ladisch static int wm8776_bit_switch_put(struct snd_kcontrol *ctl,
514d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
515d1db38c0SClemens Ladisch {
516d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
517d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
518d1db38c0SClemens Ladisch u16 bit = ctl->private_value & 0xffff;
519d1db38c0SClemens Ladisch u16 reg_value;
520d1db38c0SClemens Ladisch unsigned int reg_index = (ctl->private_value >> 16) & 0xff;
521d1db38c0SClemens Ladisch bool invert = (ctl->private_value >> 24) & 1;
522d1db38c0SClemens Ladisch int changed;
523d1db38c0SClemens Ladisch
524d1db38c0SClemens Ladisch mutex_lock(&chip->mutex);
525d1db38c0SClemens Ladisch reg_value = data->wm8776_regs[reg_index] & ~bit;
526d1db38c0SClemens Ladisch if (value->value.integer.value[0] ^ invert)
527d1db38c0SClemens Ladisch reg_value |= bit;
528d1db38c0SClemens Ladisch changed = reg_value != data->wm8776_regs[reg_index];
529d1db38c0SClemens Ladisch if (changed)
530d1db38c0SClemens Ladisch wm8776_write(chip, reg_index, reg_value);
531d1db38c0SClemens Ladisch mutex_unlock(&chip->mutex);
532d1db38c0SClemens Ladisch return changed;
533d1db38c0SClemens Ladisch }
534d1db38c0SClemens Ladisch
wm8776_field_enum_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)535d1db38c0SClemens Ladisch static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
536d1db38c0SClemens Ladisch struct snd_ctl_elem_info *info)
537d1db38c0SClemens Ladisch {
538d1db38c0SClemens Ladisch static const char *const hld[16] = {
539d1db38c0SClemens Ladisch "0 ms", "2.67 ms", "5.33 ms", "10.6 ms",
540d1db38c0SClemens Ladisch "21.3 ms", "42.7 ms", "85.3 ms", "171 ms",
541d1db38c0SClemens Ladisch "341 ms", "683 ms", "1.37 s", "2.73 s",
542d1db38c0SClemens Ladisch "5.46 s", "10.9 s", "21.8 s", "43.7 s",
543d1db38c0SClemens Ladisch };
544d1db38c0SClemens Ladisch static const char *const atk_lim[11] = {
545d1db38c0SClemens Ladisch "0.25 ms", "0.5 ms", "1 ms", "2 ms",
546d1db38c0SClemens Ladisch "4 ms", "8 ms", "16 ms", "32 ms",
547d1db38c0SClemens Ladisch "64 ms", "128 ms", "256 ms",
548d1db38c0SClemens Ladisch };
549d1db38c0SClemens Ladisch static const char *const atk_alc[11] = {
550d1db38c0SClemens Ladisch "8.40 ms", "16.8 ms", "33.6 ms", "67.2 ms",
551d1db38c0SClemens Ladisch "134 ms", "269 ms", "538 ms", "1.08 s",
552d1db38c0SClemens Ladisch "2.15 s", "4.3 s", "8.6 s",
553d1db38c0SClemens Ladisch };
554d1db38c0SClemens Ladisch static const char *const dcy_lim[11] = {
555d1db38c0SClemens Ladisch "1.2 ms", "2.4 ms", "4.8 ms", "9.6 ms",
556d1db38c0SClemens Ladisch "19.2 ms", "38.4 ms", "76.8 ms", "154 ms",
557d1db38c0SClemens Ladisch "307 ms", "614 ms", "1.23 s",
558d1db38c0SClemens Ladisch };
559d1db38c0SClemens Ladisch static const char *const dcy_alc[11] = {
560d1db38c0SClemens Ladisch "33.5 ms", "67.0 ms", "134 ms", "268 ms",
561d1db38c0SClemens Ladisch "536 ms", "1.07 s", "2.14 s", "4.29 s",
562d1db38c0SClemens Ladisch "8.58 s", "17.2 s", "34.3 s",
563d1db38c0SClemens Ladisch };
564d1db38c0SClemens Ladisch static const char *const tranwin[8] = {
565d1db38c0SClemens Ladisch "0 us", "62.5 us", "125 us", "250 us",
566d1db38c0SClemens Ladisch "500 us", "1 ms", "2 ms", "4 ms",
567d1db38c0SClemens Ladisch };
568d1db38c0SClemens Ladisch u8 max;
569d1db38c0SClemens Ladisch const char *const *names;
570d1db38c0SClemens Ladisch
571d1db38c0SClemens Ladisch max = (ctl->private_value >> 12) & 0xf;
572d1db38c0SClemens Ladisch switch ((ctl->private_value >> 24) & 0x1f) {
573d1db38c0SClemens Ladisch case WM8776_ALCCTRL2:
574d1db38c0SClemens Ladisch names = hld;
575d1db38c0SClemens Ladisch break;
576d1db38c0SClemens Ladisch case WM8776_ALCCTRL3:
577d1db38c0SClemens Ladisch if (((ctl->private_value >> 20) & 0xf) == 0) {
578d1db38c0SClemens Ladisch if (ctl->private_value & LC_CONTROL_LIMITER)
579d1db38c0SClemens Ladisch names = atk_lim;
580d1db38c0SClemens Ladisch else
581d1db38c0SClemens Ladisch names = atk_alc;
582d1db38c0SClemens Ladisch } else {
583d1db38c0SClemens Ladisch if (ctl->private_value & LC_CONTROL_LIMITER)
584d1db38c0SClemens Ladisch names = dcy_lim;
585d1db38c0SClemens Ladisch else
586d1db38c0SClemens Ladisch names = dcy_alc;
587d1db38c0SClemens Ladisch }
588d1db38c0SClemens Ladisch break;
589d1db38c0SClemens Ladisch case WM8776_LIMITER:
590d1db38c0SClemens Ladisch names = tranwin;
591d1db38c0SClemens Ladisch break;
592d1db38c0SClemens Ladisch default:
593d1db38c0SClemens Ladisch return -ENXIO;
594d1db38c0SClemens Ladisch }
5959600732bSClemens Ladisch return snd_ctl_enum_info(info, 1, max + 1, names);
596d1db38c0SClemens Ladisch }
597d1db38c0SClemens Ladisch
wm8776_field_volume_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)598d1db38c0SClemens Ladisch static int wm8776_field_volume_info(struct snd_kcontrol *ctl,
599d1db38c0SClemens Ladisch struct snd_ctl_elem_info *info)
600d1db38c0SClemens Ladisch {
601d1db38c0SClemens Ladisch info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
602d1db38c0SClemens Ladisch info->count = 1;
603d1db38c0SClemens Ladisch info->value.integer.min = (ctl->private_value >> 8) & 0xf;
604d1db38c0SClemens Ladisch info->value.integer.max = (ctl->private_value >> 12) & 0xf;
605d1db38c0SClemens Ladisch return 0;
606d1db38c0SClemens Ladisch }
607d1db38c0SClemens Ladisch
wm8776_field_set_from_ctl(struct snd_kcontrol * ctl)608d1db38c0SClemens Ladisch static void wm8776_field_set_from_ctl(struct snd_kcontrol *ctl)
609d1db38c0SClemens Ladisch {
610d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
611d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
612d1db38c0SClemens Ladisch unsigned int value, reg_index, mode;
613d1db38c0SClemens Ladisch u8 min, max, shift;
614d1db38c0SClemens Ladisch u16 mask, reg_value;
615d1db38c0SClemens Ladisch bool invert;
616d1db38c0SClemens Ladisch
617d1db38c0SClemens Ladisch if ((data->wm8776_regs[WM8776_ALCCTRL1] & WM8776_LCSEL_MASK) ==
618d1db38c0SClemens Ladisch WM8776_LCSEL_LIMITER)
619d1db38c0SClemens Ladisch mode = LC_CONTROL_LIMITER;
620d1db38c0SClemens Ladisch else
621d1db38c0SClemens Ladisch mode = LC_CONTROL_ALC;
622d1db38c0SClemens Ladisch if (!(ctl->private_value & mode))
623d1db38c0SClemens Ladisch return;
624d1db38c0SClemens Ladisch
625d1db38c0SClemens Ladisch value = ctl->private_value & 0xf;
626d1db38c0SClemens Ladisch min = (ctl->private_value >> 8) & 0xf;
627d1db38c0SClemens Ladisch max = (ctl->private_value >> 12) & 0xf;
628d1db38c0SClemens Ladisch mask = (ctl->private_value >> 16) & 0xf;
629d1db38c0SClemens Ladisch shift = (ctl->private_value >> 20) & 0xf;
630d1db38c0SClemens Ladisch reg_index = (ctl->private_value >> 24) & 0x1f;
631d1db38c0SClemens Ladisch invert = (ctl->private_value >> 29) & 0x1;
632d1db38c0SClemens Ladisch
633d1db38c0SClemens Ladisch if (invert)
634d1db38c0SClemens Ladisch value = max - (value - min);
635d1db38c0SClemens Ladisch reg_value = data->wm8776_regs[reg_index];
636d1db38c0SClemens Ladisch reg_value &= ~(mask << shift);
637d1db38c0SClemens Ladisch reg_value |= value << shift;
638d1db38c0SClemens Ladisch wm8776_write_cached(chip, reg_index, reg_value);
639d1db38c0SClemens Ladisch }
640d1db38c0SClemens Ladisch
wm8776_field_set(struct snd_kcontrol * ctl,unsigned int value)641d1db38c0SClemens Ladisch static int wm8776_field_set(struct snd_kcontrol *ctl, unsigned int value)
642d1db38c0SClemens Ladisch {
643d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
644d1db38c0SClemens Ladisch u8 min, max;
645d1db38c0SClemens Ladisch int changed;
646d1db38c0SClemens Ladisch
647d1db38c0SClemens Ladisch min = (ctl->private_value >> 8) & 0xf;
648d1db38c0SClemens Ladisch max = (ctl->private_value >> 12) & 0xf;
649d1db38c0SClemens Ladisch if (value < min || value > max)
650d1db38c0SClemens Ladisch return -EINVAL;
651d1db38c0SClemens Ladisch mutex_lock(&chip->mutex);
652d1db38c0SClemens Ladisch changed = value != (ctl->private_value & 0xf);
653d1db38c0SClemens Ladisch if (changed) {
654d1db38c0SClemens Ladisch ctl->private_value = (ctl->private_value & ~0xf) | value;
655d1db38c0SClemens Ladisch wm8776_field_set_from_ctl(ctl);
656d1db38c0SClemens Ladisch }
657d1db38c0SClemens Ladisch mutex_unlock(&chip->mutex);
658d1db38c0SClemens Ladisch return changed;
659d1db38c0SClemens Ladisch }
660d1db38c0SClemens Ladisch
wm8776_field_enum_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)661d1db38c0SClemens Ladisch static int wm8776_field_enum_get(struct snd_kcontrol *ctl,
662d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
663d1db38c0SClemens Ladisch {
664d1db38c0SClemens Ladisch value->value.enumerated.item[0] = ctl->private_value & 0xf;
665d1db38c0SClemens Ladisch return 0;
666d1db38c0SClemens Ladisch }
667d1db38c0SClemens Ladisch
wm8776_field_volume_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)668d1db38c0SClemens Ladisch static int wm8776_field_volume_get(struct snd_kcontrol *ctl,
669d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
670d1db38c0SClemens Ladisch {
671d1db38c0SClemens Ladisch value->value.integer.value[0] = ctl->private_value & 0xf;
672d1db38c0SClemens Ladisch return 0;
673d1db38c0SClemens Ladisch }
674d1db38c0SClemens Ladisch
wm8776_field_enum_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)675d1db38c0SClemens Ladisch static int wm8776_field_enum_put(struct snd_kcontrol *ctl,
676d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
677d1db38c0SClemens Ladisch {
678d1db38c0SClemens Ladisch return wm8776_field_set(ctl, value->value.enumerated.item[0]);
679d1db38c0SClemens Ladisch }
680d1db38c0SClemens Ladisch
wm8776_field_volume_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)681d1db38c0SClemens Ladisch static int wm8776_field_volume_put(struct snd_kcontrol *ctl,
682d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
683d1db38c0SClemens Ladisch {
684d1db38c0SClemens Ladisch return wm8776_field_set(ctl, value->value.integer.value[0]);
685d1db38c0SClemens Ladisch }
686d1db38c0SClemens Ladisch
wm8776_hp_vol_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)687d1db38c0SClemens Ladisch static int wm8776_hp_vol_info(struct snd_kcontrol *ctl,
688d1db38c0SClemens Ladisch struct snd_ctl_elem_info *info)
689d1db38c0SClemens Ladisch {
690d1db38c0SClemens Ladisch info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
691d1db38c0SClemens Ladisch info->count = 2;
692d1db38c0SClemens Ladisch info->value.integer.min = 0x79 - 60;
693d1db38c0SClemens Ladisch info->value.integer.max = 0x7f;
694d1db38c0SClemens Ladisch return 0;
695d1db38c0SClemens Ladisch }
696d1db38c0SClemens Ladisch
wm8776_hp_vol_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)697d1db38c0SClemens Ladisch static int wm8776_hp_vol_get(struct snd_kcontrol *ctl,
698d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
699d1db38c0SClemens Ladisch {
700d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
701d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
702d1db38c0SClemens Ladisch
703d1db38c0SClemens Ladisch mutex_lock(&chip->mutex);
704d1db38c0SClemens Ladisch value->value.integer.value[0] =
705d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK;
706d1db38c0SClemens Ladisch value->value.integer.value[1] =
707d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK;
708d1db38c0SClemens Ladisch mutex_unlock(&chip->mutex);
709d1db38c0SClemens Ladisch return 0;
710d1db38c0SClemens Ladisch }
711d1db38c0SClemens Ladisch
wm8776_hp_vol_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)712d1db38c0SClemens Ladisch static int wm8776_hp_vol_put(struct snd_kcontrol *ctl,
713d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
714d1db38c0SClemens Ladisch {
715d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
716d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
717d1db38c0SClemens Ladisch u8 to_update;
718d1db38c0SClemens Ladisch
719d1db38c0SClemens Ladisch mutex_lock(&chip->mutex);
720d1db38c0SClemens Ladisch to_update = (value->value.integer.value[0] !=
721d1db38c0SClemens Ladisch (data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK))
722d1db38c0SClemens Ladisch << 0;
723d1db38c0SClemens Ladisch to_update |= (value->value.integer.value[1] !=
724d1db38c0SClemens Ladisch (data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK))
725d1db38c0SClemens Ladisch << 1;
726d1db38c0SClemens Ladisch if (value->value.integer.value[0] == value->value.integer.value[1]) {
727d1db38c0SClemens Ladisch if (to_update) {
728d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_HPMASTER,
729d1db38c0SClemens Ladisch value->value.integer.value[0] |
730d1db38c0SClemens Ladisch WM8776_HPZCEN | WM8776_UPDATE);
731d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_HPLVOL] =
732d1db38c0SClemens Ladisch value->value.integer.value[0] | WM8776_HPZCEN;
733d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_HPRVOL] =
734d1db38c0SClemens Ladisch value->value.integer.value[0] | WM8776_HPZCEN;
735d1db38c0SClemens Ladisch }
736d1db38c0SClemens Ladisch } else {
737d1db38c0SClemens Ladisch if (to_update & 1)
738d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_HPLVOL,
739d1db38c0SClemens Ladisch value->value.integer.value[0] |
740d1db38c0SClemens Ladisch WM8776_HPZCEN |
741d1db38c0SClemens Ladisch ((to_update & 2) ? 0 : WM8776_UPDATE));
742d1db38c0SClemens Ladisch if (to_update & 2)
743d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_HPRVOL,
744d1db38c0SClemens Ladisch value->value.integer.value[1] |
745d1db38c0SClemens Ladisch WM8776_HPZCEN | WM8776_UPDATE);
746d1db38c0SClemens Ladisch }
747d1db38c0SClemens Ladisch mutex_unlock(&chip->mutex);
748d1db38c0SClemens Ladisch return to_update != 0;
749d1db38c0SClemens Ladisch }
750d1db38c0SClemens Ladisch
wm8776_input_mux_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)751d1db38c0SClemens Ladisch static int wm8776_input_mux_get(struct snd_kcontrol *ctl,
752d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
753d1db38c0SClemens Ladisch {
754d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
755d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
756d1db38c0SClemens Ladisch unsigned int mux_bit = ctl->private_value;
757d1db38c0SClemens Ladisch
758d1db38c0SClemens Ladisch value->value.integer.value[0] =
759d1db38c0SClemens Ladisch !!(data->wm8776_regs[WM8776_ADCMUX] & mux_bit);
760d1db38c0SClemens Ladisch return 0;
761d1db38c0SClemens Ladisch }
762d1db38c0SClemens Ladisch
wm8776_input_mux_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)763d1db38c0SClemens Ladisch static int wm8776_input_mux_put(struct snd_kcontrol *ctl,
764d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
765d1db38c0SClemens Ladisch {
766d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
767d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
768fe6ce80aSClemens Ladisch struct snd_kcontrol *other_ctl;
769d1db38c0SClemens Ladisch unsigned int mux_bit = ctl->private_value;
770d1db38c0SClemens Ladisch u16 reg;
771d1db38c0SClemens Ladisch int changed;
772d1db38c0SClemens Ladisch
773d1db38c0SClemens Ladisch mutex_lock(&chip->mutex);
774d1db38c0SClemens Ladisch reg = data->wm8776_regs[WM8776_ADCMUX];
775d1db38c0SClemens Ladisch if (value->value.integer.value[0]) {
776d1db38c0SClemens Ladisch reg |= mux_bit;
777fe6ce80aSClemens Ladisch /* line-in and mic-in are exclusive */
778fe6ce80aSClemens Ladisch mux_bit ^= 3;
779fe6ce80aSClemens Ladisch if (reg & mux_bit) {
780fe6ce80aSClemens Ladisch reg &= ~mux_bit;
781fe6ce80aSClemens Ladisch if (mux_bit == 1)
782fe6ce80aSClemens Ladisch other_ctl = data->line_adcmux_control;
783fe6ce80aSClemens Ladisch else
784fe6ce80aSClemens Ladisch other_ctl = data->mic_adcmux_control;
785fe6ce80aSClemens Ladisch snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
786fe6ce80aSClemens Ladisch &other_ctl->id);
787fe6ce80aSClemens Ladisch }
788d1db38c0SClemens Ladisch } else
789d1db38c0SClemens Ladisch reg &= ~mux_bit;
790d1db38c0SClemens Ladisch changed = reg != data->wm8776_regs[WM8776_ADCMUX];
791d1db38c0SClemens Ladisch if (changed) {
792d1db38c0SClemens Ladisch oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
793d1db38c0SClemens Ladisch reg & 1 ? GPIO_DS_INPUT_ROUTE : 0,
794d1db38c0SClemens Ladisch GPIO_DS_INPUT_ROUTE);
795d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_ADCMUX, reg);
796d1db38c0SClemens Ladisch }
797d1db38c0SClemens Ladisch mutex_unlock(&chip->mutex);
798d1db38c0SClemens Ladisch return changed;
799d1db38c0SClemens Ladisch }
800d1db38c0SClemens Ladisch
wm8776_input_vol_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)801d1db38c0SClemens Ladisch static int wm8776_input_vol_info(struct snd_kcontrol *ctl,
802d1db38c0SClemens Ladisch struct snd_ctl_elem_info *info)
803d1db38c0SClemens Ladisch {
804d1db38c0SClemens Ladisch info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
805d1db38c0SClemens Ladisch info->count = 2;
806d1db38c0SClemens Ladisch info->value.integer.min = 0xa5;
807d1db38c0SClemens Ladisch info->value.integer.max = 0xff;
808d1db38c0SClemens Ladisch return 0;
809d1db38c0SClemens Ladisch }
810d1db38c0SClemens Ladisch
wm8776_input_vol_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)811d1db38c0SClemens Ladisch static int wm8776_input_vol_get(struct snd_kcontrol *ctl,
812d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
813d1db38c0SClemens Ladisch {
814d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
815d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
816d1db38c0SClemens Ladisch
817d1db38c0SClemens Ladisch mutex_lock(&chip->mutex);
818d1db38c0SClemens Ladisch value->value.integer.value[0] =
819d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK;
820d1db38c0SClemens Ladisch value->value.integer.value[1] =
821d1db38c0SClemens Ladisch data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK;
822d1db38c0SClemens Ladisch mutex_unlock(&chip->mutex);
823d1db38c0SClemens Ladisch return 0;
824d1db38c0SClemens Ladisch }
825d1db38c0SClemens Ladisch
wm8776_input_vol_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)826d1db38c0SClemens Ladisch static int wm8776_input_vol_put(struct snd_kcontrol *ctl,
827d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
828d1db38c0SClemens Ladisch {
829d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
830d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
831d1db38c0SClemens Ladisch int changed = 0;
832d1db38c0SClemens Ladisch
833d1db38c0SClemens Ladisch mutex_lock(&chip->mutex);
834d1db38c0SClemens Ladisch changed = (value->value.integer.value[0] !=
835d1db38c0SClemens Ladisch (data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK)) ||
836d1db38c0SClemens Ladisch (value->value.integer.value[1] !=
837d1db38c0SClemens Ladisch (data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK));
838d1db38c0SClemens Ladisch wm8776_write_cached(chip, WM8776_ADCLVOL,
839d1db38c0SClemens Ladisch value->value.integer.value[0] | WM8776_ZCA);
840d1db38c0SClemens Ladisch wm8776_write_cached(chip, WM8776_ADCRVOL,
841d1db38c0SClemens Ladisch value->value.integer.value[1] | WM8776_ZCA);
842d1db38c0SClemens Ladisch mutex_unlock(&chip->mutex);
843d1db38c0SClemens Ladisch return changed;
844d1db38c0SClemens Ladisch }
845d1db38c0SClemens Ladisch
wm8776_level_control_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)846d1db38c0SClemens Ladisch static int wm8776_level_control_info(struct snd_kcontrol *ctl,
847d1db38c0SClemens Ladisch struct snd_ctl_elem_info *info)
848d1db38c0SClemens Ladisch {
849d1db38c0SClemens Ladisch static const char *const names[3] = {
850d1db38c0SClemens Ladisch "None", "Peak Limiter", "Automatic Level Control"
851d1db38c0SClemens Ladisch };
8529600732bSClemens Ladisch
8539600732bSClemens Ladisch return snd_ctl_enum_info(info, 1, 3, names);
854d1db38c0SClemens Ladisch }
855d1db38c0SClemens Ladisch
wm8776_level_control_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)856d1db38c0SClemens Ladisch static int wm8776_level_control_get(struct snd_kcontrol *ctl,
857d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
858d1db38c0SClemens Ladisch {
859d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
860d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
861d1db38c0SClemens Ladisch
862d1db38c0SClemens Ladisch if (!(data->wm8776_regs[WM8776_ALCCTRL2] & WM8776_LCEN))
863d1db38c0SClemens Ladisch value->value.enumerated.item[0] = 0;
864d1db38c0SClemens Ladisch else if ((data->wm8776_regs[WM8776_ALCCTRL1] & WM8776_LCSEL_MASK) ==
865d1db38c0SClemens Ladisch WM8776_LCSEL_LIMITER)
866d1db38c0SClemens Ladisch value->value.enumerated.item[0] = 1;
867d1db38c0SClemens Ladisch else
868d1db38c0SClemens Ladisch value->value.enumerated.item[0] = 2;
869d1db38c0SClemens Ladisch return 0;
870d1db38c0SClemens Ladisch }
871d1db38c0SClemens Ladisch
activate_control(struct oxygen * chip,struct snd_kcontrol * ctl,unsigned int mode)872d1db38c0SClemens Ladisch static void activate_control(struct oxygen *chip,
873d1db38c0SClemens Ladisch struct snd_kcontrol *ctl, unsigned int mode)
874d1db38c0SClemens Ladisch {
875d1db38c0SClemens Ladisch unsigned int access;
876d1db38c0SClemens Ladisch
877d1db38c0SClemens Ladisch if (ctl->private_value & mode)
878d1db38c0SClemens Ladisch access = 0;
879d1db38c0SClemens Ladisch else
880d1db38c0SClemens Ladisch access = SNDRV_CTL_ELEM_ACCESS_INACTIVE;
881d1db38c0SClemens Ladisch if ((ctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) != access) {
882d1db38c0SClemens Ladisch ctl->vd[0].access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
883d1db38c0SClemens Ladisch snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
884d1db38c0SClemens Ladisch }
885d1db38c0SClemens Ladisch }
886d1db38c0SClemens Ladisch
wm8776_level_control_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)887d1db38c0SClemens Ladisch static int wm8776_level_control_put(struct snd_kcontrol *ctl,
888d1db38c0SClemens Ladisch struct snd_ctl_elem_value *value)
889d1db38c0SClemens Ladisch {
890d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
891d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
892d1db38c0SClemens Ladisch unsigned int mode = 0, i;
893d1db38c0SClemens Ladisch u16 ctrl1, ctrl2;
894d1db38c0SClemens Ladisch int changed;
895d1db38c0SClemens Ladisch
896d1db38c0SClemens Ladisch if (value->value.enumerated.item[0] >= 3)
897d1db38c0SClemens Ladisch return -EINVAL;
898d1db38c0SClemens Ladisch mutex_lock(&chip->mutex);
899d1db38c0SClemens Ladisch changed = value->value.enumerated.item[0] != ctl->private_value;
900d1db38c0SClemens Ladisch if (changed) {
901d1db38c0SClemens Ladisch ctl->private_value = value->value.enumerated.item[0];
902d1db38c0SClemens Ladisch ctrl1 = data->wm8776_regs[WM8776_ALCCTRL1];
903d1db38c0SClemens Ladisch ctrl2 = data->wm8776_regs[WM8776_ALCCTRL2];
904d1db38c0SClemens Ladisch switch (value->value.enumerated.item[0]) {
905d1db38c0SClemens Ladisch default:
906d1db38c0SClemens Ladisch wm8776_write_cached(chip, WM8776_ALCCTRL2,
907d1db38c0SClemens Ladisch ctrl2 & ~WM8776_LCEN);
908d1db38c0SClemens Ladisch break;
909d1db38c0SClemens Ladisch case 1:
910d1db38c0SClemens Ladisch wm8776_write_cached(chip, WM8776_ALCCTRL1,
911d1db38c0SClemens Ladisch (ctrl1 & ~WM8776_LCSEL_MASK) |
912d1db38c0SClemens Ladisch WM8776_LCSEL_LIMITER);
913d1db38c0SClemens Ladisch wm8776_write_cached(chip, WM8776_ALCCTRL2,
914d1db38c0SClemens Ladisch ctrl2 | WM8776_LCEN);
915d1db38c0SClemens Ladisch mode = LC_CONTROL_LIMITER;
916d1db38c0SClemens Ladisch break;
917d1db38c0SClemens Ladisch case 2:
918d1db38c0SClemens Ladisch wm8776_write_cached(chip, WM8776_ALCCTRL1,
919d1db38c0SClemens Ladisch (ctrl1 & ~WM8776_LCSEL_MASK) |
920d1db38c0SClemens Ladisch WM8776_LCSEL_ALC_STEREO);
921d1db38c0SClemens Ladisch wm8776_write_cached(chip, WM8776_ALCCTRL2,
922d1db38c0SClemens Ladisch ctrl2 | WM8776_LCEN);
923d1db38c0SClemens Ladisch mode = LC_CONTROL_ALC;
924d1db38c0SClemens Ladisch break;
925d1db38c0SClemens Ladisch }
926d1db38c0SClemens Ladisch for (i = 0; i < ARRAY_SIZE(data->lc_controls); ++i)
927d1db38c0SClemens Ladisch activate_control(chip, data->lc_controls[i], mode);
928d1db38c0SClemens Ladisch }
929d1db38c0SClemens Ladisch mutex_unlock(&chip->mutex);
930d1db38c0SClemens Ladisch return changed;
931d1db38c0SClemens Ladisch }
932d1db38c0SClemens Ladisch
hpf_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)933d1db38c0SClemens Ladisch static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
934d1db38c0SClemens Ladisch {
935d1db38c0SClemens Ladisch static const char *const names[2] = {
936d1db38c0SClemens Ladisch "None", "High-pass Filter"
937d1db38c0SClemens Ladisch };
938d1db38c0SClemens Ladisch
9399600732bSClemens Ladisch return snd_ctl_enum_info(info, 1, 2, names);
940d1db38c0SClemens Ladisch }
941d1db38c0SClemens Ladisch
hpf_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)942d1db38c0SClemens Ladisch static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
943d1db38c0SClemens Ladisch {
944d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
945d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
946d1db38c0SClemens Ladisch
947d1db38c0SClemens Ladisch value->value.enumerated.item[0] =
948d1db38c0SClemens Ladisch !(data->wm8776_regs[WM8776_ADCIFCTRL] & WM8776_ADCHPD);
949d1db38c0SClemens Ladisch return 0;
950d1db38c0SClemens Ladisch }
951d1db38c0SClemens Ladisch
hpf_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)952d1db38c0SClemens Ladisch static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
953d1db38c0SClemens Ladisch {
954d1db38c0SClemens Ladisch struct oxygen *chip = ctl->private_data;
955d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
956d1db38c0SClemens Ladisch unsigned int reg;
957d1db38c0SClemens Ladisch int changed;
958d1db38c0SClemens Ladisch
959d1db38c0SClemens Ladisch mutex_lock(&chip->mutex);
960d1db38c0SClemens Ladisch reg = data->wm8776_regs[WM8776_ADCIFCTRL] & ~WM8776_ADCHPD;
961d1db38c0SClemens Ladisch if (!value->value.enumerated.item[0])
962d1db38c0SClemens Ladisch reg |= WM8776_ADCHPD;
963d1db38c0SClemens Ladisch changed = reg != data->wm8776_regs[WM8776_ADCIFCTRL];
964d1db38c0SClemens Ladisch if (changed)
965d1db38c0SClemens Ladisch wm8776_write(chip, WM8776_ADCIFCTRL, reg);
966d1db38c0SClemens Ladisch mutex_unlock(&chip->mutex);
967d1db38c0SClemens Ladisch return changed;
968d1db38c0SClemens Ladisch }
969d1db38c0SClemens Ladisch
970d1db38c0SClemens Ladisch #define WM8776_BIT_SWITCH(xname, reg, bit, invert, flags) { \
971d1db38c0SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
972d1db38c0SClemens Ladisch .name = xname, \
973d1db38c0SClemens Ladisch .info = snd_ctl_boolean_mono_info, \
974d1db38c0SClemens Ladisch .get = wm8776_bit_switch_get, \
975d1db38c0SClemens Ladisch .put = wm8776_bit_switch_put, \
976d1db38c0SClemens Ladisch .private_value = ((reg) << 16) | (bit) | ((invert) << 24) | (flags), \
977d1db38c0SClemens Ladisch }
978d1db38c0SClemens Ladisch #define _WM8776_FIELD_CTL(xname, reg, shift, initval, min, max, mask, flags) \
979d1db38c0SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
980d1db38c0SClemens Ladisch .name = xname, \
981d1db38c0SClemens Ladisch .private_value = (initval) | ((min) << 8) | ((max) << 12) | \
982d1db38c0SClemens Ladisch ((mask) << 16) | ((shift) << 20) | ((reg) << 24) | (flags)
983d1db38c0SClemens Ladisch #define WM8776_FIELD_CTL_ENUM(xname, reg, shift, init, min, max, mask, flags) {\
984d1db38c0SClemens Ladisch _WM8776_FIELD_CTL(xname " Capture Enum", \
985d1db38c0SClemens Ladisch reg, shift, init, min, max, mask, flags), \
986d1db38c0SClemens Ladisch .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
987d1db38c0SClemens Ladisch SNDRV_CTL_ELEM_ACCESS_INACTIVE, \
988d1db38c0SClemens Ladisch .info = wm8776_field_enum_info, \
989d1db38c0SClemens Ladisch .get = wm8776_field_enum_get, \
990d1db38c0SClemens Ladisch .put = wm8776_field_enum_put, \
991d1db38c0SClemens Ladisch }
992d1db38c0SClemens Ladisch #define WM8776_FIELD_CTL_VOLUME(a, b, c, d, e, f, g, h, tlv_p) { \
993d1db38c0SClemens Ladisch _WM8776_FIELD_CTL(a " Capture Volume", b, c, d, e, f, g, h), \
994d1db38c0SClemens Ladisch .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
995d1db38c0SClemens Ladisch SNDRV_CTL_ELEM_ACCESS_INACTIVE | \
996d1db38c0SClemens Ladisch SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
997d1db38c0SClemens Ladisch .info = wm8776_field_volume_info, \
998d1db38c0SClemens Ladisch .get = wm8776_field_volume_get, \
999d1db38c0SClemens Ladisch .put = wm8776_field_volume_put, \
1000d1db38c0SClemens Ladisch .tlv = { .p = tlv_p }, \
1001d1db38c0SClemens Ladisch }
1002d1db38c0SClemens Ladisch
1003d1db38c0SClemens Ladisch static const DECLARE_TLV_DB_SCALE(wm87x6_dac_db_scale, -6000, 50, 0);
1004d1db38c0SClemens Ladisch static const DECLARE_TLV_DB_SCALE(wm8776_adc_db_scale, -2100, 50, 0);
1005d1db38c0SClemens Ladisch static const DECLARE_TLV_DB_SCALE(wm8776_hp_db_scale, -6000, 100, 0);
1006d1db38c0SClemens Ladisch static const DECLARE_TLV_DB_SCALE(wm8776_lct_db_scale, -1600, 100, 0);
1007d1db38c0SClemens Ladisch static const DECLARE_TLV_DB_SCALE(wm8776_maxgain_db_scale, 0, 400, 0);
1008d1db38c0SClemens Ladisch static const DECLARE_TLV_DB_SCALE(wm8776_ngth_db_scale, -7800, 600, 0);
1009d1db38c0SClemens Ladisch static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_lim_db_scale, -1200, 100, 0);
1010d1db38c0SClemens Ladisch static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_alc_db_scale, -2100, 400, 0);
1011d1db38c0SClemens Ladisch
1012d1db38c0SClemens Ladisch static const struct snd_kcontrol_new ds_controls[] = {
1013d1db38c0SClemens Ladisch {
1014d1db38c0SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1015d1db38c0SClemens Ladisch .name = "Headphone Playback Volume",
1016d1db38c0SClemens Ladisch .info = wm8776_hp_vol_info,
1017d1db38c0SClemens Ladisch .get = wm8776_hp_vol_get,
1018d1db38c0SClemens Ladisch .put = wm8776_hp_vol_put,
1019d1db38c0SClemens Ladisch .tlv = { .p = wm8776_hp_db_scale },
1020d1db38c0SClemens Ladisch },
1021d1db38c0SClemens Ladisch WM8776_BIT_SWITCH("Headphone Playback Switch",
1022d1db38c0SClemens Ladisch WM8776_PWRDOWN, WM8776_HPPD, 1, 0),
1023d1db38c0SClemens Ladisch {
1024d1db38c0SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1025d1db38c0SClemens Ladisch .name = "Input Capture Volume",
1026d1db38c0SClemens Ladisch .info = wm8776_input_vol_info,
1027d1db38c0SClemens Ladisch .get = wm8776_input_vol_get,
1028d1db38c0SClemens Ladisch .put = wm8776_input_vol_put,
1029d1db38c0SClemens Ladisch .tlv = { .p = wm8776_adc_db_scale },
1030d1db38c0SClemens Ladisch },
1031d1db38c0SClemens Ladisch {
1032d1db38c0SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1033d1db38c0SClemens Ladisch .name = "Line Capture Switch",
1034d1db38c0SClemens Ladisch .info = snd_ctl_boolean_mono_info,
1035d1db38c0SClemens Ladisch .get = wm8776_input_mux_get,
1036d1db38c0SClemens Ladisch .put = wm8776_input_mux_put,
1037d1db38c0SClemens Ladisch .private_value = 1 << 0,
1038d1db38c0SClemens Ladisch },
1039d1db38c0SClemens Ladisch {
1040d1db38c0SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1041d1db38c0SClemens Ladisch .name = "Mic Capture Switch",
1042d1db38c0SClemens Ladisch .info = snd_ctl_boolean_mono_info,
1043d1db38c0SClemens Ladisch .get = wm8776_input_mux_get,
1044d1db38c0SClemens Ladisch .put = wm8776_input_mux_put,
1045d1db38c0SClemens Ladisch .private_value = 1 << 1,
1046d1db38c0SClemens Ladisch },
10479bac84edSClemens Ladisch WM8776_BIT_SWITCH("Front Mic Capture Switch",
10489bac84edSClemens Ladisch WM8776_ADCMUX, 1 << 2, 0, 0),
10499bac84edSClemens Ladisch WM8776_BIT_SWITCH("Aux Capture Switch",
10509bac84edSClemens Ladisch WM8776_ADCMUX, 1 << 3, 0, 0),
1051d1db38c0SClemens Ladisch {
1052d1db38c0SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1053d1db38c0SClemens Ladisch .name = "ADC Filter Capture Enum",
1054d1db38c0SClemens Ladisch .info = hpf_info,
1055d1db38c0SClemens Ladisch .get = hpf_get,
1056d1db38c0SClemens Ladisch .put = hpf_put,
1057d1db38c0SClemens Ladisch },
1058d1db38c0SClemens Ladisch {
1059d1db38c0SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1060d1db38c0SClemens Ladisch .name = "Level Control Capture Enum",
1061d1db38c0SClemens Ladisch .info = wm8776_level_control_info,
1062d1db38c0SClemens Ladisch .get = wm8776_level_control_get,
1063d1db38c0SClemens Ladisch .put = wm8776_level_control_put,
1064d1db38c0SClemens Ladisch .private_value = 0,
1065d1db38c0SClemens Ladisch },
1066d1db38c0SClemens Ladisch };
1067b532d6b8SClemens Ladisch static const struct snd_kcontrol_new hdav_slim_controls[] = {
1068b532d6b8SClemens Ladisch {
1069b532d6b8SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1070b532d6b8SClemens Ladisch .name = "HDMI Playback Switch",
1071b532d6b8SClemens Ladisch .info = snd_ctl_boolean_mono_info,
1072b532d6b8SClemens Ladisch .get = xonar_gpio_bit_switch_get,
1073b532d6b8SClemens Ladisch .put = xonar_gpio_bit_switch_put,
1074b532d6b8SClemens Ladisch .private_value = GPIO_SLIM_HDMI_DISABLE | XONAR_GPIO_BIT_INVERT,
1075b532d6b8SClemens Ladisch },
1076b532d6b8SClemens Ladisch {
1077b532d6b8SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1078b532d6b8SClemens Ladisch .name = "Headphone Playback Volume",
1079b532d6b8SClemens Ladisch .info = wm8776_hp_vol_info,
1080b532d6b8SClemens Ladisch .get = wm8776_hp_vol_get,
1081b532d6b8SClemens Ladisch .put = wm8776_hp_vol_put,
1082b532d6b8SClemens Ladisch .tlv = { .p = wm8776_hp_db_scale },
1083b532d6b8SClemens Ladisch },
1084b532d6b8SClemens Ladisch WM8776_BIT_SWITCH("Headphone Playback Switch",
1085b532d6b8SClemens Ladisch WM8776_PWRDOWN, WM8776_HPPD, 1, 0),
1086b532d6b8SClemens Ladisch {
1087b532d6b8SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1088b532d6b8SClemens Ladisch .name = "Input Capture Volume",
1089b532d6b8SClemens Ladisch .info = wm8776_input_vol_info,
1090b532d6b8SClemens Ladisch .get = wm8776_input_vol_get,
1091b532d6b8SClemens Ladisch .put = wm8776_input_vol_put,
1092b532d6b8SClemens Ladisch .tlv = { .p = wm8776_adc_db_scale },
1093b532d6b8SClemens Ladisch },
1094b532d6b8SClemens Ladisch WM8776_BIT_SWITCH("Mic Capture Switch",
1095b532d6b8SClemens Ladisch WM8776_ADCMUX, 1 << 0, 0, 0),
1096b532d6b8SClemens Ladisch WM8776_BIT_SWITCH("Aux Capture Switch",
1097b532d6b8SClemens Ladisch WM8776_ADCMUX, 1 << 1, 0, 0),
1098b532d6b8SClemens Ladisch {
1099b532d6b8SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1100b532d6b8SClemens Ladisch .name = "ADC Filter Capture Enum",
1101b532d6b8SClemens Ladisch .info = hpf_info,
1102b532d6b8SClemens Ladisch .get = hpf_get,
1103b532d6b8SClemens Ladisch .put = hpf_put,
1104b532d6b8SClemens Ladisch },
1105b532d6b8SClemens Ladisch {
1106b532d6b8SClemens Ladisch .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1107b532d6b8SClemens Ladisch .name = "Level Control Capture Enum",
1108b532d6b8SClemens Ladisch .info = wm8776_level_control_info,
1109b532d6b8SClemens Ladisch .get = wm8776_level_control_get,
1110b532d6b8SClemens Ladisch .put = wm8776_level_control_put,
1111b532d6b8SClemens Ladisch .private_value = 0,
1112b532d6b8SClemens Ladisch },
1113b532d6b8SClemens Ladisch };
1114d1db38c0SClemens Ladisch static const struct snd_kcontrol_new lc_controls[] = {
1115d1db38c0SClemens Ladisch WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
1116d1db38c0SClemens Ladisch WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
1117d1db38c0SClemens Ladisch LC_CONTROL_LIMITER, wm8776_lct_db_scale),
1118d1db38c0SClemens Ladisch WM8776_FIELD_CTL_ENUM("Limiter Attack Time",
1119d1db38c0SClemens Ladisch WM8776_ALCCTRL3, 0, 2, 0, 10, 0xf,
1120d1db38c0SClemens Ladisch LC_CONTROL_LIMITER),
1121d1db38c0SClemens Ladisch WM8776_FIELD_CTL_ENUM("Limiter Decay Time",
1122d1db38c0SClemens Ladisch WM8776_ALCCTRL3, 4, 3, 0, 10, 0xf,
1123d1db38c0SClemens Ladisch LC_CONTROL_LIMITER),
1124d1db38c0SClemens Ladisch WM8776_FIELD_CTL_ENUM("Limiter Transient Window",
1125d1db38c0SClemens Ladisch WM8776_LIMITER, 4, 2, 0, 7, 0x7,
1126d1db38c0SClemens Ladisch LC_CONTROL_LIMITER),
1127d1db38c0SClemens Ladisch WM8776_FIELD_CTL_VOLUME("Limiter Maximum Attenuation",
1128d1db38c0SClemens Ladisch WM8776_LIMITER, 0, 6, 3, 12, 0xf,
1129d1db38c0SClemens Ladisch LC_CONTROL_LIMITER,
1130d1db38c0SClemens Ladisch wm8776_maxatten_lim_db_scale),
1131d1db38c0SClemens Ladisch WM8776_FIELD_CTL_VOLUME("ALC Target Level",
1132d1db38c0SClemens Ladisch WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
1133d1db38c0SClemens Ladisch LC_CONTROL_ALC, wm8776_lct_db_scale),
1134d1db38c0SClemens Ladisch WM8776_FIELD_CTL_ENUM("ALC Attack Time",
1135d1db38c0SClemens Ladisch WM8776_ALCCTRL3, 0, 2, 0, 10, 0xf,
1136d1db38c0SClemens Ladisch LC_CONTROL_ALC),
1137d1db38c0SClemens Ladisch WM8776_FIELD_CTL_ENUM("ALC Decay Time",
1138d1db38c0SClemens Ladisch WM8776_ALCCTRL3, 4, 3, 0, 10, 0xf,
1139d1db38c0SClemens Ladisch LC_CONTROL_ALC),
1140d1db38c0SClemens Ladisch WM8776_FIELD_CTL_VOLUME("ALC Maximum Gain",
1141d1db38c0SClemens Ladisch WM8776_ALCCTRL1, 4, 7, 1, 7, 0x7,
1142d1db38c0SClemens Ladisch LC_CONTROL_ALC, wm8776_maxgain_db_scale),
1143d1db38c0SClemens Ladisch WM8776_FIELD_CTL_VOLUME("ALC Maximum Attenuation",
1144d1db38c0SClemens Ladisch WM8776_LIMITER, 0, 10, 10, 15, 0xf,
1145d1db38c0SClemens Ladisch LC_CONTROL_ALC, wm8776_maxatten_alc_db_scale),
1146d1db38c0SClemens Ladisch WM8776_FIELD_CTL_ENUM("ALC Hold Time",
1147d1db38c0SClemens Ladisch WM8776_ALCCTRL2, 0, 0, 0, 15, 0xf,
1148d1db38c0SClemens Ladisch LC_CONTROL_ALC),
1149d1db38c0SClemens Ladisch WM8776_BIT_SWITCH("Noise Gate Capture Switch",
1150d1db38c0SClemens Ladisch WM8776_NOISEGATE, WM8776_NGAT, 0,
1151d1db38c0SClemens Ladisch LC_CONTROL_ALC),
1152d1db38c0SClemens Ladisch WM8776_FIELD_CTL_VOLUME("Noise Gate Threshold",
1153d1db38c0SClemens Ladisch WM8776_NOISEGATE, 2, 0, 0, 7, 0x7,
1154d1db38c0SClemens Ladisch LC_CONTROL_ALC, wm8776_ngth_db_scale),
1155d1db38c0SClemens Ladisch };
1156d1db38c0SClemens Ladisch
add_lc_controls(struct oxygen * chip)1157b532d6b8SClemens Ladisch static int add_lc_controls(struct oxygen *chip)
1158b532d6b8SClemens Ladisch {
1159b532d6b8SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
1160b532d6b8SClemens Ladisch unsigned int i;
1161b532d6b8SClemens Ladisch struct snd_kcontrol *ctl;
1162b532d6b8SClemens Ladisch int err;
1163b532d6b8SClemens Ladisch
1164b532d6b8SClemens Ladisch BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
1165b532d6b8SClemens Ladisch for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
1166b532d6b8SClemens Ladisch ctl = snd_ctl_new1(&lc_controls[i], chip);
1167b532d6b8SClemens Ladisch if (!ctl)
1168b532d6b8SClemens Ladisch return -ENOMEM;
1169b532d6b8SClemens Ladisch err = snd_ctl_add(chip->card, ctl);
1170b532d6b8SClemens Ladisch if (err < 0)
1171b532d6b8SClemens Ladisch return err;
1172b532d6b8SClemens Ladisch data->lc_controls[i] = ctl;
1173b532d6b8SClemens Ladisch }
1174b532d6b8SClemens Ladisch return 0;
1175b532d6b8SClemens Ladisch }
1176b532d6b8SClemens Ladisch
xonar_ds_mixer_init(struct oxygen * chip)1177d1db38c0SClemens Ladisch static int xonar_ds_mixer_init(struct oxygen *chip)
1178d1db38c0SClemens Ladisch {
1179d1db38c0SClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
1180d1db38c0SClemens Ladisch unsigned int i;
1181d1db38c0SClemens Ladisch struct snd_kcontrol *ctl;
1182d1db38c0SClemens Ladisch int err;
1183d1db38c0SClemens Ladisch
1184d1db38c0SClemens Ladisch for (i = 0; i < ARRAY_SIZE(ds_controls); ++i) {
1185d1db38c0SClemens Ladisch ctl = snd_ctl_new1(&ds_controls[i], chip);
1186d1db38c0SClemens Ladisch if (!ctl)
1187d1db38c0SClemens Ladisch return -ENOMEM;
1188d1db38c0SClemens Ladisch err = snd_ctl_add(chip->card, ctl);
1189d1db38c0SClemens Ladisch if (err < 0)
1190d1db38c0SClemens Ladisch return err;
1191fe6ce80aSClemens Ladisch if (!strcmp(ctl->id.name, "Line Capture Switch"))
1192fe6ce80aSClemens Ladisch data->line_adcmux_control = ctl;
1193fe6ce80aSClemens Ladisch else if (!strcmp(ctl->id.name, "Mic Capture Switch"))
1194fe6ce80aSClemens Ladisch data->mic_adcmux_control = ctl;
1195d1db38c0SClemens Ladisch }
1196fe6ce80aSClemens Ladisch if (!data->line_adcmux_control || !data->mic_adcmux_control)
1197fe6ce80aSClemens Ladisch return -ENXIO;
1198b532d6b8SClemens Ladisch
1199b532d6b8SClemens Ladisch return add_lc_controls(chip);
1200b532d6b8SClemens Ladisch }
1201b532d6b8SClemens Ladisch
xonar_hdav_slim_mixer_init(struct oxygen * chip)1202b532d6b8SClemens Ladisch static int xonar_hdav_slim_mixer_init(struct oxygen *chip)
1203b532d6b8SClemens Ladisch {
1204b532d6b8SClemens Ladisch unsigned int i;
1205b532d6b8SClemens Ladisch struct snd_kcontrol *ctl;
1206b532d6b8SClemens Ladisch int err;
1207b532d6b8SClemens Ladisch
1208b532d6b8SClemens Ladisch for (i = 0; i < ARRAY_SIZE(hdav_slim_controls); ++i) {
1209b532d6b8SClemens Ladisch ctl = snd_ctl_new1(&hdav_slim_controls[i], chip);
1210d1db38c0SClemens Ladisch if (!ctl)
1211d1db38c0SClemens Ladisch return -ENOMEM;
1212d1db38c0SClemens Ladisch err = snd_ctl_add(chip->card, ctl);
1213d1db38c0SClemens Ladisch if (err < 0)
1214d1db38c0SClemens Ladisch return err;
1215d1db38c0SClemens Ladisch }
1216b532d6b8SClemens Ladisch
1217b532d6b8SClemens Ladisch return add_lc_controls(chip);
1218d1db38c0SClemens Ladisch }
1219d1db38c0SClemens Ladisch
dump_wm8776_registers(struct oxygen * chip,struct snd_info_buffer * buffer)12209719fcaaSClemens Ladisch static void dump_wm8776_registers(struct oxygen *chip,
12219719fcaaSClemens Ladisch struct snd_info_buffer *buffer)
12229719fcaaSClemens Ladisch {
12239719fcaaSClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
12249719fcaaSClemens Ladisch unsigned int i;
12259719fcaaSClemens Ladisch
12269719fcaaSClemens Ladisch snd_iprintf(buffer, "\nWM8776:\n00:");
12279719fcaaSClemens Ladisch for (i = 0; i < 0x10; ++i)
12289719fcaaSClemens Ladisch snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
12299719fcaaSClemens Ladisch snd_iprintf(buffer, "\n10:");
12309719fcaaSClemens Ladisch for (i = 0x10; i < 0x17; ++i)
12319719fcaaSClemens Ladisch snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
12329719fcaaSClemens Ladisch snd_iprintf(buffer, "\n");
12339719fcaaSClemens Ladisch }
12349719fcaaSClemens Ladisch
dump_wm87x6_registers(struct oxygen * chip,struct snd_info_buffer * buffer)12359719fcaaSClemens Ladisch static void dump_wm87x6_registers(struct oxygen *chip,
12369719fcaaSClemens Ladisch struct snd_info_buffer *buffer)
12379719fcaaSClemens Ladisch {
12389719fcaaSClemens Ladisch struct xonar_wm87x6 *data = chip->model_data;
12399719fcaaSClemens Ladisch unsigned int i;
12409719fcaaSClemens Ladisch
12419719fcaaSClemens Ladisch dump_wm8776_registers(chip, buffer);
12429719fcaaSClemens Ladisch snd_iprintf(buffer, "\nWM8766:\n00:");
12439719fcaaSClemens Ladisch for (i = 0; i < 0x10; ++i)
12449719fcaaSClemens Ladisch snd_iprintf(buffer, " %03x", data->wm8766_regs[i]);
12459719fcaaSClemens Ladisch snd_iprintf(buffer, "\n");
12469719fcaaSClemens Ladisch }
12479719fcaaSClemens Ladisch
1248d1db38c0SClemens Ladisch static const struct oxygen_model model_xonar_ds = {
124945bc307fSClemens Ladisch .longname = "Asus Virtuoso 66",
1250d1db38c0SClemens Ladisch .chip = "AV200",
1251d1db38c0SClemens Ladisch .init = xonar_ds_init,
1252d1db38c0SClemens Ladisch .mixer_init = xonar_ds_mixer_init,
1253d1db38c0SClemens Ladisch .cleanup = xonar_ds_cleanup,
1254d1db38c0SClemens Ladisch .suspend = xonar_ds_suspend,
1255d1db38c0SClemens Ladisch .resume = xonar_ds_resume,
1256d1db38c0SClemens Ladisch .pcm_hardware_filter = wm8776_adc_hardware_filter,
1257d1db38c0SClemens Ladisch .set_dac_params = set_wm87x6_dac_params,
1258d1db38c0SClemens Ladisch .set_adc_params = set_wm8776_adc_params,
1259d1db38c0SClemens Ladisch .update_dac_volume = update_wm87x6_volume,
1260d1db38c0SClemens Ladisch .update_dac_mute = update_wm87x6_mute,
12612dbf0ea2SClemens Ladisch .update_center_lfe_mix = update_wm8766_center_lfe_mix,
1262d1db38c0SClemens Ladisch .gpio_changed = xonar_ds_gpio_changed,
12639719fcaaSClemens Ladisch .dump_registers = dump_wm87x6_registers,
1264d1db38c0SClemens Ladisch .dac_tlv = wm87x6_dac_db_scale,
1265d1db38c0SClemens Ladisch .model_data_size = sizeof(struct xonar_wm87x6),
1266d1db38c0SClemens Ladisch .device_config = PLAYBACK_0_TO_I2S |
1267d1db38c0SClemens Ladisch PLAYBACK_1_TO_SPDIF |
126856225e4cSClemens Ladisch CAPTURE_0_FROM_I2S_1 |
126956225e4cSClemens Ladisch CAPTURE_1_FROM_SPDIF,
12701f4d7be7SClemens Ladisch .dac_channels_pcm = 8,
12711f4d7be7SClemens Ladisch .dac_channels_mixer = 8,
1272d1db38c0SClemens Ladisch .dac_volume_min = 255 - 2*60,
1273d1db38c0SClemens Ladisch .dac_volume_max = 255,
1274d1db38c0SClemens Ladisch .function_flags = OXYGEN_FUNCTION_SPI,
12755b8bf2a5SClemens Ladisch .dac_mclks = OXYGEN_MCLKS(256, 256, 128),
12765b8bf2a5SClemens Ladisch .adc_mclks = OXYGEN_MCLKS(256, 256, 128),
1277d1db38c0SClemens Ladisch .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
1278d1db38c0SClemens Ladisch .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
1279d1db38c0SClemens Ladisch };
1280d1db38c0SClemens Ladisch
1281b532d6b8SClemens Ladisch static const struct oxygen_model model_xonar_hdav_slim = {
1282b532d6b8SClemens Ladisch .shortname = "Xonar HDAV1.3 Slim",
1283b532d6b8SClemens Ladisch .longname = "Asus Virtuoso 200",
1284b532d6b8SClemens Ladisch .chip = "AV200",
1285b532d6b8SClemens Ladisch .init = xonar_hdav_slim_init,
1286b532d6b8SClemens Ladisch .mixer_init = xonar_hdav_slim_mixer_init,
1287b532d6b8SClemens Ladisch .cleanup = xonar_hdav_slim_cleanup,
1288b532d6b8SClemens Ladisch .suspend = xonar_hdav_slim_suspend,
1289b532d6b8SClemens Ladisch .resume = xonar_hdav_slim_resume,
1290b532d6b8SClemens Ladisch .pcm_hardware_filter = xonar_hdav_slim_hardware_filter,
1291b532d6b8SClemens Ladisch .set_dac_params = set_hdav_slim_dac_params,
1292b532d6b8SClemens Ladisch .set_adc_params = set_wm8776_adc_params,
1293b532d6b8SClemens Ladisch .update_dac_volume = update_wm8776_volume,
1294b532d6b8SClemens Ladisch .update_dac_mute = update_wm8776_mute,
1295b532d6b8SClemens Ladisch .uart_input = xonar_hdmi_uart_input,
1296b532d6b8SClemens Ladisch .dump_registers = dump_wm8776_registers,
1297b532d6b8SClemens Ladisch .dac_tlv = wm87x6_dac_db_scale,
1298b532d6b8SClemens Ladisch .model_data_size = sizeof(struct xonar_wm87x6),
1299b532d6b8SClemens Ladisch .device_config = PLAYBACK_0_TO_I2S |
1300b532d6b8SClemens Ladisch PLAYBACK_1_TO_SPDIF |
130156225e4cSClemens Ladisch CAPTURE_0_FROM_I2S_1 |
130256225e4cSClemens Ladisch CAPTURE_1_FROM_SPDIF,
1303b532d6b8SClemens Ladisch .dac_channels_pcm = 8,
1304b532d6b8SClemens Ladisch .dac_channels_mixer = 2,
1305b532d6b8SClemens Ladisch .dac_volume_min = 255 - 2*60,
1306b532d6b8SClemens Ladisch .dac_volume_max = 255,
1307b532d6b8SClemens Ladisch .function_flags = OXYGEN_FUNCTION_2WIRE,
1308b532d6b8SClemens Ladisch .dac_mclks = OXYGEN_MCLKS(256, 256, 128),
1309b532d6b8SClemens Ladisch .adc_mclks = OXYGEN_MCLKS(256, 256, 128),
1310b532d6b8SClemens Ladisch .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
1311b532d6b8SClemens Ladisch .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
1312b532d6b8SClemens Ladisch };
1313b532d6b8SClemens Ladisch
get_xonar_wm87x6_model(struct oxygen * chip,const struct pci_device_id * id)1314f120a6fbSBill Pemberton int get_xonar_wm87x6_model(struct oxygen *chip,
1315d1db38c0SClemens Ladisch const struct pci_device_id *id)
1316d1db38c0SClemens Ladisch {
1317d1db38c0SClemens Ladisch switch (id->subdevice) {
1318d1db38c0SClemens Ladisch case 0x838e:
1319d1db38c0SClemens Ladisch chip->model = model_xonar_ds;
132044923632SSergiu Giurgiu chip->model.shortname = "Xonar DS";
132144923632SSergiu Giurgiu break;
132244923632SSergiu Giurgiu case 0x8522:
132344923632SSergiu Giurgiu chip->model = model_xonar_ds;
132444923632SSergiu Giurgiu chip->model.shortname = "Xonar DSX";
1325d1db38c0SClemens Ladisch break;
1326b532d6b8SClemens Ladisch case 0x835e:
1327b532d6b8SClemens Ladisch chip->model = model_xonar_hdav_slim;
1328b532d6b8SClemens Ladisch break;
1329d1db38c0SClemens Ladisch default:
1330d1db38c0SClemens Ladisch return -EINVAL;
1331d1db38c0SClemens Ladisch }
1332d1db38c0SClemens Ladisch return 0;
1333d1db38c0SClemens Ladisch }
1334