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>
This commit is contained in:
Clemens Ladisch 2011-01-10 16:23:57 +01:00 committed by Takashi Iwai
parent 66410bfdf1
commit b532d6b8d3
5 changed files with 209 additions and 19 deletions

View file

@ -2004,9 +2004,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
Module snd-virtuoso
-------------------
Module for sound cards based on the Asus AV100/AV200 chips,
i.e., Xonar D1, DX, D2, D2X, DS, HDAV1.3 (Deluxe), Essence ST
(Deluxe) and Essence STX.
Module for sound cards based on the Asus AV66/AV100/AV200 chips,
i.e., Xonar D1, DX, D2, D2X, DS, Essence ST (Deluxe), Essence STX,
HDAV1.3 (Deluxe), and HDAV1.3 Slim.
This module supports autoprobe and multiple cards.

View file

@ -819,8 +819,8 @@ config SND_VIRTUOSO
Say Y here to include support for sound cards based on the
Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
Essence ST (Deluxe), and Essence STX.
Support for the HDAV1.3 (Deluxe) is experimental; for the
HDAV1.3 Slim and Xense, missing.
Support for the HDAV1.3 (Deluxe) and HDAV1.3 Slim is experimental;
for the Xense, missing.
To compile this driver as a module, choose M here: the module
will be called snd-virtuoso.

View file

