Commit b532d6b8 authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Takashi Iwai

ALSA: virtuoso: add Xonar HDAV1.3 Slim support

Add experimental support for the Asus Xonar HDAV1.3 Slim sound card.
Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 66410bfd
...@@ -2004,9 +2004,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. ...@@ -2004,9 +2004,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
Module snd-virtuoso Module snd-virtuoso
------------------- -------------------
Module for sound cards based on the Asus AV100/AV200 chips, Module for sound cards based on the Asus AV66/AV100/AV200 chips,
i.e., Xonar D1, DX, D2, D2X, DS, HDAV1.3 (Deluxe), Essence ST i.e., Xonar D1, DX, D2, D2X, DS, Essence ST (Deluxe), Essence STX,
(Deluxe) and Essence STX. HDAV1.3 (Deluxe), and HDAV1.3 Slim.
This module supports autoprobe and multiple cards. This module supports autoprobe and multiple cards.
......
...@@ -819,8 +819,8 @@ config SND_VIRTUOSO ...@@ -819,8 +819,8 @@ config SND_VIRTUOSO
Say Y here to include support for sound cards based on the 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, Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
Essence ST (Deluxe), and Essence STX. Essence ST (Deluxe), and Essence STX.
Support for the HDAV1.3 (Deluxe) is experimental; for the Support for the HDAV1.3 (Deluxe) and HDAV1.3 Slim is experimental;
HDAV1.3 Slim and Xense, missing. for the Xense, missing.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called snd-virtuoso. will be called snd-virtuoso.
......
/* /*
* 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> * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
* *
......
...@@ -1152,9 +1152,6 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip, ...@@ -1152,9 +1152,6 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
chip->model.resume = xonar_stx_resume; chip->model.resume = xonar_stx_resume;
chip->model.set_dac_params = set_pcm1796_params; chip->model.set_dac_params = set_pcm1796_params;
break; break;
case 0x835e:
snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n");
return -ENODEV;
default: default:
return -EINVAL; return -EINVAL;
} }
......
/* /*
* 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> * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
* *
...@@ -77,6 +77,13 @@ ...@@ -77,6 +77,13 @@
#define GPIO_DS_OUTPUT_FRONTLR 0x0080 #define GPIO_DS_OUTPUT_FRONTLR 0x0080
#define GPIO_DS_OUTPUT_ENABLE 0x0100 #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_LIMITER 0x40000000
#define LC_CONTROL_ALC 0x20000000 #define LC_CONTROL_ALC 0x20000000
...@@ -88,19 +95,37 @@ struct xonar_wm87x6 { ...@@ -88,19 +95,37 @@ struct xonar_wm87x6 {
struct snd_kcontrol *mic_adcmux_control; struct snd_kcontrol *mic_adcmux_control;
struct snd_kcontrol *lc_controls[13]; struct snd_kcontrol *lc_controls[13];
struct snd_jack *hp_jack; struct snd_jack *hp_jack;
struct xonar_hdmi hdmi;
}; };
static void wm8776_write(struct oxygen *chip, static void wm8776_write_spi(struct oxygen *chip,
unsigned int reg, unsigned int value) unsigned int reg, unsigned int value)
{ {
struct xonar_wm87x6 *data = chip->model_data;
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 | OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 | OXYGEN_SPI_CLOCK_160 |
(1 << OXYGEN_SPI_CODEC_SHIFT) | (1 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO, OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value); (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 < ARRAY_SIZE(data->wm8776_regs)) {
if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER) if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)
value &= ~WM8776_UPDATE; value &= ~WM8776_UPDATE;
...@@ -267,17 +292,50 @@ static void xonar_ds_init(struct oxygen *chip) ...@@ -267,17 +292,50 @@ static void xonar_ds_init(struct oxygen *chip)
snd_component_add(chip->card, "WM8766"); 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) static void xonar_ds_cleanup(struct oxygen *chip)
{ {
xonar_disable_output(chip); xonar_disable_output(chip);
wm8776_write(chip, WM8776_RESET, 0); 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) static void xonar_ds_suspend(struct oxygen *chip)
{ {
xonar_ds_cleanup(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) static void xonar_ds_resume(struct oxygen *chip)
{ {
wm8776_registers_init(chip); wm8776_registers_init(chip);
...@@ -286,6 +344,15 @@ static void xonar_ds_resume(struct oxygen *chip) ...@@ -286,6 +344,15 @@ static void xonar_ds_resume(struct oxygen *chip)
xonar_ds_handle_hp_jack(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, static void wm8776_adc_hardware_filter(unsigned int channel,
struct snd_pcm_hardware *hardware) struct snd_pcm_hardware *hardware)
{ {
...@@ -300,6 +367,13 @@ static void wm8776_adc_hardware_filter(unsigned int channel, ...@@ -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, static void set_wm87x6_dac_params(struct oxygen *chip,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
...@@ -316,6 +390,14 @@ static void set_wm8776_adc_params(struct oxygen *chip, ...@@ -316,6 +390,14 @@ static void set_wm8776_adc_params(struct oxygen *chip,
wm8776_write_cached(chip, WM8776_MSTRCTRL, reg); 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) static void update_wm8776_volume(struct oxygen *chip)
{ {
struct xonar_wm87x6 *data = chip->model_data; struct xonar_wm87x6 *data = chip->model_data;
...@@ -1007,6 +1089,53 @@ static const struct snd_kcontrol_new ds_controls[] = { ...@@ -1007,6 +1089,53 @@ static const struct snd_kcontrol_new ds_controls[] = {
.private_value = 0, .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[] = { static const struct snd_kcontrol_new lc_controls[] = {
WM8776_FIELD_CTL_VOLUME("Limiter Threshold", WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf, WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
...@@ -1050,6 +1179,26 @@ static const struct snd_kcontrol_new lc_controls[] = { ...@@ -1050,6 +1179,26 @@ static const struct snd_kcontrol_new lc_controls[] = {
LC_CONTROL_ALC, wm8776_ngth_db_scale), 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) static int xonar_ds_mixer_init(struct oxygen *chip)
{ {
struct xonar_wm87x6 *data = chip->model_data; struct xonar_wm87x6 *data = chip->model_data;
...@@ -1071,17 +1220,26 @@ static int xonar_ds_mixer_init(struct oxygen *chip) ...@@ -1071,17 +1220,26 @@ static int xonar_ds_mixer_init(struct oxygen *chip)
} }
if (!data->line_adcmux_control || !data->mic_adcmux_control) if (!data->line_adcmux_control || !data->mic_adcmux_control)
return -ENXIO; return -ENXIO;
BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) { return add_lc_controls(chip);
ctl = snd_ctl_new1(&lc_controls[i], 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) if (!ctl)
return -ENOMEM; return -ENOMEM;
err = snd_ctl_add(chip->card, ctl); err = snd_ctl_add(chip->card, ctl);
if (err < 0) if (err < 0)
return err; return err;
data->lc_controls[i] = ctl;
} }
return 0;
return add_lc_controls(chip);
} }
static void dump_wm8776_registers(struct oxygen *chip, static void dump_wm8776_registers(struct oxygen *chip,
...@@ -1145,6 +1303,38 @@ static const struct oxygen_model model_xonar_ds = { ...@@ -1145,6 +1303,38 @@ static const struct oxygen_model model_xonar_ds = {
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .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, int __devinit get_xonar_wm87x6_model(struct oxygen *chip,
const struct pci_device_id *id) const struct pci_device_id *id)
{ {
...@@ -1152,6 +1342,9 @@ int __devinit get_xonar_wm87x6_model(struct oxygen *chip, ...@@ -1152,6 +1342,9 @@ int __devinit get_xonar_wm87x6_model(struct oxygen *chip,
case 0x838e: case 0x838e:
chip->model = model_xonar_ds; chip->model = model_xonar_ds;
break; break;
case 0x835e:
chip->model = model_xonar_hdav_slim;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment