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:
parent
66410bfdf1
commit
b532d6b8d3
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue