diff options
Diffstat (limited to 'sound/pci/oxygen/xonar_wm87x6.c')
-rw-r--r-- | sound/pci/oxygen/xonar_wm87x6.c | 412 |
1 files changed, 353 insertions, 59 deletions
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index b82c1cfa96f5..42d1ab136217 100644 --- a/sound/pci/oxygen/xonar_wm87x6.c +++ b/sound/pci/oxygen/xonar_wm87x6.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * card driver for models with WM8776/WM8766 DACs (Xonar DS) | 2 | * card driver for models with WM8776/WM8766 DACs (Xonar DS/HDAV1.3 Slim) |
3 | * | 3 | * |
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | 4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> |
5 | * | 5 | * |
@@ -22,19 +22,49 @@ | |||
22 | * | 22 | * |
23 | * CMI8788: | 23 | * CMI8788: |
24 | * | 24 | * |
25 | * SPI 0 -> WM8766 (surround, center/LFE, back) | 25 | * SPI 0 -> WM8766 (surround, center/LFE, back) |
26 | * SPI 1 -> WM8776 (front, input) | 26 | * SPI 1 -> WM8776 (front, input) |
27 | * | 27 | * |
28 | * GPIO 4 <- headphone detect | 28 | * GPIO 4 <- headphone detect, 0 = plugged |
29 | * GPIO 6 -> route input jack to input 1/2 (1/0) | 29 | * GPIO 6 -> route input jack to mic-in (0) or line-in (1) |
30 | * GPIO 7 -> enable output to speakers | 30 | * GPIO 7 -> enable output to front L/R speaker channels |
31 | * GPIO 8 -> enable output to speakers | 31 | * GPIO 8 -> enable output to other speaker channels and front panel headphone |
32 | * | ||
33 | * WM8776: | ||
34 | * | ||
35 | * 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 | ||
32 | */ | 60 | */ |
33 | 61 | ||
34 | #include <linux/pci.h> | 62 | #include <linux/pci.h> |
35 | #include <linux/delay.h> | 63 | #include <linux/delay.h> |
36 | #include <sound/control.h> | 64 | #include <sound/control.h> |
37 | #include <sound/core.h> | 65 | #include <sound/core.h> |
66 | #include <sound/info.h> | ||
67 | #include <sound/jack.h> | ||
38 | #include <sound/pcm.h> | 68 | #include <sound/pcm.h> |
39 | #include <sound/pcm_params.h> | 69 | #include <sound/pcm_params.h> |
40 | #include <sound/tlv.h> | 70 | #include <sound/tlv.h> |
@@ -44,7 +74,15 @@ | |||
44 | 74 | ||
45 | #define GPIO_DS_HP_DETECT 0x0010 | 75 | #define GPIO_DS_HP_DETECT 0x0010 |
46 | #define GPIO_DS_INPUT_ROUTE 0x0040 | 76 | #define GPIO_DS_INPUT_ROUTE 0x0040 |
47 | #define GPIO_DS_OUTPUT_ENABLE 0x0180 | 77 | #define GPIO_DS_OUTPUT_FRONTLR 0x0080 |
78 | #define GPIO_DS_OUTPUT_ENABLE 0x0100 | ||
79 | |||
80 | #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 */ | ||
48 | 86 | ||
49 | #define LC_CONTROL_LIMITER 0x40000000 | 87 | #define LC_CONTROL_LIMITER 0x40000000 |
50 | #define LC_CONTROL_ALC 0x20000000 | 88 | #define LC_CONTROL_ALC 0x20000000 |
@@ -56,19 +94,38 @@ struct xonar_wm87x6 { | |||
56 | struct snd_kcontrol *line_adcmux_control; | 94 | struct snd_kcontrol *line_adcmux_control; |
57 | struct snd_kcontrol *mic_adcmux_control; | 95 | struct snd_kcontrol *mic_adcmux_control; |
58 | struct snd_kcontrol *lc_controls[13]; | 96 | struct snd_kcontrol *lc_controls[13]; |
97 | struct snd_jack *hp_jack; | ||
98 | struct xonar_hdmi hdmi; | ||
59 | }; | 99 | }; |
60 | 100 | ||
61 | static void wm8776_write(struct oxygen *chip, | 101 | static void wm8776_write_spi(struct oxygen *chip, |
62 | unsigned int reg, unsigned int value) | 102 | unsigned int reg, unsigned int value) |
63 | { | 103 | { |
64 | struct xonar_wm87x6 *data = chip->model_data; | ||
65 | |||
66 | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | | 104 | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | |
67 | OXYGEN_SPI_DATA_LENGTH_2 | | 105 | OXYGEN_SPI_DATA_LENGTH_2 | |
68 | OXYGEN_SPI_CLOCK_160 | | 106 | OXYGEN_SPI_CLOCK_160 | |
69 | (1 << OXYGEN_SPI_CODEC_SHIFT) | | 107 | (1 << OXYGEN_SPI_CODEC_SHIFT) | |
70 | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, | 108 | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, |
71 | (reg << 9) | value); | 109 | (reg << 9) | value); |
110 | } | ||
111 | |||
112 | static 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 | |||
119 | static 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); | ||
72 | if (reg < ARRAY_SIZE(data->wm8776_regs)) { | 129 | if (reg < ARRAY_SIZE(data->wm8776_regs)) { |
73 | if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER) | 130 | if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER) |
74 | value &= ~WM8776_UPDATE; | 131 | value &= ~WM8776_UPDATE; |
@@ -97,8 +154,12 @@ static void wm8766_write(struct oxygen *chip, | |||
97 | (0 << OXYGEN_SPI_CODEC_SHIFT) | | 154 | (0 << OXYGEN_SPI_CODEC_SHIFT) | |
98 | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, | 155 | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, |
99 | (reg << 9) | value); | 156 | (reg << 9) | value); |
100 | if (reg < ARRAY_SIZE(data->wm8766_regs)) | 157 | 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; | ||
101 | data->wm8766_regs[reg] = value; | 161 | data->wm8766_regs[reg] = value; |
162 | } | ||
102 | } | 163 | } |
103 | 164 | ||
104 | static void wm8766_write_cached(struct oxygen *chip, | 165 | static void wm8766_write_cached(struct oxygen *chip, |
@@ -107,12 +168,8 @@ static void wm8766_write_cached(struct oxygen *chip, | |||
107 | struct xonar_wm87x6 *data = chip->model_data; | 168 | struct xonar_wm87x6 *data = chip->model_data; |
108 | 169 | ||
109 | if (reg >= ARRAY_SIZE(data->wm8766_regs) || | 170 | if (reg >= ARRAY_SIZE(data->wm8766_regs) || |
110 | value != data->wm8766_regs[reg]) { | 171 | value != data->wm8766_regs[reg]) |
111 | if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) || | ||
112 | (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA)) | ||
113 | value &= ~WM8766_UPDATE; | ||
114 | wm8766_write(chip, reg, value); | 172 | wm8766_write(chip, reg, value); |
115 | } | ||
116 | } | 173 | } |
117 | 174 | ||
118 | static void wm8776_registers_init(struct oxygen *chip) | 175 | static void wm8776_registers_init(struct oxygen *chip) |
@@ -141,7 +198,10 @@ static void wm8776_registers_init(struct oxygen *chip) | |||
141 | 198 | ||
142 | static void wm8766_registers_init(struct oxygen *chip) | 199 | static void wm8766_registers_init(struct oxygen *chip) |
143 | { | 200 | { |
201 | struct xonar_wm87x6 *data = chip->model_data; | ||
202 | |||
144 | wm8766_write(chip, WM8766_RESET, 0); | 203 | wm8766_write(chip, WM8766_RESET, 0); |
204 | wm8766_write(chip, WM8766_DAC_CTRL, data->wm8766_regs[WM8766_DAC_CTRL]); | ||
145 | wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24); | 205 | wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24); |
146 | wm8766_write(chip, WM8766_DAC_CTRL2, | 206 | wm8766_write(chip, WM8766_DAC_CTRL2, |
147 | WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); | 207 | WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); |
@@ -170,6 +230,40 @@ static void wm8776_init(struct oxygen *chip) | |||
170 | wm8776_registers_init(chip); | 230 | wm8776_registers_init(chip); |
171 | } | 231 | } |
172 | 232 | ||
233 | static void wm8766_init(struct oxygen *chip) | ||
234 | { | ||
235 | struct xonar_wm87x6 *data = chip->model_data; | ||
236 | |||
237 | data->wm8766_regs[WM8766_DAC_CTRL] = | ||
238 | WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT; | ||
239 | wm8766_registers_init(chip); | ||
240 | } | ||
241 | |||
242 | static 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); | ||
265 | } | ||
266 | |||
173 | static void xonar_ds_init(struct oxygen *chip) | 267 | static void xonar_ds_init(struct oxygen *chip) |
174 | { | 268 | { |
175 | struct xonar_wm87x6 *data = chip->model_data; | 269 | struct xonar_wm87x6 *data = chip->model_data; |
@@ -178,36 +272,85 @@ static void xonar_ds_init(struct oxygen *chip) | |||
178 | data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE; | 272 | data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE; |
179 | 273 | ||
180 | wm8776_init(chip); | 274 | wm8776_init(chip); |
181 | wm8766_registers_init(chip); | 275 | wm8766_init(chip); |
182 | 276 | ||
183 | oxygen_write16_masked(chip, OXYGEN_GPIO_CONTROL, GPIO_DS_INPUT_ROUTE, | 277 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, |
184 | GPIO_DS_HP_DETECT | GPIO_DS_INPUT_ROUTE); | 278 | GPIO_DS_INPUT_ROUTE | GPIO_DS_OUTPUT_FRONTLR); |
279 | oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, | ||
280 | GPIO_DS_HP_DETECT); | ||
185 | oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE); | 281 | oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE); |
186 | oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT); | 282 | oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT); |
187 | chip->interrupt_mask |= OXYGEN_INT_GPIO; | 283 | chip->interrupt_mask |= OXYGEN_INT_GPIO; |
188 | 284 | ||
189 | xonar_enable_output(chip); | 285 | xonar_enable_output(chip); |
190 | 286 | ||
287 | snd_jack_new(chip->card, "Headphone", | ||
288 | SND_JACK_HEADPHONE, &data->hp_jack); | ||
289 | xonar_ds_handle_hp_jack(chip); | ||
290 | |||
191 | snd_component_add(chip->card, "WM8776"); | 291 | snd_component_add(chip->card, "WM8776"); |
192 | snd_component_add(chip->card, "WM8766"); | 292 | snd_component_add(chip->card, "WM8766"); |
193 | } | 293 | } |
194 | 294 | ||
295 | static 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 | |||
195 | static void xonar_ds_cleanup(struct oxygen *chip) | 315 | static void xonar_ds_cleanup(struct oxygen *chip) |
196 | { | 316 | { |
197 | xonar_disable_output(chip); | 317 | xonar_disable_output(chip); |
198 | wm8776_write(chip, WM8776_RESET, 0); | 318 | wm8776_write(chip, WM8776_RESET, 0); |
199 | } | 319 | } |
200 | 320 | ||
321 | static 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 | |||
201 | static void xonar_ds_suspend(struct oxygen *chip) | 329 | static void xonar_ds_suspend(struct oxygen *chip) |
202 | { | 330 | { |
203 | xonar_ds_cleanup(chip); | 331 | xonar_ds_cleanup(chip); |
204 | } | 332 | } |
205 | 333 | ||
334 | static void xonar_hdav_slim_suspend(struct oxygen *chip) | ||
335 | { | ||
336 | xonar_hdav_slim_cleanup(chip); | ||
337 | } | ||
338 | |||
206 | static void xonar_ds_resume(struct oxygen *chip) | 339 | static void xonar_ds_resume(struct oxygen *chip) |
207 | { | 340 | { |
208 | wm8776_registers_init(chip); | 341 | wm8776_registers_init(chip); |
209 | wm8766_registers_init(chip); | 342 | wm8766_registers_init(chip); |
210 | xonar_enable_output(chip); | 343 | xonar_enable_output(chip); |
344 | xonar_ds_handle_hp_jack(chip); | ||
345 | } | ||
346 | |||
347 | static 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); | ||
211 | } | 354 | } |
212 | 355 | ||
213 | static void wm8776_adc_hardware_filter(unsigned int channel, | 356 | static void wm8776_adc_hardware_filter(unsigned int channel, |
@@ -224,6 +367,13 @@ static void wm8776_adc_hardware_filter(unsigned int channel, | |||
224 | } | 367 | } |
225 | } | 368 | } |
226 | 369 | ||
370 | static 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 | |||
227 | static void set_wm87x6_dac_params(struct oxygen *chip, | 377 | static void set_wm87x6_dac_params(struct oxygen *chip, |
228 | struct snd_pcm_hw_params *params) | 378 | struct snd_pcm_hw_params *params) |
229 | { | 379 | { |
@@ -240,6 +390,14 @@ static void set_wm8776_adc_params(struct oxygen *chip, | |||
240 | wm8776_write_cached(chip, WM8776_MSTRCTRL, reg); | 390 | wm8776_write_cached(chip, WM8776_MSTRCTRL, reg); |
241 | } | 391 | } |
242 | 392 | ||
393 | static 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 | |||
243 | static void update_wm8776_volume(struct oxygen *chip) | 401 | static void update_wm8776_volume(struct oxygen *chip) |
244 | { | 402 | { |
245 | struct xonar_wm87x6 *data = chip->model_data; | 403 | struct xonar_wm87x6 *data = chip->model_data; |
@@ -323,12 +481,27 @@ static void update_wm87x6_mute(struct oxygen *chip) | |||
323 | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); | 481 | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); |
324 | } | 482 | } |
325 | 483 | ||
326 | static void xonar_ds_gpio_changed(struct oxygen *chip) | 484 | static void update_wm8766_center_lfe_mix(struct oxygen *chip, bool mixed) |
327 | { | 485 | { |
328 | u16 bits; | 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 | } | ||
329 | 501 | ||
330 | bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); | 502 | static void xonar_ds_gpio_changed(struct oxygen *chip) |
331 | snd_printk(KERN_INFO "HP detect: %d\n", !!(bits & GPIO_DS_HP_DETECT)); | 503 | { |
504 | xonar_ds_handle_hp_jack(chip); | ||
332 | } | 505 | } |
333 | 506 | ||
334 | static int wm8776_bit_switch_get(struct snd_kcontrol *ctl, | 507 | static int wm8776_bit_switch_get(struct snd_kcontrol *ctl, |
@@ -404,11 +577,6 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl, | |||
404 | const char *const *names; | 577 | const char *const *names; |
405 | 578 | ||
406 | max = (ctl->private_value >> 12) & 0xf; | 579 | max = (ctl->private_value >> 12) & 0xf; |
407 | info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
408 | info->count = 1; | ||
409 | info->value.enumerated.items = max + 1; | ||
410 | if (info->value.enumerated.item > max) | ||
411 | info->value.enumerated.item = max; | ||
412 | switch ((ctl->private_value >> 24) & 0x1f) { | 580 | switch ((ctl->private_value >> 24) & 0x1f) { |
413 | case WM8776_ALCCTRL2: | 581 | case WM8776_ALCCTRL2: |
414 | names = hld; | 582 | names = hld; |
@@ -432,8 +600,7 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl, | |||
432 | default: | 600 | default: |
433 | return -ENXIO; | 601 | return -ENXIO; |
434 | } | 602 | } |
435 | strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); | 603 | return snd_ctl_enum_info(info, 1, max + 1, names); |
436 | return 0; | ||
437 | } | 604 | } |
438 | 605 | ||
439 | static int wm8776_field_volume_info(struct snd_kcontrol *ctl, | 606 | static int wm8776_field_volume_info(struct snd_kcontrol *ctl, |
@@ -690,13 +857,8 @@ static int wm8776_level_control_info(struct snd_kcontrol *ctl, | |||
690 | static const char *const names[3] = { | 857 | static const char *const names[3] = { |
691 | "None", "Peak Limiter", "Automatic Level Control" | 858 | "None", "Peak Limiter", "Automatic Level Control" |
692 | }; | 859 | }; |
693 | info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | 860 | |
694 | info->count = 1; | 861 | return snd_ctl_enum_info(info, 1, 3, names); |
695 | info->value.enumerated.items = 3; | ||
696 | if (info->value.enumerated.item >= 3) | ||
697 | info->value.enumerated.item = 2; | ||
698 | strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); | ||
699 | return 0; | ||
700 | } | 862 | } |
701 | 863 | ||
702 | static int wm8776_level_control_get(struct snd_kcontrol *ctl, | 864 | static int wm8776_level_control_get(struct snd_kcontrol *ctl, |
@@ -782,13 +944,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) | |||
782 | "None", "High-pass Filter" | 944 | "None", "High-pass Filter" |
783 | }; | 945 | }; |
784 | 946 | ||
785 | info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | 947 | return snd_ctl_enum_info(info, 1, 2, names); |
786 | info->count = 1; | ||
787 | info->value.enumerated.items = 2; | ||
788 | if (info->value.enumerated.item >= 2) | ||
789 | info->value.enumerated.item = 1; | ||
790 | strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); | ||
791 | return 0; | ||
792 | } | 948 | } |
793 | 949 | ||
794 | static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) | 950 | static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) |
@@ -896,7 +1052,57 @@ static const struct snd_kcontrol_new ds_controls[] = { | |||
896 | .put = wm8776_input_mux_put, | 1052 | .put = wm8776_input_mux_put, |
897 | .private_value = 1 << 1, | 1053 | .private_value = 1 << 1, |
898 | }, | 1054 | }, |
899 | WM8776_BIT_SWITCH("Aux", WM8776_ADCMUX, 1 << 2, 0, 0), | 1055 | 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), | ||
1059 | { | ||
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 | }; | ||
1075 | static 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), | ||
900 | { | 1106 | { |
901 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 1107 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
902 | .name = "ADC Filter Capture Enum", | 1108 | .name = "ADC Filter Capture Enum", |
@@ -956,10 +1162,23 @@ static const struct snd_kcontrol_new lc_controls[] = { | |||
956 | LC_CONTROL_ALC, wm8776_ngth_db_scale), | 1162 | LC_CONTROL_ALC, wm8776_ngth_db_scale), |
957 | }; | 1163 | }; |
958 | 1164 | ||
959 | static int xonar_ds_control_filter(struct snd_kcontrol_new *template) | 1165 | static int add_lc_controls(struct oxygen *chip) |
960 | { | 1166 | { |
961 | if (!strncmp(template->name, "CD Capture ", 11)) | 1167 | struct xonar_wm87x6 *data = chip->model_data; |
962 | return 1; /* no CD input */ | 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 | } | ||
963 | return 0; | 1182 | return 0; |
964 | } | 1183 | } |
965 | 1184 | ||
@@ -984,45 +1203,117 @@ static int xonar_ds_mixer_init(struct oxygen *chip) | |||
984 | } | 1203 | } |
985 | if (!data->line_adcmux_control || !data->mic_adcmux_control) | 1204 | if (!data->line_adcmux_control || !data->mic_adcmux_control) |
986 | return -ENXIO; | 1205 | return -ENXIO; |
987 | BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls)); | 1206 | |
988 | for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) { | 1207 | return add_lc_controls(chip); |
989 | ctl = snd_ctl_new1(&lc_controls[i], chip); | 1208 | } |
1209 | |||
1210 | static 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); | ||
990 | if (!ctl) | 1218 | if (!ctl) |
991 | return -ENOMEM; | 1219 | return -ENOMEM; |
992 | err = snd_ctl_add(chip->card, ctl); | 1220 | err = snd_ctl_add(chip->card, ctl); |
993 | if (err < 0) | 1221 | if (err < 0) |
994 | return err; | 1222 | return err; |
995 | data->lc_controls[i] = ctl; | ||
996 | } | 1223 | } |
997 | return 0; | 1224 | |
1225 | return add_lc_controls(chip); | ||
1226 | } | ||
1227 | |||
1228 | static 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 | |||
1243 | static 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"); | ||
998 | } | 1254 | } |
999 | 1255 | ||
1000 | static const struct oxygen_model model_xonar_ds = { | 1256 | static const struct oxygen_model model_xonar_ds = { |
1001 | .shortname = "Xonar DS", | 1257 | .shortname = "Xonar DS", |
1002 | .longname = "Asus Virtuoso 200", | 1258 | .longname = "Asus Virtuoso 66", |
1003 | .chip = "AV200", | 1259 | .chip = "AV200", |
1004 | .init = xonar_ds_init, | 1260 | .init = xonar_ds_init, |
1005 | .control_filter = xonar_ds_control_filter, | ||
1006 | .mixer_init = xonar_ds_mixer_init, | 1261 | .mixer_init = xonar_ds_mixer_init, |
1007 | .cleanup = xonar_ds_cleanup, | 1262 | .cleanup = xonar_ds_cleanup, |
1008 | .suspend = xonar_ds_suspend, | 1263 | .suspend = xonar_ds_suspend, |
1009 | .resume = xonar_ds_resume, | 1264 | .resume = xonar_ds_resume, |
1010 | .pcm_hardware_filter = wm8776_adc_hardware_filter, | 1265 | .pcm_hardware_filter = wm8776_adc_hardware_filter, |
1011 | .get_i2s_mclk = oxygen_default_i2s_mclk, | ||
1012 | .set_dac_params = set_wm87x6_dac_params, | 1266 | .set_dac_params = set_wm87x6_dac_params, |
1013 | .set_adc_params = set_wm8776_adc_params, | 1267 | .set_adc_params = set_wm8776_adc_params, |
1014 | .update_dac_volume = update_wm87x6_volume, | 1268 | .update_dac_volume = update_wm87x6_volume, |
1015 | .update_dac_mute = update_wm87x6_mute, | 1269 | .update_dac_mute = update_wm87x6_mute, |
1270 | .update_center_lfe_mix = update_wm8766_center_lfe_mix, | ||
1016 | .gpio_changed = xonar_ds_gpio_changed, | 1271 | .gpio_changed = xonar_ds_gpio_changed, |
1272 | .dump_registers = dump_wm87x6_registers, | ||
1017 | .dac_tlv = wm87x6_dac_db_scale, | 1273 | .dac_tlv = wm87x6_dac_db_scale, |
1018 | .model_data_size = sizeof(struct xonar_wm87x6), | 1274 | .model_data_size = sizeof(struct xonar_wm87x6), |
1019 | .device_config = PLAYBACK_0_TO_I2S | | 1275 | .device_config = PLAYBACK_0_TO_I2S | |
1020 | PLAYBACK_1_TO_SPDIF | | 1276 | PLAYBACK_1_TO_SPDIF | |
1021 | CAPTURE_0_FROM_I2S_1, | 1277 | CAPTURE_0_FROM_I2S_1, |
1022 | .dac_channels = 8, | 1278 | .dac_channels_pcm = 8, |
1279 | .dac_channels_mixer = 8, | ||
1023 | .dac_volume_min = 255 - 2*60, | 1280 | .dac_volume_min = 255 - 2*60, |
1024 | .dac_volume_max = 255, | 1281 | .dac_volume_max = 255, |
1025 | .function_flags = OXYGEN_FUNCTION_SPI, | 1282 | .function_flags = OXYGEN_FUNCTION_SPI, |
1283 | .dac_mclks = OXYGEN_MCLKS(256, 256, 128), | ||
1284 | .adc_mclks = OXYGEN_MCLKS(256, 256, 128), | ||
1285 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | ||
1286 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | ||
1287 | }; | ||
1288 | |||
1289 | static 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), | ||
1026 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | 1317 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, |
1027 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | 1318 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, |
1028 | }; | 1319 | }; |
@@ -1034,6 +1325,9 @@ int __devinit get_xonar_wm87x6_model(struct oxygen *chip, | |||
1034 | case 0x838e: | 1325 | case 0x838e: |
1035 | chip->model = model_xonar_ds; | 1326 | chip->model = model_xonar_ds; |
1036 | break; | 1327 | break; |
1328 | case 0x835e: | ||
1329 | chip->model = model_xonar_hdav_slim; | ||
1330 | break; | ||
1037 | default: | 1331 | default: |
1038 | return -EINVAL; | 1332 | return -EINVAL; |
1039 | } | 1333 | } |