@ -1,5 +1,5 @@
/*
* helper functions for HDMI models (Xonar HDAV1.3)
* helper functions for HDMI models (Xonar HDAV1.3/HDAV1.3 Slim)
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*

View file

@ -1152,9 +1152,6 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
chip->model.resume = xonar_stx_resume;
chip->model.set_dac_params = set_pcm1796_params;
break;
case 0x835e:
snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n");
return -ENODEV;
default:
return -EINVAL;
}

View file

@ -1,5 +1,5 @@
/*
* card driver for models with WM8776/WM8766 DACs (Xonar DS)
* card driver for models with WM8776/WM8766 DACs (Xonar DS/HDAV1.3 Slim)
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
@ -77,6 +77,13 @@
#define GPIO_DS_OUTPUT_FRONTLR 0x0080
#define GPIO_DS_OUTPUT_ENABLE 0x0100
#define GPIO_SLIM_HDMI_DISABLE 0x0001
#define GPIO_SLIM_OUTPUT_ENABLE 0x0002
#define GPIO_SLIM_FIRMWARE_CLK 0x0040
#define GPIO_SLIM_FIRMWARE_DATA 0x0080
#define I2C_DEVICE_WM8776 0x34 /* 001101, 0, /W=0 */
#define LC_CONTROL_LIMITER 0x40000000
#define LC_CONTROL_ALC 0x20000000
@ -88,19 +95,37 @@ struct xonar_wm87x6 {
struct snd_kcontrol *mic_adcmux_control;
struct snd_kcontrol *lc_controls[13];
struct snd_jack *hp_jack;
struct xonar_hdmi hdmi;
};
static void wm8776_write(struct oxygen *chip,
unsigned int reg, unsigned int value)
static void wm8776_write_spi(struct oxygen *chip,
unsigned int reg, unsigned int value)
{
struct xonar_wm87x6 *data = chip->model_data;
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
(1 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value);
}
static void wm8776_write_i2c(struct oxygen *chip,
unsigned int reg, unsigned int value)
{
oxygen_write_i2c(chip, I2C_DEVICE_WM8776,
(reg << 1) | (value >> 8), value);
}
static void wm8776_write(struct oxygen *chip,
unsigned int reg, unsigned int value)
{
struct xonar_wm87x6 *data = chip->model_data;
if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
OXYGEN_FUNCTION_SPI)
wm8776_write_spi(chip, reg, value);
else
wm8776_write_i2c(chip, reg, value);
if (reg < ARRAY_SIZE(data->wm8776_regs)) {
if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)
value &= ~WM8776_UPDATE;
@ -267,17 +292,50 @@ static void xonar_ds_init(struct oxygen *chip)
snd_component_add(chip->card, "WM8766");
}
static void xonar_hdav_slim_init(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
data->generic.anti_pop_delay = 300;
data->generic.output_enable_bit = GPIO_SLIM_OUTPUT_ENABLE;
wm8776_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
GPIO_SLIM_HDMI_DISABLE |
GPIO_SLIM_FIRMWARE_CLK |
GPIO_SLIM_FIRMWARE_DATA);
xonar_hdmi_init(chip, &data->hdmi);
xonar_enable_output(chip);
snd_component_add(chip->card, "WM8776");
}
static void xonar_ds_cleanup(struct oxygen *chip)
{
xonar_disable_output(chip);
wm8776_write(chip, WM8776_RESET, 0);
}
static void xonar_hdav_slim_cleanup(struct oxygen *chip)
{
xonar_hdmi_cleanup(chip);
xonar_disable_output(chip);
wm8776_write(chip, WM8776_RESET, 0);
msleep(2);
}
static void xonar_ds_suspend(struct oxygen *chip)
{
xonar_ds_cleanup(chip);
}
static void xonar_hdav_slim_suspend(struct oxygen *chip)
{
xonar_hdav_slim_cleanup(chip);
}
static void xonar_ds_resume(struct oxygen *chip)
{
wm8776_registers_init(chip);
@ -286,6 +344,15 @@ static void xonar_ds_resume(struct oxygen *chip)
xonar_ds_handle_hp_jack(chip);
}
static void xonar_hdav_slim_resume(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
wm8776_registers_init(chip);
xonar_hdmi_resume(chip, &data->hdmi);
xonar_enable_output(chip);
}
static void wm8776_adc_hardware_filter(unsigned int channel,
struct snd_pcm_hardware *hardware)
{
@ -300,6 +367,13 @@ static void wm8776_adc_hardware_filter(unsigned int channel,
}
}
static void xonar_hdav_slim_hardware_filter(unsigned int channel,
struct snd_pcm_hardware *hardware)
{
wm8776_adc_hardware_filter(channel, hardware);
xonar_hdmi_pcm_hardware_filter(channel, hardware);
}
static void set_wm87x6_dac_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
@ -316,6 +390,14 @@ static void set_wm8776_adc_params(struct oxygen *chip,
wm8776_write_cached(chip, WM8776_MSTRCTRL, reg);
}
static void set_hdav_slim_dac_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
struct xonar_wm87x6 *data = chip->model_data;
xonar_set_hdmi_params(chip, &data->hdmi, params);
}
static void update_wm8776_volume(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
@ -1007,6 +1089,53 @@ static const struct snd_kcontrol_new ds_controls[] = {
.private_value = 0,
},
};
static const struct snd_kcontrol_new hdav_slim_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "HDMI Playback Switch",
.info = snd_ctl_boolean_mono_info,
.get = xonar_gpio_bit_switch_get,
.put = xonar_gpio_bit_switch_put,
.private_value = GPIO_SLIM_HDMI_DISABLE | XONAR_GPIO_BIT_INVERT,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Playback Volume",
.info = wm8776_hp_vol_info,
.get = wm8776_hp_vol_get,
.put = wm8776_hp_vol_put,
.tlv = { .p = wm8776_hp_db_scale },
},
WM8776_BIT_SWITCH("Headphone Playback Switch",
WM8776_PWRDOWN, WM8776_HPPD, 1, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Capture Volume",
.info = wm8776_input_vol_info,
.get = wm8776_input_vol_get,
.put = wm8776_input_vol_put,
.tlv = { .p = wm8776_adc_db_scale },
},
WM8776_BIT_SWITCH("Mic Capture Switch",
WM8776_ADCMUX, 1 << 0, 0, 0),
WM8776_BIT_SWITCH("Aux Capture Switch",
WM8776_ADCMUX, 1 << 1, 0, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Filter Capture Enum",
.info = hpf_info,
.get = hpf_get,
.put = hpf_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Level Control Capture Enum",
.info = wm8776_level_control_info,
.get = wm8776_level_control_get,
.put = wm8776_level_control_put,
.private_value = 0,
},
};
static const struct snd_kcontrol_new lc_controls[] = {
WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
@ -1050,6 +1179,26 @@ static const struct snd_kcontrol_new lc_controls[] = {
LC_CONTROL_ALC, wm8776_ngth_db_scale),
};
static int add_lc_controls(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
unsigned int i;
struct snd_kcontrol *ctl;
int err;
BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
ctl = snd_ctl_new1(&lc_controls[i], chip);
if (!ctl)
return -ENOMEM;
err = snd_ctl_add(chip->card, ctl);
if (err < 0)
return err;
data->lc_controls[i] = ctl;
}
return 0;
}
static int xonar_ds_mixer_init(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
@ -1071,17 +1220,26 @@ static int xonar_ds_mixer_init(struct oxygen *chip)
}
if (!data->line_adcmux_control || !data->mic_adcmux_control)
return -ENXIO;
BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
ctl = snd_ctl_new1(&lc_controls[i], chip);
return add_lc_controls(chip);
}
static int xonar_hdav_slim_mixer_init(struct oxygen *chip)
{
unsigned int i;
struct snd_kcontrol *ctl;
int err;
for (i = 0; i < ARRAY_SIZE(hdav_slim_controls); ++i) {
ctl = snd_ctl_new1(&hdav_slim_controls[i], chip);
if (!ctl)
return -ENOMEM;
err = snd_ctl_add(chip->card, ctl);
if (err < 0)
return err;
data->lc_controls[i] = ctl;
}
return 0;
return add_lc_controls(chip);
}
static void dump_wm8776_registers(struct oxygen *chip,
@ -1145,6 +1303,38 @@ static const struct oxygen_model model_xonar_ds = {
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
static const struct oxygen_model model_xonar_hdav_slim = {
.shortname = "Xonar HDAV1.3 Slim",
.longname = "Asus Virtuoso 200",
.chip = "AV200",
.init = xonar_hdav_slim_init,
.mixer_init = xonar_hdav_slim_mixer_init,
.cleanup = xonar_hdav_slim_cleanup,
.suspend = xonar_hdav_slim_suspend,
.resume = xonar_hdav_slim_resume,
.pcm_hardware_filter = xonar_hdav_slim_hardware_filter,
.set_dac_params = set_hdav_slim_dac_params,
.set_adc_params = set_wm8776_adc_params,
.update_dac_volume = update_wm8776_volume,
.update_dac_mute = update_wm8776_mute,
.uart_input = xonar_hdmi_uart_input,
.dump_registers = dump_wm8776_registers,
.dac_tlv = wm87x6_dac_db_scale,
.model_data_size = sizeof(struct xonar_wm87x6),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_1,
.dac_channels_pcm = 8,
.dac_channels_mixer = 2,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_2WIRE,
.dac_mclks = OXYGEN_MCLKS(256, 256, 128),
.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
int __devinit get_xonar_wm87x6_model(struct oxygen *chip,
const struct pci_device_id *id)
{
@ -1152,6 +1342,9 @@ int __devinit get_xonar_wm87x6_model(struct oxygen *chip,
case 0x838e:
chip->model = model_xonar_ds;
break;
case 0x835e:
chip->model = model_xonar_hdav_slim;
break;
default:
return -EINVAL;
}