aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2011-01-10 10:23:57 -0500
committerTakashi Iwai <tiwai@suse.de>2011-01-10 10:46:49 -0500
commitb532d6b8d3aa163e1dc143bc729e9ee92f75baf5 (patch)
treeec0676e5a327ca94a1edffca60ca53675ba45f04 /sound/pci
parent66410bfdf14f7c2ad3b2d4a8adeab41d368b6f05 (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')
-rw-r--r--sound/pci/Kconfig4
-rw-r--r--sound/pci/oxygen/xonar_hdmi.c2
-rw-r--r--sound/pci/oxygen/xonar_pcm179x.c3
-rw-r--r--sound/pci/oxygen/xonar_wm87x6.c213
4 files changed, 206 insertions, 16 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index f0bcf689de24..9823d59d7ad7 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -819,8 +819,8 @@ config SND_VIRTUOSO
819 Say Y here to include support for sound cards based on the 819 Say Y here to include support for sound cards based on the
820 Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, 820 Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
821 Essence ST (Deluxe), and Essence STX. 821 Essence ST (Deluxe), and Essence STX.
822 Support for the HDAV1.3 (Deluxe) is experimental; for the 822 Support for the HDAV1.3 (Deluxe) and HDAV1.3 Slim is experimental;
823 HDAV1.3 Slim and Xense, missing. 823 for the Xense, missing.
824 824
825 To compile this driver as a module, choose M here: the module 825 To compile this driver as a module, choose M here: the module
826 will be called snd-virtuoso. 826 will be called snd-virtuoso.
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
93static void wm8776_write(struct oxygen *chip, 101static 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
112static void wm8776_write_i2c(struct oxygen *chip,
113 unsigned int reg, unsigned int value)
114{
115 oxygen_write_i2c(chip, I2C_DEVICE_WM8776,
116 (reg << 1) | (value >> 8), value);
117}
118
119static void wm8776_write(struct oxygen *chip,
120 unsigned int reg, unsigned int value)
121{
122 struct xonar_wm87x6 *data = chip->model_data;
123
124 if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
125 OXYGEN_FUNCTION_SPI)
126 wm8776_write_spi(chip, reg, value);
127 else
128 wm8776_write_i2c(chip, reg, value);
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
295static 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
270static void xonar_ds_cleanup(struct oxygen *chip) 315static 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
321static 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
276static void xonar_ds_suspend(struct oxygen *chip) 329static void xonar_ds_suspend(struct oxygen *chip)
277{ 330{
278 xonar_ds_cleanup(chip); 331 xonar_ds_cleanup(chip);
279} 332}
280 333
334static void xonar_hdav_slim_suspend(struct oxygen *chip)
335{
336 xonar_hdav_slim_cleanup(chip);
337}
338
281static void xonar_ds_resume(struct oxygen *chip) 339static 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
347static 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
289static void wm8776_adc_hardware_filter(unsigned int channel, 356static 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
370static 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
303static void set_wm87x6_dac_params(struct oxygen *chip, 377static 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
393static 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
319static void update_wm8776_volume(struct oxygen *chip) 401static 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};
1092static 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};
1010static const struct snd_kcontrol_new lc_controls[] = { 1139static 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
1182static 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
1053static int xonar_ds_mixer_init(struct oxygen *chip) 1202static 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
1227static 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
1087static void dump_wm8776_registers(struct oxygen *chip, 1245static 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
1306static 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
1148int __devinit get_xonar_wm87x6_model(struct oxygen *chip, 1338int __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 }