Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -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.

+2 −2
Original line number Diff line number Diff line
@@ -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 −1
Original line number Diff line number Diff line
/*
 * 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>
 *
+0 −3
Original line number Diff line number Diff line
@@ -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;
	}
+203 −10
Original line number Diff line number Diff line
/*
 * 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,
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;
	}