blob: 42d1ab136217dd1e001d7a1d9621d26cd741285c [file] [log] [blame]
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001/*
Clemens Ladischb532d6b2011-01-10 16:23:57 +01002 * card driver for models with WM8776/WM8766 DACs (Xonar DS/HDAV1.3 Slim)
Clemens Ladischd1db38c2010-01-18 15:44:04 +01003 *
4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
5 *
6 *
7 * This driver is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License, version 2.
9 *
10 * This driver is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this driver; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19/*
20 * Xonar DS
21 * --------
22 *
23 * CMI8788:
24 *
Clemens Ladischde664932010-12-02 11:42:48 +010025 * SPI 0 -> WM8766 (surround, center/LFE, back)
26 * SPI 1 -> WM8776 (front, input)
Clemens Ladischd1db38c2010-01-18 15:44:04 +010027 *
Clemens Ladischde664932010-12-02 11:42:48 +010028 * GPIO 4 <- headphone detect, 0 = plugged
29 * GPIO 6 -> route input jack to mic-in (0) or line-in (1)
30 * GPIO 7 -> enable output to front L/R speaker channels
31 * GPIO 8 -> enable output to other speaker channels and front panel headphone
Clemens Ladisch9bac84e2010-09-09 12:19:21 +020032 *
Clemens Ladischde664932010-12-02 11:42:48 +010033 * WM8776:
Clemens Ladisch9bac84e2010-09-09 12:19:21 +020034 *
Clemens Ladischde664932010-12-02 11:42:48 +010035 * input 1 <- line
36 * input 2 <- mic
37 * input 3 <- front mic
38 * input 4 <- aux
39 */
40
41/*
42 * Xonar HDAV1.3 Slim
43 * ------------------
44 *
45 * CMI8788:
46 *
47 * I²C <-> WM8776 (addr 0011010)
48 *
49 * GPIO 0 -> disable HDMI output
50 * GPIO 1 -> enable HP output
51 * GPIO 6 -> firmware EEPROM I²C clock
52 * GPIO 7 <-> firmware EEPROM I²C data
53 *
54 * UART <-> HDMI controller
55 *
56 * WM8776:
57 *
58 * input 1 <- mic
59 * input 2 <- aux
Clemens Ladischd1db38c2010-01-18 15:44:04 +010060 */
61
62#include <linux/pci.h>
63#include <linux/delay.h>
64#include <sound/control.h>
65#include <sound/core.h>
Clemens Ladisch9719fca2010-12-02 11:41:10 +010066#include <sound/info.h>
Clemens Ladisch435feac2010-09-09 12:20:29 +020067#include <sound/jack.h>
Clemens Ladischd1db38c2010-01-18 15:44:04 +010068#include <sound/pcm.h>
69#include <sound/pcm_params.h>
70#include <sound/tlv.h>
71#include "xonar.h"
72#include "wm8776.h"
73#include "wm8766.h"
74
75#define GPIO_DS_HP_DETECT 0x0010
76#define GPIO_DS_INPUT_ROUTE 0x0040
Clemens Ladisch84cf83a2010-09-09 12:23:06 +020077#define GPIO_DS_OUTPUT_FRONTLR 0x0080
78#define GPIO_DS_OUTPUT_ENABLE 0x0100
Clemens Ladischd1db38c2010-01-18 15:44:04 +010079
Clemens Ladischb532d6b2011-01-10 16:23:57 +010080#define GPIO_SLIM_HDMI_DISABLE 0x0001
81#define GPIO_SLIM_OUTPUT_ENABLE 0x0002
82#define GPIO_SLIM_FIRMWARE_CLK 0x0040
83#define GPIO_SLIM_FIRMWARE_DATA 0x0080
84
85#define I2C_DEVICE_WM8776 0x34 /* 001101, 0, /W=0 */
86
Clemens Ladischd1db38c2010-01-18 15:44:04 +010087#define LC_CONTROL_LIMITER 0x40000000
88#define LC_CONTROL_ALC 0x20000000
89
90struct xonar_wm87x6 {
91 struct xonar_generic generic;
92 u16 wm8776_regs[0x17];
93 u16 wm8766_regs[0x10];
Clemens Ladischfe6ce802010-09-07 13:38:49 +020094 struct snd_kcontrol *line_adcmux_control;
95 struct snd_kcontrol *mic_adcmux_control;
Clemens Ladischd1db38c2010-01-18 15:44:04 +010096 struct snd_kcontrol *lc_controls[13];
Clemens Ladisch435feac2010-09-09 12:20:29 +020097 struct snd_jack *hp_jack;
Clemens Ladischb532d6b2011-01-10 16:23:57 +010098 struct xonar_hdmi hdmi;
Clemens Ladischd1db38c2010-01-18 15:44:04 +010099};
100
Clemens Ladischb532d6b2011-01-10 16:23:57 +0100101static void wm8776_write_spi(struct oxygen *chip,
102 unsigned int reg, unsigned int value)
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100103{
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100104 oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
105 OXYGEN_SPI_DATA_LENGTH_2 |
106 OXYGEN_SPI_CLOCK_160 |
107 (1 << OXYGEN_SPI_CODEC_SHIFT) |
108 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
109 (reg << 9) | value);
Clemens Ladischb532d6b2011-01-10 16:23:57 +0100110}
111
112static void wm8776_write_i2c(struct oxygen *chip,
113 unsigned int reg, unsigned int value)
114{
115 oxygen_write_i2c(chip, I2C_DEVICE_WM8776,
116 (reg << 1) | (value >> 8), value);
117}
118
119static void wm8776_write(struct oxygen *chip,
120 unsigned int reg, unsigned int value)
121{
122 struct xonar_wm87x6 *data = chip->model_data;
123
124 if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
125 OXYGEN_FUNCTION_SPI)
126 wm8776_write_spi(chip, reg, value);
127 else
128 wm8776_write_i2c(chip, reg, value);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100129 if (reg < ARRAY_SIZE(data->wm8776_regs)) {
Clemens Ladischfaf4eb22010-03-03 09:16:18 +0100130 if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100131 value &= ~WM8776_UPDATE;
132 data->wm8776_regs[reg] = value;
133 }
134}
135
136static void wm8776_write_cached(struct oxygen *chip,
137 unsigned int reg, unsigned int value)
138{
139 struct xonar_wm87x6 *data = chip->model_data;
140
141 if (reg >= ARRAY_SIZE(data->wm8776_regs) ||
142 value != data->wm8776_regs[reg])
143 wm8776_write(chip, reg, value);
144}
145
146static void wm8766_write(struct oxygen *chip,
147 unsigned int reg, unsigned int value)
148{
149 struct xonar_wm87x6 *data = chip->model_data;
150
151 oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
152 OXYGEN_SPI_DATA_LENGTH_2 |
153 OXYGEN_SPI_CLOCK_160 |
154 (0 << OXYGEN_SPI_CODEC_SHIFT) |
155 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
156 (reg << 9) | value);
Clemens Ladischda0dab52010-09-09 12:18:35 +0200157 if (reg < ARRAY_SIZE(data->wm8766_regs)) {
158 if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
159 (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
160 value &= ~WM8766_UPDATE;
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100161 data->wm8766_regs[reg] = value;
Clemens Ladischda0dab52010-09-09 12:18:35 +0200162 }
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100163}
164
165static void wm8766_write_cached(struct oxygen *chip,
166 unsigned int reg, unsigned int value)
167{
168 struct xonar_wm87x6 *data = chip->model_data;
169
170 if (reg >= ARRAY_SIZE(data->wm8766_regs) ||
Clemens Ladischda0dab52010-09-09 12:18:35 +0200171 value != data->wm8766_regs[reg])
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100172 wm8766_write(chip, reg, value);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100173}
174
175static void wm8776_registers_init(struct oxygen *chip)
176{
177 struct xonar_wm87x6 *data = chip->model_data;
178
179 wm8776_write(chip, WM8776_RESET, 0);
180 wm8776_write(chip, WM8776_DACCTRL1, WM8776_DZCEN |
181 WM8776_PL_LEFT_LEFT | WM8776_PL_RIGHT_RIGHT);
182 wm8776_write(chip, WM8776_DACMUTE, chip->dac_mute ? WM8776_DMUTE : 0);
183 wm8776_write(chip, WM8776_DACIFCTRL,
184 WM8776_DACFMT_LJUST | WM8776_DACWL_24);
185 wm8776_write(chip, WM8776_ADCIFCTRL,
186 data->wm8776_regs[WM8776_ADCIFCTRL]);
187 wm8776_write(chip, WM8776_MSTRCTRL, data->wm8776_regs[WM8776_MSTRCTRL]);
188 wm8776_write(chip, WM8776_PWRDOWN, data->wm8776_regs[WM8776_PWRDOWN]);
189 wm8776_write(chip, WM8776_HPLVOL, data->wm8776_regs[WM8776_HPLVOL]);
190 wm8776_write(chip, WM8776_HPRVOL, data->wm8776_regs[WM8776_HPRVOL] |
191 WM8776_UPDATE);
192 wm8776_write(chip, WM8776_ADCLVOL, data->wm8776_regs[WM8776_ADCLVOL]);
193 wm8776_write(chip, WM8776_ADCRVOL, data->wm8776_regs[WM8776_ADCRVOL]);
194 wm8776_write(chip, WM8776_ADCMUX, data->wm8776_regs[WM8776_ADCMUX]);
195 wm8776_write(chip, WM8776_DACLVOL, chip->dac_volume[0]);
196 wm8776_write(chip, WM8776_DACRVOL, chip->dac_volume[1] | WM8776_UPDATE);
197}
198
199static void wm8766_registers_init(struct oxygen *chip)
200{
Clemens Ladisch84cf83a2010-09-09 12:23:06 +0200201 struct xonar_wm87x6 *data = chip->model_data;
202
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100203 wm8766_write(chip, WM8766_RESET, 0);
Clemens Ladisch84cf83a2010-09-09 12:23:06 +0200204 wm8766_write(chip, WM8766_DAC_CTRL, data->wm8766_regs[WM8766_DAC_CTRL]);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100205 wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24);
206 wm8766_write(chip, WM8766_DAC_CTRL2,
207 WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0));
208 wm8766_write(chip, WM8766_LDA1, chip->dac_volume[2]);
209 wm8766_write(chip, WM8766_RDA1, chip->dac_volume[3]);
210 wm8766_write(chip, WM8766_LDA2, chip->dac_volume[4]);
211 wm8766_write(chip, WM8766_RDA2, chip->dac_volume[5]);
212 wm8766_write(chip, WM8766_LDA3, chip->dac_volume[6]);
213 wm8766_write(chip, WM8766_RDA3, chip->dac_volume[7] | WM8766_UPDATE);
214}
215
216static void wm8776_init(struct oxygen *chip)
217{
218 struct xonar_wm87x6 *data = chip->model_data;
219
220 data->wm8776_regs[WM8776_HPLVOL] = (0x79 - 60) | WM8776_HPZCEN;
221 data->wm8776_regs[WM8776_HPRVOL] = (0x79 - 60) | WM8776_HPZCEN;
222 data->wm8776_regs[WM8776_ADCIFCTRL] =
223 WM8776_ADCFMT_LJUST | WM8776_ADCWL_24 | WM8776_ADCMCLK;
224 data->wm8776_regs[WM8776_MSTRCTRL] =
225 WM8776_ADCRATE_256 | WM8776_DACRATE_256;
226 data->wm8776_regs[WM8776_PWRDOWN] = WM8776_HPPD;
227 data->wm8776_regs[WM8776_ADCLVOL] = 0xa5 | WM8776_ZCA;
228 data->wm8776_regs[WM8776_ADCRVOL] = 0xa5 | WM8776_ZCA;
229 data->wm8776_regs[WM8776_ADCMUX] = 0x001;
230 wm8776_registers_init(chip);
231}
232
Clemens Ladisch84cf83a2010-09-09 12:23:06 +0200233static void wm8766_init(struct oxygen *chip)
Clemens Ladisch435feac2010-09-09 12:20:29 +0200234{
235 struct xonar_wm87x6 *data = chip->model_data;
Clemens Ladisch435feac2010-09-09 12:20:29 +0200236
Clemens Ladisch84cf83a2010-09-09 12:23:06 +0200237 data->wm8766_regs[WM8766_DAC_CTRL] =
238 WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT;
239 wm8766_registers_init(chip);
240}
241
242static void xonar_ds_handle_hp_jack(struct oxygen *chip)
243{
244 struct xonar_wm87x6 *data = chip->model_data;
245 bool hp_plugged;
246 unsigned int reg;
247
248 mutex_lock(&chip->mutex);
249
250 hp_plugged = !(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
251 GPIO_DS_HP_DETECT);
252
253 oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
254 hp_plugged ? 0 : GPIO_DS_OUTPUT_FRONTLR,
255 GPIO_DS_OUTPUT_FRONTLR);
256
257 reg = data->wm8766_regs[WM8766_DAC_CTRL] & ~WM8766_MUTEALL;
258 if (hp_plugged)
259 reg |= WM8766_MUTEALL;
260 wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
261
262 snd_jack_report(data->hp_jack, hp_plugged ? SND_JACK_HEADPHONE : 0);
263
264 mutex_unlock(&chip->mutex);
Clemens Ladisch435feac2010-09-09 12:20:29 +0200265}
266
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100267static void xonar_ds_init(struct oxygen *chip)
268{
269 struct xonar_wm87x6 *data = chip->model_data;
270
271 data->generic.anti_pop_delay = 300;
272 data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE;
273
274 wm8776_init(chip);
Clemens Ladisch84cf83a2010-09-09 12:23:06 +0200275 wm8766_init(chip);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100276
Clemens Ladisch84cf83a2010-09-09 12:23:06 +0200277 oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
278 GPIO_DS_INPUT_ROUTE | GPIO_DS_OUTPUT_FRONTLR);
279 oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
280 GPIO_DS_HP_DETECT);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100281 oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE);
282 oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT);
283 chip->interrupt_mask |= OXYGEN_INT_GPIO;
284
285 xonar_enable_output(chip);
286
Clemens Ladisch435feac2010-09-09 12:20:29 +0200287 snd_jack_new(chip->card, "Headphone",
288 SND_JACK_HEADPHONE, &data->hp_jack);
Clemens Ladisch84cf83a2010-09-09 12:23:06 +0200289 xonar_ds_handle_hp_jack(chip);
Clemens Ladisch435feac2010-09-09 12:20:29 +0200290
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100291 snd_component_add(chip->card, "WM8776");
292 snd_component_add(chip->card, "WM8766");
293}
294
Clemens Ladischb532d6b2011-01-10 16:23:57 +0100295static void xonar_hdav_slim_init(struct oxygen *chip)
296{
297 struct xonar_wm87x6 *data = chip->model_data;
298
299 data->generic.anti_pop_delay = 300;
300 data->generic.output_enable_bit = GPIO_SLIM_OUTPUT_ENABLE;
301
302 wm8776_init(chip);
303
304 oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
305 GPIO_SLIM_HDMI_DISABLE |
306 GPIO_SLIM_FIRMWARE_CLK |
307 GPIO_SLIM_FIRMWARE_DATA);
308
309 xonar_hdmi_init(chip, &data->hdmi);
310 xonar_enable_output(chip);
311
312 snd_component_add(chip->card, "WM8776");
313}
314
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100315static void xonar_ds_cleanup(struct oxygen *chip)
316{
317 xonar_disable_output(chip);
Clemens Ladisch4c25b932010-09-07 13:37:10 +0200318 wm8776_write(chip, WM8776_RESET, 0);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100319}
320
Clemens Ladischb532d6b2011-01-10 16:23:57 +0100321static void xonar_hdav_slim_cleanup(struct oxygen *chip)
322{
323 xonar_hdmi_cleanup(chip);
324 xonar_disable_output(chip);
325 wm8776_write(chip, WM8776_RESET, 0);
326 msleep(2);
327}
328
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100329static void xonar_ds_suspend(struct oxygen *chip)
330{
331 xonar_ds_cleanup(chip);
332}
333
Clemens Ladischb532d6b2011-01-10 16:23:57 +0100334static void xonar_hdav_slim_suspend(struct oxygen *chip)
335{
336 xonar_hdav_slim_cleanup(chip);
337}
338
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100339static void xonar_ds_resume(struct oxygen *chip)
340{
341 wm8776_registers_init(chip);
342 wm8766_registers_init(chip);
343 xonar_enable_output(chip);
Clemens Ladisch84cf83a2010-09-09 12:23:06 +0200344 xonar_ds_handle_hp_jack(chip);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100345}
346
Clemens Ladischb532d6b2011-01-10 16:23:57 +0100347static void xonar_hdav_slim_resume(struct oxygen *chip)
348{
349 struct xonar_wm87x6 *data = chip->model_data;
350
351 wm8776_registers_init(chip);
352 xonar_hdmi_resume(chip, &data->hdmi);
353 xonar_enable_output(chip);
354}
355
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100356static void wm8776_adc_hardware_filter(unsigned int channel,
357 struct snd_pcm_hardware *hardware)
358{
359 if (channel == PCM_A) {
360 hardware->rates = SNDRV_PCM_RATE_32000 |
361 SNDRV_PCM_RATE_44100 |
362 SNDRV_PCM_RATE_48000 |
363 SNDRV_PCM_RATE_64000 |
364 SNDRV_PCM_RATE_88200 |
365 SNDRV_PCM_RATE_96000;
366 hardware->rate_max = 96000;
367 }
368}
369
Clemens Ladischb532d6b2011-01-10 16:23:57 +0100370static void xonar_hdav_slim_hardware_filter(unsigned int channel,
371 struct snd_pcm_hardware *hardware)
372{
373 wm8776_adc_hardware_filter(channel, hardware);
374 xonar_hdmi_pcm_hardware_filter(channel, hardware);
375}
376
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100377static void set_wm87x6_dac_params(struct oxygen *chip,
378 struct snd_pcm_hw_params *params)
379{
380}
381
382static void set_wm8776_adc_params(struct oxygen *chip,
383 struct snd_pcm_hw_params *params)
384{
385 u16 reg;
386
387 reg = WM8776_ADCRATE_256 | WM8776_DACRATE_256;
388 if (params_rate(params) > 48000)
389 reg |= WM8776_ADCOSR;
390 wm8776_write_cached(chip, WM8776_MSTRCTRL, reg);
391}
392
Clemens Ladischb532d6b2011-01-10 16:23:57 +0100393static void set_hdav_slim_dac_params(struct oxygen *chip,
394 struct snd_pcm_hw_params *params)
395{
396 struct xonar_wm87x6 *data = chip->model_data;
397
398 xonar_set_hdmi_params(chip, &data->hdmi, params);
399}
400
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100401static void update_wm8776_volume(struct oxygen *chip)
402{
403 struct xonar_wm87x6 *data = chip->model_data;
404 u8 to_change;
405
406 if (chip->dac_volume[0] == chip->dac_volume[1]) {
407 if (chip->dac_volume[0] != data->wm8776_regs[WM8776_DACLVOL] ||
408 chip->dac_volume[1] != data->wm8776_regs[WM8776_DACRVOL]) {
409 wm8776_write(chip, WM8776_DACMASTER,
410 chip->dac_volume[0] | WM8776_UPDATE);
411 data->wm8776_regs[WM8776_DACLVOL] = chip->dac_volume[0];
412 data->wm8776_regs[WM8776_DACRVOL] = chip->dac_volume[0];
413 }
414 } else {
415 to_change = (chip->dac_volume[0] !=
416 data->wm8776_regs[WM8776_DACLVOL]) << 0;
417 to_change |= (chip->dac_volume[1] !=
418 data->wm8776_regs[WM8776_DACLVOL]) << 1;
419 if (to_change & 1)
420 wm8776_write(chip, WM8776_DACLVOL, chip->dac_volume[0] |
421 ((to_change & 2) ? 0 : WM8776_UPDATE));
422 if (to_change & 2)
423 wm8776_write(chip, WM8776_DACRVOL,
424 chip->dac_volume[1] | WM8776_UPDATE);
425 }
426}
427
428static void update_wm87x6_volume(struct oxygen *chip)
429{
430 static const u8 wm8766_regs[6] = {
431 WM8766_LDA1, WM8766_RDA1,
432 WM8766_LDA2, WM8766_RDA2,
433 WM8766_LDA3, WM8766_RDA3,
434 };
435 struct xonar_wm87x6 *data = chip->model_data;
436 unsigned int i;
437 u8 to_change;
438
439 update_wm8776_volume(chip);
440 if (chip->dac_volume[2] == chip->dac_volume[3] &&
441 chip->dac_volume[2] == chip->dac_volume[4] &&
442 chip->dac_volume[2] == chip->dac_volume[5] &&
443 chip->dac_volume[2] == chip->dac_volume[6] &&
444 chip->dac_volume[2] == chip->dac_volume[7]) {
445 to_change = 0;
446 for (i = 0; i < 6; ++i)
447 if (chip->dac_volume[2] !=
448 data->wm8766_regs[wm8766_regs[i]])
449 to_change = 1;
450 if (to_change) {
451 wm8766_write(chip, WM8766_MASTDA,
452 chip->dac_volume[2] | WM8766_UPDATE);
453 for (i = 0; i < 6; ++i)
454 data->wm8766_regs[wm8766_regs[i]] =
455 chip->dac_volume[2];
456 }
457 } else {
458 to_change = 0;
459 for (i = 0; i < 6; ++i)
460 to_change |= (chip->dac_volume[2 + i] !=
461 data->wm8766_regs[wm8766_regs[i]]) << i;
462 for (i = 0; i < 6; ++i)
463 if (to_change & (1 << i))
464 wm8766_write(chip, wm8766_regs[i],
465 chip->dac_volume[2 + i] |
466 ((to_change & (0x3e << i))
467 ? 0 : WM8766_UPDATE));
468 }
469}
470
471static void update_wm8776_mute(struct oxygen *chip)
472{
473 wm8776_write_cached(chip, WM8776_DACMUTE,
474 chip->dac_mute ? WM8776_DMUTE : 0);
475}
476
477static void update_wm87x6_mute(struct oxygen *chip)
478{
479 update_wm8776_mute(chip);
480 wm8766_write_cached(chip, WM8766_DAC_CTRL2, WM8766_ZCD |
481 (chip->dac_mute ? WM8766_DMUTE_MASK : 0));
482}
483
Clemens Ladisch2dbf0ea2010-09-09 12:24:35 +0200484static void update_wm8766_center_lfe_mix(struct oxygen *chip, bool mixed)
485{
486 struct xonar_wm87x6 *data = chip->model_data;
487 unsigned int reg;
488
489 /*
490 * The WM8766 can mix left and right channels, but this setting
491 * applies to all three stereo pairs.
492 */
493 reg = data->wm8766_regs[WM8766_DAC_CTRL] &
494 ~(WM8766_PL_LEFT_MASK | WM8766_PL_RIGHT_MASK);
495 if (mixed)
496 reg |= WM8766_PL_LEFT_LRMIX | WM8766_PL_RIGHT_LRMIX;
497 else
498 reg |= WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT;
499 wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
500}
501
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100502static void xonar_ds_gpio_changed(struct oxygen *chip)
503{
Clemens Ladisch84cf83a2010-09-09 12:23:06 +0200504 xonar_ds_handle_hp_jack(chip);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100505}
506
507static int wm8776_bit_switch_get(struct snd_kcontrol *ctl,
508 struct snd_ctl_elem_value *value)
509{
510 struct oxygen *chip = ctl->private_data;
511 struct xonar_wm87x6 *data = chip->model_data;
512 u16 bit = ctl->private_value & 0xffff;
513 unsigned int reg_index = (ctl->private_value >> 16) & 0xff;
514 bool invert = (ctl->private_value >> 24) & 1;
515
516 value->value.integer.value[0] =
517 ((data->wm8776_regs[reg_index] & bit) != 0) ^ invert;
518 return 0;
519}
520
521static int wm8776_bit_switch_put(struct snd_kcontrol *ctl,
522 struct snd_ctl_elem_value *value)
523{
524 struct oxygen *chip = ctl->private_data;
525 struct xonar_wm87x6 *data = chip->model_data;
526 u16 bit = ctl->private_value & 0xffff;
527 u16 reg_value;
528 unsigned int reg_index = (ctl->private_value >> 16) & 0xff;
529 bool invert = (ctl->private_value >> 24) & 1;
530 int changed;
531
532 mutex_lock(&chip->mutex);
533 reg_value = data->wm8776_regs[reg_index] & ~bit;
534 if (value->value.integer.value[0] ^ invert)
535 reg_value |= bit;
536 changed = reg_value != data->wm8776_regs[reg_index];
537 if (changed)
538 wm8776_write(chip, reg_index, reg_value);
539 mutex_unlock(&chip->mutex);
540 return changed;
541}
542
543static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
544 struct snd_ctl_elem_info *info)
545{
546 static const char *const hld[16] = {
547 "0 ms", "2.67 ms", "5.33 ms", "10.6 ms",
548 "21.3 ms", "42.7 ms", "85.3 ms", "171 ms",
549 "341 ms", "683 ms", "1.37 s", "2.73 s",
550 "5.46 s", "10.9 s", "21.8 s", "43.7 s",
551 };
552 static const char *const atk_lim[11] = {
553 "0.25 ms", "0.5 ms", "1 ms", "2 ms",
554 "4 ms", "8 ms", "16 ms", "32 ms",
555 "64 ms", "128 ms", "256 ms",
556 };
557 static const char *const atk_alc[11] = {
558 "8.40 ms", "16.8 ms", "33.6 ms", "67.2 ms",
559 "134 ms", "269 ms", "538 ms", "1.08 s",
560 "2.15 s", "4.3 s", "8.6 s",
561 };
562 static const char *const dcy_lim[11] = {
563 "1.2 ms", "2.4 ms", "4.8 ms", "9.6 ms",
564 "19.2 ms", "38.4 ms", "76.8 ms", "154 ms",
565 "307 ms", "614 ms", "1.23 s",
566 };
567 static const char *const dcy_alc[11] = {
568 "33.5 ms", "67.0 ms", "134 ms", "268 ms",
569 "536 ms", "1.07 s", "2.14 s", "4.29 s",
570 "8.58 s", "17.2 s", "34.3 s",
571 };
572 static const char *const tranwin[8] = {
573 "0 us", "62.5 us", "125 us", "250 us",
574 "500 us", "1 ms", "2 ms", "4 ms",
575 };
576 u8 max;
577 const char *const *names;
578
579 max = (ctl->private_value >> 12) & 0xf;
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100580 switch ((ctl->private_value >> 24) & 0x1f) {
581 case WM8776_ALCCTRL2:
582 names = hld;
583 break;
584 case WM8776_ALCCTRL3:
585 if (((ctl->private_value >> 20) & 0xf) == 0) {
586 if (ctl->private_value & LC_CONTROL_LIMITER)
587 names = atk_lim;
588 else
589 names = atk_alc;
590 } else {
591 if (ctl->private_value & LC_CONTROL_LIMITER)
592 names = dcy_lim;
593 else
594 names = dcy_alc;
595 }
596 break;
597 case WM8776_LIMITER:
598 names = tranwin;
599 break;
600 default:
601 return -ENXIO;
602 }
Clemens Ladisch96007322011-01-10 16:25:44 +0100603 return snd_ctl_enum_info(info, 1, max + 1, names);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100604}
605
606static int wm8776_field_volume_info(struct snd_kcontrol *ctl,
607 struct snd_ctl_elem_info *info)
608{
609 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
610 info->count = 1;
611 info->value.integer.min = (ctl->private_value >> 8) & 0xf;
612 info->value.integer.max = (ctl->private_value >> 12) & 0xf;
613 return 0;
614}
615
616static void wm8776_field_set_from_ctl(struct snd_kcontrol *ctl)
617{
618 struct oxygen *chip = ctl->private_data;
619 struct xonar_wm87x6 *data = chip->model_data;
620 unsigned int value, reg_index, mode;
621 u8 min, max, shift;
622 u16 mask, reg_value;
623 bool invert;
624
625 if ((data->wm8776_regs[WM8776_ALCCTRL1] & WM8776_LCSEL_MASK) ==
626 WM8776_LCSEL_LIMITER)
627 mode = LC_CONTROL_LIMITER;
628 else
629 mode = LC_CONTROL_ALC;
630 if (!(ctl->private_value & mode))
631 return;
632
633 value = ctl->private_value & 0xf;
634 min = (ctl->private_value >> 8) & 0xf;
635 max = (ctl->private_value >> 12) & 0xf;
636 mask = (ctl->private_value >> 16) & 0xf;
637 shift = (ctl->private_value >> 20) & 0xf;
638 reg_index = (ctl->private_value >> 24) & 0x1f;
639 invert = (ctl->private_value >> 29) & 0x1;
640
641 if (invert)
642 value = max - (value - min);
643 reg_value = data->wm8776_regs[reg_index];
644 reg_value &= ~(mask << shift);
645 reg_value |= value << shift;
646 wm8776_write_cached(chip, reg_index, reg_value);
647}
648
649static int wm8776_field_set(struct snd_kcontrol *ctl, unsigned int value)
650{
651 struct oxygen *chip = ctl->private_data;
652 u8 min, max;
653 int changed;
654
655 min = (ctl->private_value >> 8) & 0xf;
656 max = (ctl->private_value >> 12) & 0xf;
657 if (value < min || value > max)
658 return -EINVAL;
659 mutex_lock(&chip->mutex);
660 changed = value != (ctl->private_value & 0xf);
661 if (changed) {
662 ctl->private_value = (ctl->private_value & ~0xf) | value;
663 wm8776_field_set_from_ctl(ctl);
664 }
665 mutex_unlock(&chip->mutex);
666 return changed;
667}
668
669static int wm8776_field_enum_get(struct snd_kcontrol *ctl,
670 struct snd_ctl_elem_value *value)
671{
672 value->value.enumerated.item[0] = ctl->private_value & 0xf;
673 return 0;
674}
675
676static int wm8776_field_volume_get(struct snd_kcontrol *ctl,
677 struct snd_ctl_elem_value *value)
678{
679 value->value.integer.value[0] = ctl->private_value & 0xf;
680 return 0;
681}
682
683static int wm8776_field_enum_put(struct snd_kcontrol *ctl,
684 struct snd_ctl_elem_value *value)
685{
686 return wm8776_field_set(ctl, value->value.enumerated.item[0]);
687}
688
689static int wm8776_field_volume_put(struct snd_kcontrol *ctl,
690 struct snd_ctl_elem_value *value)
691{
692 return wm8776_field_set(ctl, value->value.integer.value[0]);
693}
694
695static int wm8776_hp_vol_info(struct snd_kcontrol *ctl,
696 struct snd_ctl_elem_info *info)
697{
698 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
699 info->count = 2;
700 info->value.integer.min = 0x79 - 60;
701 info->value.integer.max = 0x7f;
702 return 0;
703}
704
705static int wm8776_hp_vol_get(struct snd_kcontrol *ctl,
706 struct snd_ctl_elem_value *value)
707{
708 struct oxygen *chip = ctl->private_data;
709 struct xonar_wm87x6 *data = chip->model_data;
710
711 mutex_lock(&chip->mutex);
712 value->value.integer.value[0] =
713 data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK;
714 value->value.integer.value[1] =
715 data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK;
716 mutex_unlock(&chip->mutex);
717 return 0;
718}
719
720static int wm8776_hp_vol_put(struct snd_kcontrol *ctl,
721 struct snd_ctl_elem_value *value)
722{
723 struct oxygen *chip = ctl->private_data;
724 struct xonar_wm87x6 *data = chip->model_data;
725 u8 to_update;
726
727 mutex_lock(&chip->mutex);
728 to_update = (value->value.integer.value[0] !=
729 (data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK))
730 << 0;
731 to_update |= (value->value.integer.value[1] !=
732 (data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK))
733 << 1;
734 if (value->value.integer.value[0] == value->value.integer.value[1]) {
735 if (to_update) {
736 wm8776_write(chip, WM8776_HPMASTER,
737 value->value.integer.value[0] |
738 WM8776_HPZCEN | WM8776_UPDATE);
739 data->wm8776_regs[WM8776_HPLVOL] =
740 value->value.integer.value[0] | WM8776_HPZCEN;
741 data->wm8776_regs[WM8776_HPRVOL] =
742 value->value.integer.value[0] | WM8776_HPZCEN;
743 }
744 } else {
745 if (to_update & 1)
746 wm8776_write(chip, WM8776_HPLVOL,
747 value->value.integer.value[0] |
748 WM8776_HPZCEN |
749 ((to_update & 2) ? 0 : WM8776_UPDATE));
750 if (to_update & 2)
751 wm8776_write(chip, WM8776_HPRVOL,
752 value->value.integer.value[1] |
753 WM8776_HPZCEN | WM8776_UPDATE);
754 }
755 mutex_unlock(&chip->mutex);
756 return to_update != 0;
757}
758
759static int wm8776_input_mux_get(struct snd_kcontrol *ctl,
760 struct snd_ctl_elem_value *value)
761{
762 struct oxygen *chip = ctl->private_data;
763 struct xonar_wm87x6 *data = chip->model_data;
764 unsigned int mux_bit = ctl->private_value;
765
766 value->value.integer.value[0] =
767 !!(data->wm8776_regs[WM8776_ADCMUX] & mux_bit);
768 return 0;
769}
770
771static int wm8776_input_mux_put(struct snd_kcontrol *ctl,
772 struct snd_ctl_elem_value *value)
773{
774 struct oxygen *chip = ctl->private_data;
775 struct xonar_wm87x6 *data = chip->model_data;
Clemens Ladischfe6ce802010-09-07 13:38:49 +0200776 struct snd_kcontrol *other_ctl;
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100777 unsigned int mux_bit = ctl->private_value;
778 u16 reg;
779 int changed;
780
781 mutex_lock(&chip->mutex);
782 reg = data->wm8776_regs[WM8776_ADCMUX];
783 if (value->value.integer.value[0]) {
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100784 reg |= mux_bit;
Clemens Ladischfe6ce802010-09-07 13:38:49 +0200785 /* line-in and mic-in are exclusive */
786 mux_bit ^= 3;
787 if (reg & mux_bit) {
788 reg &= ~mux_bit;
789 if (mux_bit == 1)
790 other_ctl = data->line_adcmux_control;
791 else
792 other_ctl = data->mic_adcmux_control;
793 snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
794 &other_ctl->id);
795 }
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100796 } else
797 reg &= ~mux_bit;
798 changed = reg != data->wm8776_regs[WM8776_ADCMUX];
799 if (changed) {
800 oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
801 reg & 1 ? GPIO_DS_INPUT_ROUTE : 0,
802 GPIO_DS_INPUT_ROUTE);
803 wm8776_write(chip, WM8776_ADCMUX, reg);
804 }
805 mutex_unlock(&chip->mutex);
806 return changed;
807}
808
809static int wm8776_input_vol_info(struct snd_kcontrol *ctl,
810 struct snd_ctl_elem_info *info)
811{
812 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
813 info->count = 2;
814 info->value.integer.min = 0xa5;
815 info->value.integer.max = 0xff;
816 return 0;
817}
818
819static int wm8776_input_vol_get(struct snd_kcontrol *ctl,
820 struct snd_ctl_elem_value *value)
821{
822 struct oxygen *chip = ctl->private_data;
823 struct xonar_wm87x6 *data = chip->model_data;
824
825 mutex_lock(&chip->mutex);
826 value->value.integer.value[0] =
827 data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK;
828 value->value.integer.value[1] =
829 data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK;
830 mutex_unlock(&chip->mutex);
831 return 0;
832}
833
834static int wm8776_input_vol_put(struct snd_kcontrol *ctl,
835 struct snd_ctl_elem_value *value)
836{
837 struct oxygen *chip = ctl->private_data;
838 struct xonar_wm87x6 *data = chip->model_data;
839 int changed = 0;
840
841 mutex_lock(&chip->mutex);
842 changed = (value->value.integer.value[0] !=
843 (data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK)) ||
844 (value->value.integer.value[1] !=
845 (data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK));
846 wm8776_write_cached(chip, WM8776_ADCLVOL,
847 value->value.integer.value[0] | WM8776_ZCA);
848 wm8776_write_cached(chip, WM8776_ADCRVOL,
849 value->value.integer.value[1] | WM8776_ZCA);
850 mutex_unlock(&chip->mutex);
851 return changed;
852}
853
854static int wm8776_level_control_info(struct snd_kcontrol *ctl,
855 struct snd_ctl_elem_info *info)
856{
857 static const char *const names[3] = {
858 "None", "Peak Limiter", "Automatic Level Control"
859 };
Clemens Ladisch96007322011-01-10 16:25:44 +0100860
861 return snd_ctl_enum_info(info, 1, 3, names);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100862}
863
864static int wm8776_level_control_get(struct snd_kcontrol *ctl,
865 struct snd_ctl_elem_value *value)
866{
867 struct oxygen *chip = ctl->private_data;
868 struct xonar_wm87x6 *data = chip->model_data;
869
870 if (!(data->wm8776_regs[WM8776_ALCCTRL2] & WM8776_LCEN))
871 value->value.enumerated.item[0] = 0;
872 else if ((data->wm8776_regs[WM8776_ALCCTRL1] & WM8776_LCSEL_MASK) ==
873 WM8776_LCSEL_LIMITER)
874 value->value.enumerated.item[0] = 1;
875 else
876 value->value.enumerated.item[0] = 2;
877 return 0;
878}
879
880static void activate_control(struct oxygen *chip,
881 struct snd_kcontrol *ctl, unsigned int mode)
882{
883 unsigned int access;
884
885 if (ctl->private_value & mode)
886 access = 0;
887 else
888 access = SNDRV_CTL_ELEM_ACCESS_INACTIVE;
889 if ((ctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) != access) {
890 ctl->vd[0].access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
891 snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
892 }
893}
894
895static int wm8776_level_control_put(struct snd_kcontrol *ctl,
896 struct snd_ctl_elem_value *value)
897{
898 struct oxygen *chip = ctl->private_data;
899 struct xonar_wm87x6 *data = chip->model_data;
900 unsigned int mode = 0, i;
901 u16 ctrl1, ctrl2;
902 int changed;
903
904 if (value->value.enumerated.item[0] >= 3)
905 return -EINVAL;
906 mutex_lock(&chip->mutex);
907 changed = value->value.enumerated.item[0] != ctl->private_value;
908 if (changed) {
909 ctl->private_value = value->value.enumerated.item[0];
910 ctrl1 = data->wm8776_regs[WM8776_ALCCTRL1];
911 ctrl2 = data->wm8776_regs[WM8776_ALCCTRL2];
912 switch (value->value.enumerated.item[0]) {
913 default:
914 wm8776_write_cached(chip, WM8776_ALCCTRL2,
915 ctrl2 & ~WM8776_LCEN);
916 break;
917 case 1:
918 wm8776_write_cached(chip, WM8776_ALCCTRL1,
919 (ctrl1 & ~WM8776_LCSEL_MASK) |
920 WM8776_LCSEL_LIMITER);
921 wm8776_write_cached(chip, WM8776_ALCCTRL2,
922 ctrl2 | WM8776_LCEN);
923 mode = LC_CONTROL_LIMITER;
924 break;
925 case 2:
926 wm8776_write_cached(chip, WM8776_ALCCTRL1,
927 (ctrl1 & ~WM8776_LCSEL_MASK) |
928 WM8776_LCSEL_ALC_STEREO);
929 wm8776_write_cached(chip, WM8776_ALCCTRL2,
930 ctrl2 | WM8776_LCEN);
931 mode = LC_CONTROL_ALC;
932 break;
933 }
934 for (i = 0; i < ARRAY_SIZE(data->lc_controls); ++i)
935 activate_control(chip, data->lc_controls[i], mode);
936 }
937 mutex_unlock(&chip->mutex);
938 return changed;
939}
940
941static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
942{
943 static const char *const names[2] = {
944 "None", "High-pass Filter"
945 };
946
Clemens Ladisch96007322011-01-10 16:25:44 +0100947 return snd_ctl_enum_info(info, 1, 2, names);
Clemens Ladischd1db38c2010-01-18 15:44:04 +0100948}
949
950static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
951{
952 struct oxygen *chip = ctl->private_data;
953 struct xonar_wm87x6 *data = chip->model_data;
954
955 value->value.enumerated.item[0] =
956 !(data->wm8776_regs[WM8776_ADCIFCTRL] & WM8776_ADCHPD);
957 return 0;
958}
959
960static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
961{
962 struct oxygen *chip = ctl->private_data;
963 struct xonar_wm87x6 *data = chip->model_data;
964 unsigned int reg;
965 int changed;
966
967 mutex_lock(&chip->mutex);
968 reg = data->wm8776_regs[WM8776_ADCIFCTRL] & ~WM8776_ADCHPD;
969 if (!value->value.enumerated.item[0])
970 reg |= WM8776_ADCHPD;
971 changed = reg != data->wm8776_regs[WM8776_ADCIFCTRL];
972 if (changed)
973 wm8776_write(chip, WM8776_ADCIFCTRL, reg);
974 mutex_unlock(&chip->mutex);
975 return changed;
976}
977
978#define WM8776_BIT_SWITCH(xname, reg, bit, invert, flags) { \
979 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
980 .name = xname, \
981 .info = snd_ctl_boolean_mono_info, \
982 .get = wm8776_bit_switch_get, \
983 .put = wm8776_bit_switch_put, \
984 .private_value = ((reg) << 16) | (bit) | ((invert) << 24) | (flags), \
985}
986#define _WM8776_FIELD_CTL(xname, reg, shift, initval, min, max, mask, flags) \
987 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
988 .name = xname, \
989 .private_value = (initval) | ((min) << 8) | ((max) << 12) | \
990 ((mask) << 16) | ((shift) << 20) | ((reg) << 24) | (flags)
991#define WM8776_FIELD_CTL_ENUM(xname, reg, shift, init, min, max, mask, flags) {\
992 _WM8776_FIELD_CTL(xname " Capture Enum", \
993 reg, shift, init, min, max, mask, flags), \
994 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
995 SNDRV_CTL_ELEM_ACCESS_INACTIVE, \
996 .info = wm8776_field_enum_info, \
997 .get = wm8776_field_enum_get, \
998 .put = wm8776_field_enum_put, \
999}
1000#define WM8776_FIELD_CTL_VOLUME(a, b, c, d, e, f, g, h, tlv_p) { \
1001 _WM8776_FIELD_CTL(a " Capture Volume", b, c, d, e, f, g, h), \
1002 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
1003 SNDRV_CTL_ELEM_ACCESS_INACTIVE | \
1004 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
1005 .info = wm8776_field_volume_info, \
1006 .get = wm8776_field_volume_get, \
1007 .put = wm8776_field_volume_put, \
1008 .tlv = { .p = tlv_p }, \
1009}
1010
1011static const DECLARE_TLV_DB_SCALE(wm87x6_dac_db_scale, -6000, 50, 0);
1012static const DECLARE_TLV_DB_SCALE(wm8776_adc_db_scale, -2100, 50, 0);
1013static const DECLARE_TLV_DB_SCALE(wm8776_hp_db_scale, -6000, 100, 0);
1014static const DECLARE_TLV_DB_SCALE(wm8776_lct_db_scale, -1600, 100, 0);
1015static const DECLARE_TLV_DB_SCALE(wm8776_maxgain_db_scale, 0, 400, 0);
1016static const DECLARE_TLV_DB_SCALE(wm8776_ngth_db_scale, -7800, 600, 0);
1017static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_lim_db_scale, -1200, 100, 0);
1018static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_alc_db_scale, -2100, 400, 0);
1019
1020static const struct snd_kcontrol_new ds_controls[] = {
1021 {
1022 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1023 .name = "Headphone Playback Volume",
1024 .info = wm8776_hp_vol_info,
1025 .get = wm8776_hp_vol_get,
1026 .put = wm8776_hp_vol_put,
1027 .tlv = { .p = wm8776_hp_db_scale },
1028 },
1029 WM8776_BIT_SWITCH("Headphone Playback Switch",
1030 WM8776_PWRDOWN, WM8776_HPPD, 1, 0),
1031 {
1032 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1033 .name = "Input Capture Volume",
1034 .info = wm8776_input_vol_info,
1035 .get = wm8776_input_vol_get,
1036 .put = wm8776_input_vol_put,
1037 .tlv = { .p = wm8776_adc_db_scale },
1038 },
1039 {
1040 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1041 .name = "Line Capture Switch",
1042 .info = snd_ctl_boolean_mono_info,
1043 .get = wm8776_input_mux_get,
1044 .put = wm8776_input_mux_put,
1045 .private_value = 1 << 0,
1046 },
1047 {
1048 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1049 .name = "Mic Capture Switch",
1050 .info = snd_ctl_boolean_mono_info,
1051 .get = wm8776_input_mux_get,
1052 .put = wm8776_input_mux_put,
1053 .private_value = 1 << 1,
1054 },
Clemens Ladisch9bac84e2010-09-09 12:19:21 +02001055 WM8776_BIT_SWITCH("Front Mic Capture Switch",
1056 WM8776_ADCMUX, 1 << 2, 0, 0),
1057 WM8776_BIT_SWITCH("Aux Capture Switch",
1058 WM8776_ADCMUX, 1 << 3, 0, 0),
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001059 {
1060 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1061 .name = "ADC Filter Capture Enum",
1062 .info = hpf_info,
1063 .get = hpf_get,
1064 .put = hpf_put,
1065 },
1066 {
1067 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1068 .name = "Level Control Capture Enum",
1069 .info = wm8776_level_control_info,
1070 .get = wm8776_level_control_get,
1071 .put = wm8776_level_control_put,
1072 .private_value = 0,
1073 },
1074};
Clemens Ladischb532d6b2011-01-10 16:23:57 +01001075static const struct snd_kcontrol_new hdav_slim_controls[] = {
1076 {
1077 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1078 .name = "HDMI Playback Switch",
1079 .info = snd_ctl_boolean_mono_info,
1080 .get = xonar_gpio_bit_switch_get,
1081 .put = xonar_gpio_bit_switch_put,
1082 .private_value = GPIO_SLIM_HDMI_DISABLE | XONAR_GPIO_BIT_INVERT,
1083 },
1084 {
1085 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1086 .name = "Headphone Playback Volume",
1087 .info = wm8776_hp_vol_info,
1088 .get = wm8776_hp_vol_get,
1089 .put = wm8776_hp_vol_put,
1090 .tlv = { .p = wm8776_hp_db_scale },
1091 },
1092 WM8776_BIT_SWITCH("Headphone Playback Switch",
1093 WM8776_PWRDOWN, WM8776_HPPD, 1, 0),
1094 {
1095 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1096 .name = "Input Capture Volume",
1097 .info = wm8776_input_vol_info,
1098 .get = wm8776_input_vol_get,
1099 .put = wm8776_input_vol_put,
1100 .tlv = { .p = wm8776_adc_db_scale },
1101 },
1102 WM8776_BIT_SWITCH("Mic Capture Switch",
1103 WM8776_ADCMUX, 1 << 0, 0, 0),
1104 WM8776_BIT_SWITCH("Aux Capture Switch",
1105 WM8776_ADCMUX, 1 << 1, 0, 0),
1106 {
1107 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1108 .name = "ADC Filter Capture Enum",
1109 .info = hpf_info,
1110 .get = hpf_get,
1111 .put = hpf_put,
1112 },
1113 {
1114 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1115 .name = "Level Control Capture Enum",
1116 .info = wm8776_level_control_info,
1117 .get = wm8776_level_control_get,
1118 .put = wm8776_level_control_put,
1119 .private_value = 0,
1120 },
1121};
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001122static const struct snd_kcontrol_new lc_controls[] = {
1123 WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
1124 WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
1125 LC_CONTROL_LIMITER, wm8776_lct_db_scale),
1126 WM8776_FIELD_CTL_ENUM("Limiter Attack Time",
1127 WM8776_ALCCTRL3, 0, 2, 0, 10, 0xf,
1128 LC_CONTROL_LIMITER),
1129 WM8776_FIELD_CTL_ENUM("Limiter Decay Time",
1130 WM8776_ALCCTRL3, 4, 3, 0, 10, 0xf,
1131 LC_CONTROL_LIMITER),
1132 WM8776_FIELD_CTL_ENUM("Limiter Transient Window",
1133 WM8776_LIMITER, 4, 2, 0, 7, 0x7,
1134 LC_CONTROL_LIMITER),
1135 WM8776_FIELD_CTL_VOLUME("Limiter Maximum Attenuation",
1136 WM8776_LIMITER, 0, 6, 3, 12, 0xf,
1137 LC_CONTROL_LIMITER,
1138 wm8776_maxatten_lim_db_scale),
1139 WM8776_FIELD_CTL_VOLUME("ALC Target Level",
1140 WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
1141 LC_CONTROL_ALC, wm8776_lct_db_scale),
1142 WM8776_FIELD_CTL_ENUM("ALC Attack Time",
1143 WM8776_ALCCTRL3, 0, 2, 0, 10, 0xf,
1144 LC_CONTROL_ALC),
1145 WM8776_FIELD_CTL_ENUM("ALC Decay Time",
1146 WM8776_ALCCTRL3, 4, 3, 0, 10, 0xf,
1147 LC_CONTROL_ALC),
1148 WM8776_FIELD_CTL_VOLUME("ALC Maximum Gain",
1149 WM8776_ALCCTRL1, 4, 7, 1, 7, 0x7,
1150 LC_CONTROL_ALC, wm8776_maxgain_db_scale),
1151 WM8776_FIELD_CTL_VOLUME("ALC Maximum Attenuation",
1152 WM8776_LIMITER, 0, 10, 10, 15, 0xf,
1153 LC_CONTROL_ALC, wm8776_maxatten_alc_db_scale),
1154 WM8776_FIELD_CTL_ENUM("ALC Hold Time",
1155 WM8776_ALCCTRL2, 0, 0, 0, 15, 0xf,
1156 LC_CONTROL_ALC),
1157 WM8776_BIT_SWITCH("Noise Gate Capture Switch",
1158 WM8776_NOISEGATE, WM8776_NGAT, 0,
1159 LC_CONTROL_ALC),
1160 WM8776_FIELD_CTL_VOLUME("Noise Gate Threshold",
1161 WM8776_NOISEGATE, 2, 0, 0, 7, 0x7,
1162 LC_CONTROL_ALC, wm8776_ngth_db_scale),
1163};
1164
Clemens Ladischb532d6b2011-01-10 16:23:57 +01001165static int add_lc_controls(struct oxygen *chip)
1166{
1167 struct xonar_wm87x6 *data = chip->model_data;
1168 unsigned int i;
1169 struct snd_kcontrol *ctl;
1170 int err;
1171
1172 BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
1173 for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
1174 ctl = snd_ctl_new1(&lc_controls[i], chip);
1175 if (!ctl)
1176 return -ENOMEM;
1177 err = snd_ctl_add(chip->card, ctl);
1178 if (err < 0)
1179 return err;
1180 data->lc_controls[i] = ctl;
1181 }
1182 return 0;
1183}
1184
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001185static int xonar_ds_mixer_init(struct oxygen *chip)
1186{
1187 struct xonar_wm87x6 *data = chip->model_data;
1188 unsigned int i;
1189 struct snd_kcontrol *ctl;
1190 int err;
1191
1192 for (i = 0; i < ARRAY_SIZE(ds_controls); ++i) {
1193 ctl = snd_ctl_new1(&ds_controls[i], chip);
1194 if (!ctl)
1195 return -ENOMEM;
1196 err = snd_ctl_add(chip->card, ctl);
1197 if (err < 0)
1198 return err;
Clemens Ladischfe6ce802010-09-07 13:38:49 +02001199 if (!strcmp(ctl->id.name, "Line Capture Switch"))
1200 data->line_adcmux_control = ctl;
1201 else if (!strcmp(ctl->id.name, "Mic Capture Switch"))
1202 data->mic_adcmux_control = ctl;
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001203 }
Clemens Ladischfe6ce802010-09-07 13:38:49 +02001204 if (!data->line_adcmux_control || !data->mic_adcmux_control)
1205 return -ENXIO;
Clemens Ladischb532d6b2011-01-10 16:23:57 +01001206
1207 return add_lc_controls(chip);
1208}
1209
1210static int xonar_hdav_slim_mixer_init(struct oxygen *chip)
1211{
1212 unsigned int i;
1213 struct snd_kcontrol *ctl;
1214 int err;
1215
1216 for (i = 0; i < ARRAY_SIZE(hdav_slim_controls); ++i) {
1217 ctl = snd_ctl_new1(&hdav_slim_controls[i], chip);
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001218 if (!ctl)
1219 return -ENOMEM;
1220 err = snd_ctl_add(chip->card, ctl);
1221 if (err < 0)
1222 return err;
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001223 }
Clemens Ladischb532d6b2011-01-10 16:23:57 +01001224
1225 return add_lc_controls(chip);
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001226}
1227
Clemens Ladisch9719fca2010-12-02 11:41:10 +01001228static void dump_wm8776_registers(struct oxygen *chip,
1229 struct snd_info_buffer *buffer)
1230{
1231 struct xonar_wm87x6 *data = chip->model_data;
1232 unsigned int i;
1233
1234 snd_iprintf(buffer, "\nWM8776:\n00:");
1235 for (i = 0; i < 0x10; ++i)
1236 snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
1237 snd_iprintf(buffer, "\n10:");
1238 for (i = 0x10; i < 0x17; ++i)
1239 snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
1240 snd_iprintf(buffer, "\n");
1241}
1242
1243static void dump_wm87x6_registers(struct oxygen *chip,
1244 struct snd_info_buffer *buffer)
1245{
1246 struct xonar_wm87x6 *data = chip->model_data;
1247 unsigned int i;
1248
1249 dump_wm8776_registers(chip, buffer);
1250 snd_iprintf(buffer, "\nWM8766:\n00:");
1251 for (i = 0; i < 0x10; ++i)
1252 snd_iprintf(buffer, " %03x", data->wm8766_regs[i]);
1253 snd_iprintf(buffer, "\n");
1254}
1255
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001256static const struct oxygen_model model_xonar_ds = {
1257 .shortname = "Xonar DS",
Clemens Ladisch45bc3072010-10-04 13:17:26 +02001258 .longname = "Asus Virtuoso 66",
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001259 .chip = "AV200",
1260 .init = xonar_ds_init,
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001261 .mixer_init = xonar_ds_mixer_init,
1262 .cleanup = xonar_ds_cleanup,
1263 .suspend = xonar_ds_suspend,
1264 .resume = xonar_ds_resume,
1265 .pcm_hardware_filter = wm8776_adc_hardware_filter,
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001266 .set_dac_params = set_wm87x6_dac_params,
1267 .set_adc_params = set_wm8776_adc_params,
1268 .update_dac_volume = update_wm87x6_volume,
1269 .update_dac_mute = update_wm87x6_mute,
Clemens Ladisch2dbf0ea2010-09-09 12:24:35 +02001270 .update_center_lfe_mix = update_wm8766_center_lfe_mix,
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001271 .gpio_changed = xonar_ds_gpio_changed,
Clemens Ladisch9719fca2010-12-02 11:41:10 +01001272 .dump_registers = dump_wm87x6_registers,
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001273 .dac_tlv = wm87x6_dac_db_scale,
1274 .model_data_size = sizeof(struct xonar_wm87x6),
1275 .device_config = PLAYBACK_0_TO_I2S |
1276 PLAYBACK_1_TO_SPDIF |
1277 CAPTURE_0_FROM_I2S_1,
Clemens Ladisch1f4d7be2011-01-10 15:59:38 +01001278 .dac_channels_pcm = 8,
1279 .dac_channels_mixer = 8,
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001280 .dac_volume_min = 255 - 2*60,
1281 .dac_volume_max = 255,
1282 .function_flags = OXYGEN_FUNCTION_SPI,
Clemens Ladisch5b8bf2a2011-01-10 16:14:52 +01001283 .dac_mclks = OXYGEN_MCLKS(256, 256, 128),
1284 .adc_mclks = OXYGEN_MCLKS(256, 256, 128),
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001285 .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
1286 .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
1287};
1288
Clemens Ladischb532d6b2011-01-10 16:23:57 +01001289static const struct oxygen_model model_xonar_hdav_slim = {
1290 .shortname = "Xonar HDAV1.3 Slim",
1291 .longname = "Asus Virtuoso 200",
1292 .chip = "AV200",
1293 .init = xonar_hdav_slim_init,
1294 .mixer_init = xonar_hdav_slim_mixer_init,
1295 .cleanup = xonar_hdav_slim_cleanup,
1296 .suspend = xonar_hdav_slim_suspend,
1297 .resume = xonar_hdav_slim_resume,
1298 .pcm_hardware_filter = xonar_hdav_slim_hardware_filter,
1299 .set_dac_params = set_hdav_slim_dac_params,
1300 .set_adc_params = set_wm8776_adc_params,
1301 .update_dac_volume = update_wm8776_volume,
1302 .update_dac_mute = update_wm8776_mute,
1303 .uart_input = xonar_hdmi_uart_input,
1304 .dump_registers = dump_wm8776_registers,
1305 .dac_tlv = wm87x6_dac_db_scale,
1306 .model_data_size = sizeof(struct xonar_wm87x6),
1307 .device_config = PLAYBACK_0_TO_I2S |
1308 PLAYBACK_1_TO_SPDIF |
1309 CAPTURE_0_FROM_I2S_1,
1310 .dac_channels_pcm = 8,
1311 .dac_channels_mixer = 2,
1312 .dac_volume_min = 255 - 2*60,
1313 .dac_volume_max = 255,
1314 .function_flags = OXYGEN_FUNCTION_2WIRE,
1315 .dac_mclks = OXYGEN_MCLKS(256, 256, 128),
1316 .adc_mclks = OXYGEN_MCLKS(256, 256, 128),
1317 .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
1318 .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
1319};
1320
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001321int __devinit get_xonar_wm87x6_model(struct oxygen *chip,
1322 const struct pci_device_id *id)
1323{
1324 switch (id->subdevice) {
1325 case 0x838e:
1326 chip->model = model_xonar_ds;
1327 break;
Clemens Ladischb532d6b2011-01-10 16:23:57 +01001328 case 0x835e:
1329 chip->model = model_xonar_hdav_slim;
1330 break;
Clemens Ladischd1db38c2010-01-18 15:44:04 +01001331 default:
1332 return -EINVAL;
1333 }
1334 return 0;
1335}