diff options
Diffstat (limited to 'sound/pci/oxygen/xonar_wm87x6.c')
-rw-r--r-- | sound/pci/oxygen/xonar_wm87x6.c | 121 |
1 files changed, 93 insertions, 28 deletions
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index b82c1cfa96f5..200f7601276f 100644 --- a/sound/pci/oxygen/xonar_wm87x6.c +++ b/sound/pci/oxygen/xonar_wm87x6.c | |||
@@ -25,16 +25,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 | * WM8766: | ||
34 | * | ||
35 | * input 1 <- line | ||
36 | * input 2 <- mic | ||
37 | * input 3 <- front mic | ||
38 | * input 4 <- aux | ||
32 | */ | 39 | */ |
33 | 40 | ||
34 | #include <linux/pci.h> | 41 | #include <linux/pci.h> |
35 | #include <linux/delay.h> | 42 | #include <linux/delay.h> |
36 | #include <sound/control.h> | 43 | #include <sound/control.h> |
37 | #include <sound/core.h> | 44 | #include <sound/core.h> |
45 | #include <sound/jack.h> | ||
38 | #include <sound/pcm.h> | 46 | #include <sound/pcm.h> |
39 | #include <sound/pcm_params.h> | 47 | #include <sound/pcm_params.h> |
40 | #include <sound/tlv.h> | 48 | #include <sound/tlv.h> |
@@ -44,7 +52,8 @@ | |||
44 | 52 | ||
45 | #define GPIO_DS_HP_DETECT 0x0010 | 53 | #define GPIO_DS_HP_DETECT 0x0010 |
46 | #define GPIO_DS_INPUT_ROUTE 0x0040 | 54 | #define GPIO_DS_INPUT_ROUTE 0x0040 |
47 | #define GPIO_DS_OUTPUT_ENABLE 0x0180 | 55 | #define GPIO_DS_OUTPUT_FRONTLR 0x0080 |
56 | #define GPIO_DS_OUTPUT_ENABLE 0x0100 | ||
48 | 57 | ||
49 | #define LC_CONTROL_LIMITER 0x40000000 | 58 | #define LC_CONTROL_LIMITER 0x40000000 |
50 | #define LC_CONTROL_ALC 0x20000000 | 59 | #define LC_CONTROL_ALC 0x20000000 |
@@ -56,6 +65,7 @@ struct xonar_wm87x6 { | |||
56 | struct snd_kcontrol *line_adcmux_control; | 65 | struct snd_kcontrol *line_adcmux_control; |
57 | struct snd_kcontrol *mic_adcmux_control; | 66 | struct snd_kcontrol *mic_adcmux_control; |
58 | struct snd_kcontrol *lc_controls[13]; | 67 | struct snd_kcontrol *lc_controls[13]; |
68 | struct snd_jack *hp_jack; | ||
59 | }; | 69 | }; |
60 | 70 | ||
61 | static void wm8776_write(struct oxygen *chip, | 71 | static void wm8776_write(struct oxygen *chip, |
@@ -97,8 +107,12 @@ static void wm8766_write(struct oxygen *chip, | |||
97 | (0 << OXYGEN_SPI_CODEC_SHIFT) | | 107 | (0 << OXYGEN_SPI_CODEC_SHIFT) | |
98 | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, | 108 | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, |
99 | (reg << 9) | value); | 109 | (reg << 9) | value); |
100 | if (reg < ARRAY_SIZE(data->wm8766_regs)) | 110 | if (reg < ARRAY_SIZE(data->wm8766_regs)) { |
111 | if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) || | ||
112 | (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA)) | ||
113 | value &= ~WM8766_UPDATE; | ||
101 | data->wm8766_regs[reg] = value; | 114 | data->wm8766_regs[reg] = value; |
115 | } | ||
102 | } | 116 | } |
103 | 117 | ||
104 | static void wm8766_write_cached(struct oxygen *chip, | 118 | static void wm8766_write_cached(struct oxygen *chip, |
@@ -107,12 +121,8 @@ static void wm8766_write_cached(struct oxygen *chip, | |||
107 | struct xonar_wm87x6 *data = chip->model_data; | 121 | struct xonar_wm87x6 *data = chip->model_data; |
108 | 122 | ||
109 | if (reg >= ARRAY_SIZE(data->wm8766_regs) || | 123 | if (reg >= ARRAY_SIZE(data->wm8766_regs) || |
110 | value != data->wm8766_regs[reg]) { | 124 | 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); | 125 | wm8766_write(chip, reg, value); |
115 | } | ||
116 | } | 126 | } |
117 | 127 | ||
118 | static void wm8776_registers_init(struct oxygen *chip) | 128 | static void wm8776_registers_init(struct oxygen *chip) |
@@ -141,7 +151,10 @@ static void wm8776_registers_init(struct oxygen *chip) | |||
141 | 151 | ||
142 | static void wm8766_registers_init(struct oxygen *chip) | 152 | static void wm8766_registers_init(struct oxygen *chip) |
143 | { | 153 | { |
154 | struct xonar_wm87x6 *data = chip->model_data; | ||
155 | |||
144 | wm8766_write(chip, WM8766_RESET, 0); | 156 | wm8766_write(chip, WM8766_RESET, 0); |
157 | 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); | 158 | wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24); |
146 | wm8766_write(chip, WM8766_DAC_CTRL2, | 159 | wm8766_write(chip, WM8766_DAC_CTRL2, |
147 | WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); | 160 | WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); |
@@ -170,6 +183,40 @@ static void wm8776_init(struct oxygen *chip) | |||
170 | wm8776_registers_init(chip); | 183 | wm8776_registers_init(chip); |
171 | } | 184 | } |
172 | 185 | ||
186 | static void wm8766_init(struct oxygen *chip) | ||
187 | { | ||
188 | struct xonar_wm87x6 *data = chip->model_data; | ||
189 | |||
190 | data->wm8766_regs[WM8766_DAC_CTRL] = | ||
191 | WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT; | ||
192 | wm8766_registers_init(chip); | ||
193 | } | ||
194 | |||
195 | static void xonar_ds_handle_hp_jack(struct oxygen *chip) | ||
196 | { | ||
197 | struct xonar_wm87x6 *data = chip->model_data; | ||
198 | bool hp_plugged; | ||
199 | unsigned int reg; | ||
200 | |||
201 | mutex_lock(&chip->mutex); | ||
202 | |||
203 | hp_plugged = !(oxygen_read16(chip, OXYGEN_GPIO_DATA) & | ||
204 | GPIO_DS_HP_DETECT); | ||
205 | |||
206 | oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, | ||
207 | hp_plugged ? 0 : GPIO_DS_OUTPUT_FRONTLR, | ||
208 | GPIO_DS_OUTPUT_FRONTLR); | ||
209 | |||
210 | reg = data->wm8766_regs[WM8766_DAC_CTRL] & ~WM8766_MUTEALL; | ||
211 | if (hp_plugged) | ||
212 | reg |= WM8766_MUTEALL; | ||
213 | wm8766_write_cached(chip, WM8766_DAC_CTRL, reg); | ||
214 | |||
215 | snd_jack_report(data->hp_jack, hp_plugged ? SND_JACK_HEADPHONE : 0); | ||
216 | |||
217 | mutex_unlock(&chip->mutex); | ||
218 | } | ||
219 | |||
173 | static void xonar_ds_init(struct oxygen *chip) | 220 | static void xonar_ds_init(struct oxygen *chip) |
174 | { | 221 | { |
175 | struct xonar_wm87x6 *data = chip->model_data; | 222 | struct xonar_wm87x6 *data = chip->model_data; |
@@ -178,16 +225,22 @@ static void xonar_ds_init(struct oxygen *chip) | |||
178 | data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE; | 225 | data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE; |
179 | 226 | ||
180 | wm8776_init(chip); | 227 | wm8776_init(chip); |
181 | wm8766_registers_init(chip); | 228 | wm8766_init(chip); |
182 | 229 | ||
183 | oxygen_write16_masked(chip, OXYGEN_GPIO_CONTROL, GPIO_DS_INPUT_ROUTE, | 230 | oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, |
184 | GPIO_DS_HP_DETECT | GPIO_DS_INPUT_ROUTE); | 231 | GPIO_DS_INPUT_ROUTE | GPIO_DS_OUTPUT_FRONTLR); |
232 | oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, | ||
233 | GPIO_DS_HP_DETECT); | ||
185 | oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE); | 234 | oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE); |
186 | oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT); | 235 | oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT); |
187 | chip->interrupt_mask |= OXYGEN_INT_GPIO; | 236 | chip->interrupt_mask |= OXYGEN_INT_GPIO; |
188 | 237 | ||
189 | xonar_enable_output(chip); | 238 | xonar_enable_output(chip); |
190 | 239 | ||
240 | snd_jack_new(chip->card, "Headphone", | ||
241 | SND_JACK_HEADPHONE, &data->hp_jack); | ||
242 | xonar_ds_handle_hp_jack(chip); | ||
243 | |||
191 | snd_component_add(chip->card, "WM8776"); | 244 | snd_component_add(chip->card, "WM8776"); |
192 | snd_component_add(chip->card, "WM8766"); | 245 | snd_component_add(chip->card, "WM8766"); |
193 | } | 246 | } |
@@ -208,6 +261,7 @@ static void xonar_ds_resume(struct oxygen *chip) | |||
208 | wm8776_registers_init(chip); | 261 | wm8776_registers_init(chip); |
209 | wm8766_registers_init(chip); | 262 | wm8766_registers_init(chip); |
210 | xonar_enable_output(chip); | 263 | xonar_enable_output(chip); |
264 | xonar_ds_handle_hp_jack(chip); | ||
211 | } | 265 | } |
212 | 266 | ||
213 | static void wm8776_adc_hardware_filter(unsigned int channel, | 267 | static void wm8776_adc_hardware_filter(unsigned int channel, |
@@ -323,12 +377,27 @@ static void update_wm87x6_mute(struct oxygen *chip) | |||
323 | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); | 377 | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); |
324 | } | 378 | } |
325 | 379 | ||
326 | static void xonar_ds_gpio_changed(struct oxygen *chip) | 380 | static void update_wm8766_center_lfe_mix(struct oxygen *chip, bool mixed) |
327 | { | 381 | { |
328 | u16 bits; | 382 | struct xonar_wm87x6 *data = chip->model_data; |
383 | unsigned int reg; | ||
329 | 384 | ||
330 | bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); | 385 | /* |
331 | snd_printk(KERN_INFO "HP detect: %d\n", !!(bits & GPIO_DS_HP_DETECT)); | 386 | * The WM8766 can mix left and right channels, but this setting |
387 | * applies to all three stereo pairs. | ||
388 | */ | ||
389 | reg = data->wm8766_regs[WM8766_DAC_CTRL] & | ||
390 | ~(WM8766_PL_LEFT_MASK | WM8766_PL_RIGHT_MASK); | ||
391 | if (mixed) | ||
392 | reg |= WM8766_PL_LEFT_LRMIX | WM8766_PL_RIGHT_LRMIX; | ||
393 | else | ||
394 | reg |= WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT; | ||
395 | wm8766_write_cached(chip, WM8766_DAC_CTRL, reg); | ||
396 | } | ||
397 | |||
398 | static void xonar_ds_gpio_changed(struct oxygen *chip) | ||
399 | { | ||
400 | xonar_ds_handle_hp_jack(chip); | ||
332 | } | 401 | } |
333 | 402 | ||
334 | static int wm8776_bit_switch_get(struct snd_kcontrol *ctl, | 403 | static int wm8776_bit_switch_get(struct snd_kcontrol *ctl, |
@@ -896,7 +965,10 @@ static const struct snd_kcontrol_new ds_controls[] = { | |||
896 | .put = wm8776_input_mux_put, | 965 | .put = wm8776_input_mux_put, |
897 | .private_value = 1 << 1, | 966 | .private_value = 1 << 1, |
898 | }, | 967 | }, |
899 | WM8776_BIT_SWITCH("Aux", WM8776_ADCMUX, 1 << 2, 0, 0), | 968 | WM8776_BIT_SWITCH("Front Mic Capture Switch", |
969 | WM8776_ADCMUX, 1 << 2, 0, 0), | ||
970 | WM8776_BIT_SWITCH("Aux Capture Switch", | ||
971 | WM8776_ADCMUX, 1 << 3, 0, 0), | ||
900 | { | 972 | { |
901 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 973 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
902 | .name = "ADC Filter Capture Enum", | 974 | .name = "ADC Filter Capture Enum", |
@@ -956,13 +1028,6 @@ static const struct snd_kcontrol_new lc_controls[] = { | |||
956 | LC_CONTROL_ALC, wm8776_ngth_db_scale), | 1028 | LC_CONTROL_ALC, wm8776_ngth_db_scale), |
957 | }; | 1029 | }; |
958 | 1030 | ||
959 | static int xonar_ds_control_filter(struct snd_kcontrol_new *template) | ||
960 | { | ||
961 | if (!strncmp(template->name, "CD Capture ", 11)) | ||
962 | return 1; /* no CD input */ | ||
963 | return 0; | ||
964 | } | ||
965 | |||
966 | static int xonar_ds_mixer_init(struct oxygen *chip) | 1031 | static int xonar_ds_mixer_init(struct oxygen *chip) |
967 | { | 1032 | { |
968 | struct xonar_wm87x6 *data = chip->model_data; | 1033 | struct xonar_wm87x6 *data = chip->model_data; |
@@ -999,10 +1064,9 @@ static int xonar_ds_mixer_init(struct oxygen *chip) | |||
999 | 1064 | ||
1000 | static const struct oxygen_model model_xonar_ds = { | 1065 | static const struct oxygen_model model_xonar_ds = { |
1001 | .shortname = "Xonar DS", | 1066 | .shortname = "Xonar DS", |
1002 | .longname = "Asus Virtuoso 200", | 1067 | .longname = "Asus Virtuoso 66", |
1003 | .chip = "AV200", | 1068 | .chip = "AV200", |
1004 | .init = xonar_ds_init, | 1069 | .init = xonar_ds_init, |
1005 | .control_filter = xonar_ds_control_filter, | ||
1006 | .mixer_init = xonar_ds_mixer_init, | 1070 | .mixer_init = xonar_ds_mixer_init, |
1007 | .cleanup = xonar_ds_cleanup, | 1071 | .cleanup = xonar_ds_cleanup, |
1008 | .suspend = xonar_ds_suspend, | 1072 | .suspend = xonar_ds_suspend, |
@@ -1013,6 +1077,7 @@ static const struct oxygen_model model_xonar_ds = { | |||
1013 | .set_adc_params = set_wm8776_adc_params, | 1077 | .set_adc_params = set_wm8776_adc_params, |
1014 | .update_dac_volume = update_wm87x6_volume, | 1078 | .update_dac_volume = update_wm87x6_volume, |
1015 | .update_dac_mute = update_wm87x6_mute, | 1079 | .update_dac_mute = update_wm87x6_mute, |
1080 | .update_center_lfe_mix = update_wm8766_center_lfe_mix, | ||
1016 | .gpio_changed = xonar_ds_gpio_changed, | 1081 | .gpio_changed = xonar_ds_gpio_changed, |
1017 | .dac_tlv = wm87x6_dac_db_scale, | 1082 | .dac_tlv = wm87x6_dac_db_scale, |
1018 | .model_data_size = sizeof(struct xonar_wm87x6), | 1083 | .model_data_size = sizeof(struct xonar_wm87x6), |