diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2011-01-10 10:23:57 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-01-10 10:46:49 -0500 |
commit | b532d6b8d3aa163e1dc143bc729e9ee92f75baf5 (patch) | |
tree | ec0676e5a327ca94a1edffca60ca53675ba45f04 /sound/pci/oxygen | |
parent | 66410bfdf14f7c2ad3b2d4a8adeab41d368b6f05 (diff) |
ALSA: virtuoso: add Xonar HDAV1.3 Slim support
Add experimental support for the Asus Xonar HDAV1.3 Slim sound card.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/oxygen')
-rw-r--r-- | sound/pci/oxygen/xonar_hdmi.c | 2 | ||||
-rw-r--r-- | sound/pci/oxygen/xonar_pcm179x.c | 3 | ||||
-rw-r--r-- | sound/pci/oxygen/xonar_wm87x6.c | 213 |
3 files changed, 204 insertions, 14 deletions
diff --git a/sound/pci/oxygen/xonar_hdmi.c b/sound/pci/oxygen/xonar_hdmi.c index b12db1f1cea9..136dac6a3964 100644 --- a/sound/pci/oxygen/xonar_hdmi.c +++ b/sound/pci/oxygen/xonar_hdmi.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * helper functions for HDMI models (Xonar HDAV1.3) | 2 | * helper functions for HDMI models (Xonar HDAV1.3/HDAV1.3 Slim) |
3 | * | 3 | * |
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | 4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> |
5 | * | 5 | * |
diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 003c4800400b..bf4fd4bb1359 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c | |||
@@ -1152,9 +1152,6 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip, | |||
1152 | chip->model.resume = xonar_stx_resume; | 1152 | chip->model.resume = xonar_stx_resume; |
1153 | chip->model.set_dac_params = set_pcm1796_params; | 1153 | chip->model.set_dac_params = set_pcm1796_params; |
1154 | break; | 1154 | break; |
1155 | case 0x835e: | ||
1156 | snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n"); | ||
1157 | return -ENODEV; | ||
1158 | default: | 1155 | default: |
1159 | return -EINVAL; | 1156 | return -EINVAL; |
1160 | } | 1157 | } |
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index 4f9657084603..ad48356c54e4 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 | * |
@@ -77,6 +77,13 @@ | |||
77 | #define GPIO_DS_OUTPUT_FRONTLR 0x0080 | 77 | #define GPIO_DS_OUTPUT_FRONTLR 0x0080 |
78 | #define GPIO_DS_OUTPUT_ENABLE 0x0100 | 78 | #define GPIO_DS_OUTPUT_ENABLE 0x0100 |
79 | 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 */ | ||
86 | |||
80 | #define LC_CONTROL_LIMITER 0x40000000 | 87 | #define LC_CONTROL_LIMITER 0x40000000 |
81 | #define LC_CONTROL_ALC 0x20000000 | 88 | #define LC_CONTROL_ALC 0x20000000 |
82 | 89 | ||
@@ -88,19 +95,37 @@ struct xonar_wm87x6 { | |||
88 | struct snd_kcontrol *mic_adcmux_control; | 95 | struct snd_kcontrol *mic_adcmux_control; |
89 | struct snd_kcontrol *lc_controls[13]; | 96 | struct snd_kcontrol *lc_controls[13]; |
90 | struct snd_jack *hp_jack; | 97 | struct snd_jack *hp_jack; |
98 | struct xonar_hdmi hdmi; | ||
91 | }; | 99 | }; |
92 | 100 | ||
93 | static void wm8776_write(struct oxygen *chip, | 101 | static void wm8776_write_spi(struct oxygen *chip, |
94 | unsigned int reg, unsigned int value) | 102 | unsigned int reg, unsigned int value) |
95 | { | 103 | { |
96 | struct xonar_wm87x6 *data = chip->model_data; | ||
97 | |||
98 | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | | 104 | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | |
99 | OXYGEN_SPI_DATA_LENGTH_2 | | 105 | OXYGEN_SPI_DATA_LENGTH_2 | |
100 | OXYGEN_SPI_CLOCK_160 | | 106 | OXYGEN_SPI_CLOCK_160 | |
101 | (1 << OXYGEN_SPI_CODEC_SHIFT) | | 107 | (1 << OXYGEN_SPI_CODEC_SHIFT) | |
102 | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, | 108 | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, |
103 | (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); | ||
104 | if (reg < ARRAY_SIZE(data->wm8776_regs)) { | 129 | if (reg < ARRAY_SIZE(data->wm8776_regs)) { |
105 | if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER) | 130 | if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER) |
106 | value &= ~WM8776_UPDATE; | 131 | value &= ~WM8776_UPDATE; |
@@ -267,17 +292,50 @@ static void xonar_ds_init(struct oxygen *chip) | |||
267 | snd_component_add(chip->card, "WM8766"); | 292 | snd_component_add(chip->card, "WM8766"); |
268 | } | 293 | } |
269 | 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 | |||
270 | static void xonar_ds_cleanup(struct oxygen *chip) | 315 | static void xonar_ds_cleanup(struct oxygen *chip) |
271 | { | 316 | { |
272 | xonar_disable_output(chip); | 317 | xonar_disable_output(chip); |
273 | wm8776_write(chip, WM8776_RESET, 0); | 318 | wm8776_write(chip, WM8776_RESET, 0); |
274 | } | 319 | } |
275 | 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 | |||
276 | static void xonar_ds_suspend(struct oxygen *chip) | 329 | static void xonar_ds_suspend(struct oxygen *chip) |
277 | { | 330 | { |
278 | xonar_ds_cleanup(chip); | 331 | xonar_ds_cleanup(chip); |
279 | } | 332 | } |
280 | 333 | ||
334 | static void xonar_hdav_slim_suspend(struct oxygen *chip) | ||
335 | { | ||
336 | xonar_hdav_slim_cleanup(chip); | ||
337 | } | ||
338 | |||
281 | static void xonar_ds_resume(struct oxygen *chip) | 339 | static void xonar_ds_resume(struct oxygen *chip) |
282 | { | 340 | { |
283 | wm8776_registers_init(chip); | 341 | wm8776_registers_init(chip); |
@@ -286,6 +344,15 @@ static void xonar_ds_resume(struct oxygen *chip) | |||
286 | xonar_ds_handle_hp_jack(chip); | 344 | xonar_ds_handle_hp_jack(chip); |
287 | } | 345 | } |
288 | 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); | ||
354 | } | ||
355 | |||
289 | static void wm8776_adc_hardware_filter(unsigned int channel, | 356 | static void wm8776_adc_hardware_filter(unsigned int channel, |
290 | struct snd_pcm_hardware *hardware) | 357 | struct snd_pcm_hardware *hardware) |
291 | { | 358 | { |
@@ -300,6 +367,13 @@ static void wm8776_adc_hardware_filter(unsigned int channel, | |||
300 | } | 367 | } |
301 | } | 368 | } |
302 | 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 | |||
303 | static void set_wm87x6_dac_params(struct oxygen *chip, | 377 | static void set_wm87x6_dac_params(struct oxygen *chip, |
304 | struct snd_pcm_hw_params *params) | 378 | struct snd_pcm_hw_params *params) |
305 | { | 379 | { |
@@ -316,6 +390,14 @@ static void set_wm8776_adc_params(struct oxygen *chip, | |||
316 | wm8776_write_cached(chip, WM8776_MSTRCTRL, reg); | 390 | wm8776_write_cached(chip, WM8776_MSTRCTRL, reg); |
317 | } | 391 | } |
318 | 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 | |||
319 | static void update_wm8776_volume(struct oxygen *chip) | 401 | static void update_wm8776_volume(struct oxygen *chip) |
320 | { | 402 | { |
321 | struct xonar_wm87x6 *data = chip->model_data; | 403 | struct xonar_wm87x6 *data = chip->model_data; |
@@ -1007,6 +1089,53 @@ static const struct snd_kcontrol_new ds_controls[] = { | |||
1007 | .private_value = 0, | 1089 | .private_value = 0, |
1008 | }, | 1090 | }, |
1009 | }; | 1091 | }; |
1092 | static const struct snd_kcontrol_new hdav_slim_controls[] = { | ||
1093 | { | ||
1094 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1095 | .name = "HDMI Playback Switch", | ||
1096 | .info = snd_ctl_boolean_mono_info, | ||
1097 | .get = xonar_gpio_bit_switch_get, | ||
1098 | .put = xonar_gpio_bit_switch_put, | ||
1099 | .private_value = GPIO_SLIM_HDMI_DISABLE | XONAR_GPIO_BIT_INVERT, | ||
1100 | }, | ||
1101 | { | ||
1102 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1103 | .name = "Headphone Playback Volume", | ||
1104 | .info = wm8776_hp_vol_info, | ||
1105 | .get = wm8776_hp_vol_get, | ||
1106 | .put = wm8776_hp_vol_put, | ||
1107 | .tlv = { .p = wm8776_hp_db_scale }, | ||
1108 | }, | ||
1109 | WM8776_BIT_SWITCH("Headphone Playback Switch", | ||
1110 | WM8776_PWRDOWN, WM8776_HPPD, 1, 0), | ||
1111 | { | ||
1112 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1113 | .name = "Input Capture Volume", | ||
1114 | .info = wm8776_input_vol_info, | ||
1115 | .get = wm8776_input_vol_get, | ||
1116 | .put = wm8776_input_vol_put, | ||
1117 | .tlv = { .p = wm8776_adc_db_scale }, | ||
1118 | }, | ||
1119 | WM8776_BIT_SWITCH("Mic Capture Switch", | ||
1120 | WM8776_ADCMUX, 1 << 0, 0, 0), | ||
1121 | WM8776_BIT_SWITCH("Aux Capture Switch", | ||
1122 | WM8776_ADCMUX, 1 << 1, 0, 0), | ||
1123 | { | ||
1124 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1125 | .name = "ADC Filter Capture Enum", | ||
1126 | .info = hpf_info, | ||
1127 | .get = hpf_get, | ||
1128 | .put = hpf_put, | ||
1129 | }, | ||
1130 | { | ||
1131 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1132 | .name = "Level Control Capture Enum", | ||
1133 | .info = wm8776_level_control_info, | ||
1134 | .get = wm8776_level_control_get, | ||
1135 | .put = wm8776_level_control_put, | ||
1136 | .private_value = 0, | ||
1137 | }, | ||
1138 | }; | ||
1010 | static const struct snd_kcontrol_new lc_controls[] = { | 1139 | static const struct snd_kcontrol_new lc_controls[] = { |
1011 | WM8776_FIELD_CTL_VOLUME("Limiter Threshold", | 1140 | WM8776_FIELD_CTL_VOLUME("Limiter Threshold", |
1012 | WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf, | 1141 | WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf, |
@@ -1050,6 +1179,26 @@ static const struct snd_kcontrol_new lc_controls[] = { | |||
1050 | LC_CONTROL_ALC, wm8776_ngth_db_scale), | 1179 | LC_CONTROL_ALC, wm8776_ngth_db_scale), |
1051 | }; | 1180 | }; |
1052 | 1181 | ||
1182 | static int add_lc_controls(struct oxygen *chip) | ||
1183 | { | ||
1184 | struct xonar_wm87x6 *data = chip->model_data; | ||
1185 | unsigned int i; | ||
1186 | struct snd_kcontrol *ctl; | ||
1187 | int err; | ||
1188 | |||
1189 | BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls)); | ||
1190 | for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) { | ||
1191 | ctl = snd_ctl_new1(&lc_controls[i], chip); | ||
1192 | if (!ctl) | ||
1193 | return -ENOMEM; | ||
1194 | err = snd_ctl_add(chip->card, ctl); | ||
1195 | if (err < 0) | ||
1196 | return err; | ||
1197 | data->lc_controls[i] = ctl; | ||
1198 | } | ||
1199 | return 0; | ||
1200 | } | ||
1201 | |||
1053 | static int xonar_ds_mixer_init(struct oxygen *chip) | 1202 | static int xonar_ds_mixer_init(struct oxygen *chip) |
1054 | { | 1203 | { |
1055 | struct xonar_wm87x6 *data = chip->model_data; | 1204 | struct xonar_wm87x6 *data = chip->model_data; |
@@ -1071,17 +1220,26 @@ static int xonar_ds_mixer_init(struct oxygen *chip) | |||
1071 | } | 1220 | } |
1072 | if (!data->line_adcmux_control || !data->mic_adcmux_control) | 1221 | if (!data->line_adcmux_control || !data->mic_adcmux_control) |
1073 | return -ENXIO; | 1222 | return -ENXIO; |
1074 | BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls)); | 1223 | |
1075 | for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) { | 1224 | return add_lc_controls(chip); |
1076 | ctl = snd_ctl_new1(&lc_controls[i], chip); | 1225 | } |
1226 | |||
1227 | static int xonar_hdav_slim_mixer_init(struct oxygen *chip) | ||
1228 | { | ||
1229 | unsigned int i; | ||
1230 | struct snd_kcontrol *ctl; | ||
1231 | int err; | ||
1232 | |||
1233 | for (i = 0; i < ARRAY_SIZE(hdav_slim_controls); ++i) { | ||
1234 | ctl = snd_ctl_new1(&hdav_slim_controls[i], chip); | ||
1077 | if (!ctl) | 1235 | if (!ctl) |
1078 | return -ENOMEM; | 1236 | return -ENOMEM; |
1079 | err = snd_ctl_add(chip->card, ctl); | 1237 | err = snd_ctl_add(chip->card, ctl); |
1080 | if (err < 0) | 1238 | if (err < 0) |
1081 | return err; | 1239 | return err; |
1082 | data->lc_controls[i] = ctl; | ||
1083 | } | 1240 | } |
1084 | return 0; | 1241 | |
1242 | return add_lc_controls(chip); | ||
1085 | } | 1243 | } |
1086 | 1244 | ||
1087 | static void dump_wm8776_registers(struct oxygen *chip, | 1245 | static void dump_wm8776_registers(struct oxygen *chip, |
@@ -1145,6 +1303,38 @@ static const struct oxygen_model model_xonar_ds = { | |||
1145 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | 1303 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, |
1146 | }; | 1304 | }; |
1147 | 1305 | ||
1306 | static const struct oxygen_model model_xonar_hdav_slim = { | ||
1307 | .shortname = "Xonar HDAV1.3 Slim", | ||
1308 | .longname = "Asus Virtuoso 200", | ||
1309 | .chip = "AV200", | ||
1310 | .init = xonar_hdav_slim_init, | ||
1311 | .mixer_init = xonar_hdav_slim_mixer_init, | ||
1312 | .cleanup = xonar_hdav_slim_cleanup, | ||
1313 | .suspend = xonar_hdav_slim_suspend, | ||
1314 | .resume = xonar_hdav_slim_resume, | ||
1315 | .pcm_hardware_filter = xonar_hdav_slim_hardware_filter, | ||
1316 | .set_dac_params = set_hdav_slim_dac_params, | ||
1317 | .set_adc_params = set_wm8776_adc_params, | ||
1318 | .update_dac_volume = update_wm8776_volume, | ||
1319 | .update_dac_mute = update_wm8776_mute, | ||
1320 | .uart_input = xonar_hdmi_uart_input, | ||
1321 | .dump_registers = dump_wm8776_registers, | ||
1322 | .dac_tlv = wm87x6_dac_db_scale, | ||
1323 | .model_data_size = sizeof(struct xonar_wm87x6), | ||
1324 | .device_config = PLAYBACK_0_TO_I2S | | ||
1325 | PLAYBACK_1_TO_SPDIF | | ||
1326 | CAPTURE_0_FROM_I2S_1, | ||
1327 | .dac_channels_pcm = 8, | ||
1328 | .dac_channels_mixer = 2, | ||
1329 | .dac_volume_min = 255 - 2*60, | ||
1330 | .dac_volume_max = 255, | ||
1331 | .function_flags = OXYGEN_FUNCTION_2WIRE, | ||
1332 | .dac_mclks = OXYGEN_MCLKS(256, 256, 128), | ||
1333 | .adc_mclks = OXYGEN_MCLKS(256, 256, 128), | ||
1334 | .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | ||
1335 | .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | ||
1336 | }; | ||
1337 | |||
1148 | int __devinit get_xonar_wm87x6_model(struct oxygen *chip, | 1338 | int __devinit get_xonar_wm87x6_model(struct oxygen *chip, |
1149 | const struct pci_device_id *id) | 1339 | const struct pci_device_id *id) |
1150 | { | 1340 | { |
@@ -1152,6 +1342,9 @@ int __devinit get_xonar_wm87x6_model(struct oxygen *chip, | |||
1152 | case 0x838e: | 1342 | case 0x838e: |
1153 | chip->model = model_xonar_ds; | 1343 | chip->model = model_xonar_ds; |
1154 | break; | 1344 | break; |
1345 | case 0x835e: | ||
1346 | chip->model = model_xonar_hdav_slim; | ||
1347 | break; | ||
1155 | default: | 1348 | default: |
1156 | return -EINVAL; | 1349 | return -EINVAL; |
1157 | } | 1350 | } |