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