Merge branch 'topic/oxygen' into for-linus
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index c911c20..012858d 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -1859,7 +1859,7 @@
   -------------------
 
     Module for sound cards based on the Asus AV100/AV200 chips,
-    i.e., Xonar D1, DX, D2, D2X and HDAV1.3 (Deluxe).
+    i.e., Xonar D1, DX, D2, D2X, HDAV1.3 (Deluxe), and Essence STX.
 
     This module supports autoprobe and multiple cards.
 
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 9387ab0..ca25e61 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -764,7 +764,8 @@
 	select SND_OXYGEN_LIB
 	help
 	  Say Y here to include support for sound cards based on the
-	  Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2 and D2X.
+	  Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, and
+	  Essence STX.
 	  Support for the HDAV1.3 (Deluxe) is very experimental.
 
 	  To compile this driver as a module, choose M here: the module
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
index 1ab833f..84ef131 100644
--- a/sound/pci/oxygen/hifier.c
+++ b/sound/pci/oxygen/hifier.c
@@ -45,6 +45,7 @@
 static struct pci_device_id hifier_ids[] __devinitdata = {
 	{ OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
 	{ OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
+	{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, hifier_ids);
@@ -151,7 +152,6 @@
 	.shortname = "C-Media CMI8787",
 	.longname = "C-Media Oxygen HD Audio",
 	.chip = "CMI8788",
-	.owner = THIS_MODULE,
 	.init = hifier_init,
 	.control_filter = hifier_control_filter,
 	.cleanup = hifier_cleanup,
@@ -173,6 +173,13 @@
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 
+static int __devinit get_hifier_model(struct oxygen *chip,
+				      const struct pci_device_id *id)
+{
+	chip->model = model_hifier;
+	return 0;
+}
+
 static int __devinit hifier_probe(struct pci_dev *pci,
 				  const struct pci_device_id *pci_id)
 {
@@ -185,7 +192,8 @@
 		++dev;
 		return -ENOENT;
 	}
-	err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier, 0);
+	err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
+			       hifier_ids, get_hifier_model);
 	if (err >= 0)
 		++dev;
 	return err;
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index de999c6..72db4c3 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -1,5 +1,5 @@
 /*
- * C-Media CMI8788 driver for C-Media's reference design and for the X-Meridian
+ * C-Media CMI8788 driver for C-Media's reference design and similar models
  *
  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  *
@@ -26,6 +26,7 @@
  *
  * GPIO 0 -> DFS0 of AK5385
  * GPIO 1 -> DFS1 of AK5385
+ * GPIO 8 -> enable headphone amplifier on HT-Omega models
  */
 
 #include <linux/delay.h>
@@ -61,7 +62,8 @@
 enum {
 	MODEL_CMEDIA_REF,	/* C-Media's reference design */
 	MODEL_MERIDIAN,		/* AuzenTech X-Meridian */
-	MODEL_HALO,		/* HT-Omega Claro halo */
+	MODEL_CLARO,		/* HT-Omega Claro */
+	MODEL_CLARO_HALO,	/* HT-Omega Claro halo */
 };
 
 static struct pci_device_id oxygen_ids[] __devinitdata = {
@@ -74,8 +76,8 @@
 	{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
 	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
-	{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CMEDIA_REF },
-	{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_HALO },
+	{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
+	{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, oxygen_ids);
@@ -86,6 +88,8 @@
 #define GPIO_AK5385_DFS_DOUBLE	0x0001
 #define GPIO_AK5385_DFS_QUAD	0x0002
 
+#define GPIO_CLARO_HP		0x0100
+
 struct generic_data {
 	u8 ak4396_ctl2;
 	u16 saved_wm8785_registers[2];
@@ -196,10 +200,46 @@
 	ak5385_init(chip);
 }
 
+static void claro_enable_hp(struct oxygen *chip)
+{
+	msleep(300);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_HP);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP);
+}
+
+static void claro_init(struct oxygen *chip)
+{
+	ak4396_init(chip);
+	wm8785_init(chip);
+	claro_enable_hp(chip);
+}
+
+static void claro_halo_init(struct oxygen *chip)
+{
+	ak4396_init(chip);
+	ak5385_init(chip);
+	claro_enable_hp(chip);
+}
+
 static void generic_cleanup(struct oxygen *chip)
 {
 }
 
+static void claro_disable_hp(struct oxygen *chip)
+{
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP);
+}
+
+static void claro_cleanup(struct oxygen *chip)
+{
+	claro_disable_hp(chip);
+}
+
+static void claro_suspend(struct oxygen *chip)
+{
+	claro_disable_hp(chip);
+}
+
 static void generic_resume(struct oxygen *chip)
 {
 	ak4396_registers_init(chip);
@@ -211,6 +251,12 @@
 	ak4396_registers_init(chip);
 }
 
+static void claro_resume(struct oxygen *chip)
+{
+	ak4396_registers_init(chip);
+	claro_enable_hp(chip);
+}
+
 static void set_ak4396_params(struct oxygen *chip,
 			      struct snd_pcm_hw_params *params)
 {
@@ -293,30 +339,10 @@
 
 static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
 
-static int generic_probe(struct oxygen *chip, unsigned long driver_data)
-{
-	if (driver_data == MODEL_MERIDIAN) {
-		chip->model.init = meridian_init;
-		chip->model.resume = meridian_resume;
-		chip->model.set_adc_params = set_ak5385_params;
-		chip->model.device_config = PLAYBACK_0_TO_I2S |
-					    PLAYBACK_1_TO_SPDIF |
-					    CAPTURE_0_FROM_I2S_2 |
-					    CAPTURE_1_FROM_SPDIF;
-	}
-	if (driver_data == MODEL_MERIDIAN || driver_data == MODEL_HALO) {
-		chip->model.misc_flags = OXYGEN_MISC_MIDI;
-		chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
-	}
-	return 0;
-}
-
 static const struct oxygen_model model_generic = {
 	.shortname = "C-Media CMI8788",
 	.longname = "C-Media Oxygen HD Audio",
 	.chip = "CMI8788",
-	.owner = THIS_MODULE,
-	.probe = generic_probe,
 	.init = generic_init,
 	.cleanup = generic_cleanup,
 	.resume = generic_resume,
@@ -341,6 +367,42 @@
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 
+static int __devinit get_oxygen_model(struct oxygen *chip,
+				      const struct pci_device_id *id)
+{
+	chip->model = model_generic;
+	switch (id->driver_data) {
+	case MODEL_MERIDIAN:
+		chip->model.init = meridian_init;
+		chip->model.resume = meridian_resume;
+		chip->model.set_adc_params = set_ak5385_params;
+		chip->model.device_config = PLAYBACK_0_TO_I2S |
+					    PLAYBACK_1_TO_SPDIF |
+					    CAPTURE_0_FROM_I2S_2 |
+					    CAPTURE_1_FROM_SPDIF;
+		break;
+	case MODEL_CLARO:
+		chip->model.init = claro_init;
+		chip->model.cleanup = claro_cleanup;
+		chip->model.suspend = claro_suspend;
+		chip->model.resume = claro_resume;
+		break;
+	case MODEL_CLARO_HALO:
+		chip->model.init = claro_halo_init;
+		chip->model.cleanup = claro_cleanup;
+		chip->model.suspend = claro_suspend;
+		chip->model.resume = claro_resume;
+		chip->model.set_adc_params = set_ak5385_params;
+		break;
+	}
+	if (id->driver_data == MODEL_MERIDIAN ||
+	    id->driver_data == MODEL_CLARO_HALO) {
+		chip->model.misc_flags = OXYGEN_MISC_MIDI;
+		chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
+	}
+	return 0;
+}
+
 static int __devinit generic_oxygen_probe(struct pci_dev *pci,
 					  const struct pci_device_id *pci_id)
 {
@@ -353,8 +415,8 @@
 		++dev;
 		return -ENOENT;
 	}
-	err = oxygen_pci_probe(pci, index[dev], id[dev],
-			       &model_generic, pci_id->driver_data);
+	err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
+			       oxygen_ids, get_oxygen_model);
 	if (err >= 0)
 		++dev;
 	return err;
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index 19107c6..bd615db 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -18,6 +18,8 @@
 
 #define OXYGEN_IO_SIZE	0x100
 
+#define OXYGEN_EEPROM_ID	0x434d	/* "CM" */
+
 /* model-specific configuration of outputs/inputs */
 #define PLAYBACK_0_TO_I2S	0x0001
      /* PLAYBACK_0_TO_AC97_0		not implemented */
@@ -49,7 +51,13 @@
 	.subvendor = sv, \
 	.subdevice = sd
 
+#define BROKEN_EEPROM_DRIVER_DATA ((unsigned long)-1)
+#define OXYGEN_PCI_SUBID_BROKEN_EEPROM \
+	OXYGEN_PCI_SUBID(PCI_VENDOR_ID_CMEDIA, 0x8788), \
+	.driver_data = BROKEN_EEPROM_DRIVER_DATA
+
 struct pci_dev;
+struct pci_device_id;
 struct snd_card;
 struct snd_pcm_substream;
 struct snd_pcm_hardware;
@@ -62,8 +70,6 @@
 	const char *shortname;
 	const char *longname;
 	const char *chip;
-	struct module *owner;
-	int (*probe)(struct oxygen *chip, unsigned long driver_data);
 	void (*init)(struct oxygen *chip);
 	int (*control_filter)(struct snd_kcontrol_new *template);
 	int (*mixer_init)(struct oxygen *chip);
@@ -83,6 +89,7 @@
 	void (*ac97_switch)(struct oxygen *chip,
 			    unsigned int reg, unsigned int mute);
 	const unsigned int *dac_tlv;
+	unsigned long private_data;
 	size_t model_data_size;
 	unsigned int device_config;
 	u8 dac_channels;
@@ -134,8 +141,12 @@
 /* oxygen_lib.c */
 
 int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
-		     const struct oxygen_model *model,
-		     unsigned long driver_data);
+		     struct module *owner,
+		     const struct pci_device_id *ids,
+		     int (*get_model)(struct oxygen *chip,
+				      const struct pci_device_id *id
+				     )
+		    );
 void oxygen_pci_remove(struct pci_dev *pci);
 #ifdef CONFIG_PM
 int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
@@ -180,6 +191,9 @@
 void oxygen_reset_uart(struct oxygen *chip);
 void oxygen_write_uart(struct oxygen *chip, u8 data);
 
+u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index);
+void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value);
+
 static inline void oxygen_set_bits8(struct oxygen *chip,
 				    unsigned int reg, u8 value)
 {
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index 3126c4b..c1eb923 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -254,3 +254,34 @@
 	_write_uart(chip, 0, data);
 }
 EXPORT_SYMBOL(oxygen_write_uart);
+
+u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index)
+{
+	unsigned int timeout;
+
+	oxygen_write8(chip, OXYGEN_EEPROM_CONTROL,
+		      index | OXYGEN_EEPROM_DIR_READ);
+	for (timeout = 0; timeout < 100; ++timeout) {
+		udelay(1);
+		if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS)
+		      & OXYGEN_EEPROM_BUSY))
+			break;
+	}
+	return oxygen_read16(chip, OXYGEN_EEPROM_DATA);
+}
+
+void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value)
+{
+	unsigned int timeout;
+
+	oxygen_write16(chip, OXYGEN_EEPROM_DATA, value);
+	oxygen_write8(chip, OXYGEN_EEPROM_CONTROL,
+		      index | OXYGEN_EEPROM_DIR_WRITE);
+	for (timeout = 0; timeout < 10; ++timeout) {
+		msleep(1);
+		if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS)
+		      & OXYGEN_EEPROM_BUSY))
+			return;
+	}
+	snd_printk(KERN_ERR "EEPROM write timeout\n");
+}
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 9c81e0b..312251d 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -34,6 +34,7 @@
 MODULE_DESCRIPTION("C-Media CMI8788 helper library");
 MODULE_LICENSE("GPL v2");
 
+#define DRIVER "oxygen"
 
 static inline int oxygen_uart_input_ready(struct oxygen *chip)
 {
@@ -243,6 +244,62 @@
 #define oxygen_proc_init(chip)
 #endif
 
+static const struct pci_device_id *
+oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
+{
+	u16 subdevice;
+
+	/*
+	 * Make sure the EEPROM pins are available, i.e., not used for SPI.
+	 * (This function is called before we initialize or use SPI.)
+	 */
+	oxygen_clear_bits8(chip, OXYGEN_FUNCTION,
+			   OXYGEN_FUNCTION_ENABLE_SPI_4_5);
+	/*
+	 * Read the subsystem device ID directly from the EEPROM, because the
+	 * chip didn't if the first EEPROM word was overwritten.
+	 */
+	subdevice = oxygen_read_eeprom(chip, 2);
+	/*
+	 * We use only the subsystem device ID for searching because it is
+	 * unique even without the subsystem vendor ID, which may have been
+	 * overwritten in the EEPROM.
+	 */
+	for (; ids->vendor; ++ids)
+		if (ids->subdevice == subdevice &&
+		    ids->driver_data != BROKEN_EEPROM_DRIVER_DATA)
+			return ids;
+	return NULL;
+}
+
+static void oxygen_restore_eeprom(struct oxygen *chip,
+				  const struct pci_device_id *id)
+{
+	if (oxygen_read_eeprom(chip, 0) != OXYGEN_EEPROM_ID) {
+		/*
+		 * This function gets called only when a known card model has
+		 * been detected, i.e., we know there is a valid subsystem
+		 * product ID at index 2 in the EEPROM.  Therefore, we have
+		 * been able to deduce the correct subsystem vendor ID, and
+		 * this is enough information to restore the original EEPROM
+		 * contents.
+		 */
+		oxygen_write_eeprom(chip, 1, id->subvendor);
+		oxygen_write_eeprom(chip, 0, OXYGEN_EEPROM_ID);
+
+		oxygen_set_bits8(chip, OXYGEN_MISC,
+				 OXYGEN_MISC_WRITE_PCI_SUBID);
+		pci_write_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID,
+				      id->subvendor);
+		pci_write_config_word(chip->pci, PCI_SUBSYSTEM_ID,
+				      id->subdevice);
+		oxygen_clear_bits8(chip, OXYGEN_MISC,
+				   OXYGEN_MISC_WRITE_PCI_SUBID);
+
+		snd_printk(KERN_INFO "EEPROM ID restored\n");
+	}
+}
+
 static void oxygen_init(struct oxygen *chip)
 {
 	unsigned int i;
@@ -446,21 +503,26 @@
 		free_irq(chip->irq, chip);
 	flush_scheduled_work();
 	chip->model.cleanup(chip);
+	kfree(chip->model_data);
 	mutex_destroy(&chip->mutex);
 	pci_release_regions(chip->pci);
 	pci_disable_device(chip->pci);
 }
 
 int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
-		     const struct oxygen_model *model,
-		     unsigned long driver_data)
+		     struct module *owner,
+		     const struct pci_device_id *ids,
+		     int (*get_model)(struct oxygen *chip,
+				      const struct pci_device_id *id
+				     )
+		    )
 {
 	struct snd_card *card;
 	struct oxygen *chip;
+	const struct pci_device_id *pci_id;
 	int err;
 
-	err = snd_card_create(index, id, model->owner,
-			      sizeof(*chip) + model->model_data_size, &card);
+	err = snd_card_create(index, id, owner, sizeof(*chip), &card);
 	if (err < 0)
 		return err;
 
@@ -468,8 +530,6 @@
 	chip->card = card;
 	chip->pci = pci;
 	chip->irq = -1;
-	chip->model = *model;
-	chip->model_data = chip + 1;
 	spin_lock_init(&chip->reg_lock);
 	mutex_init(&chip->mutex);
 	INIT_WORK(&chip->spdif_input_bits_work,
@@ -481,7 +541,7 @@
 	if (err < 0)
 		goto err_card;
 
-	err = pci_request_regions(pci, model->chip);
+	err = pci_request_regions(pci, DRIVER);
 	if (err < 0) {
 		snd_printk(KERN_ERR "cannot reserve PCI resources\n");
 		goto err_pci_enable;
@@ -495,20 +555,34 @@
 	}
 	chip->addr = pci_resource_start(pci, 0);
 
+	pci_id = oxygen_search_pci_id(chip, ids);
+	if (!pci_id) {
+		err = -ENODEV;
+		goto err_pci_regions;
+	}
+	oxygen_restore_eeprom(chip, pci_id);
+	err = get_model(chip, pci_id);
+	if (err < 0)
+		goto err_pci_regions;
+
+	if (chip->model.model_data_size) {
+		chip->model_data = kzalloc(chip->model.model_data_size,
+					   GFP_KERNEL);
+		if (!chip->model_data) {
+			err = -ENOMEM;
+			goto err_pci_regions;
+		}
+	}
+
 	pci_set_master(pci);
 	snd_card_set_dev(card, &pci->dev);
 	card->private_free = oxygen_card_free;
 
-	if (chip->model.probe) {
-		err = chip->model.probe(chip, driver_data);
-		if (err < 0)
-			goto err_card;
-	}
 	oxygen_init(chip);
 	chip->model.init(chip);
 
 	err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
-			  chip->model.chip, chip);
+			  DRIVER, chip);
 	if (err < 0) {
 		snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
 		goto err_card;
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 6c870c1..bc5ce11 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -112,6 +112,34 @@
  * CS4362A: AD0 <- 0
  */
 
+/*
+ * Xonar Essence STX
+ * -----------------
+ *
+ * CMI8788:
+ *
+ * I²C <-> PCM1792A
+ *
+ * GPI 0 <- external power present
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 1 -> route HP to front panel (0) or rear jack (1)
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 7 -> route output to speaker jacks (0) or HP (1)
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * PCM1792A:
+ *
+ * AD0 <- 0
+ *
+ * H6 daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 0
+ */
+
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
@@ -152,6 +180,7 @@
 	MODEL_DX,
 	MODEL_HDAV,	/* without daughterboard */
 	MODEL_HDAV_H6,	/* with H6 daughterboard */
+	MODEL_STX,
 };
 
 static struct pci_device_id xonar_ids[] __devinitdata = {
@@ -160,6 +189,8 @@
 	{ OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX },
+	{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, xonar_ids);
@@ -183,12 +214,14 @@
 #define GPIO_HDAV_DB_H6		0x0000
 #define GPIO_HDAV_DB_XX		0x0020
 
+#define GPIO_ST_HP_REAR		0x0002
+#define GPIO_ST_HP		0x0080
+
 #define I2C_DEVICE_PCM1796(i)	(0x98 + ((i) << 1))	/* 10011, ADx=i, /W=0 */
 #define I2C_DEVICE_CS4398	0x9e	/* 10011, AD1=1, AD0=1, /W=0 */
 #define I2C_DEVICE_CS4362A	0x30	/* 001100, AD0=0, /W=0 */
 
 struct xonar_data {
-	unsigned int model;
 	unsigned int anti_pop_delay;
 	unsigned int dacs;
 	u16 output_enable_bit;
@@ -334,15 +367,9 @@
 	struct xonar_data *data = chip->model_data;
 
 	data->anti_pop_delay = 300;
+	data->dacs = 4;
 	data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
 	data->pcm1796_oversampling = PCM1796_OS_64;
-	if (data->model == MODEL_D2X) {
-		data->ext_power_reg = OXYGEN_GPIO_DATA;
-		data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
-		data->ext_power_bit = GPIO_D2X_EXT_POWER;
-		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
-				    GPIO_D2X_EXT_POWER);
-	}
 
 	pcm1796_init(chip);
 
@@ -355,6 +382,18 @@
 	snd_component_add(chip->card, "CS5381");
 }
 
+static void xonar_d2x_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	data->ext_power_reg = OXYGEN_GPIO_DATA;
+	data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
+	data->ext_power_bit = GPIO_D2X_EXT_POWER;
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER);
+
+	xonar_d2_init(chip);
+}
+
 static void update_cs4362a_volumes(struct oxygen *chip)
 {
 	u8 mute;
@@ -422,11 +461,6 @@
 	data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
 	data->cs4362a_fm = CS4362A_FM_SINGLE |
 		CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
-	if (data->model == MODEL_DX) {
-		data->ext_power_reg = OXYGEN_GPI_DATA;
-		data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
-		data->ext_power_bit = GPI_DX_EXT_POWER;
-	}
 
 	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
 		       OXYGEN_2WIRE_LENGTH_8 |
@@ -447,6 +481,17 @@
 	snd_component_add(chip->card, "CS5361");
 }
 
+static void xonar_dx_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	data->ext_power_reg = OXYGEN_GPI_DATA;
+	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+	data->ext_power_bit = GPI_DX_EXT_POWER;
+
+	xonar_d1_init(chip);
+}
+
 static void xonar_hdav_init(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
@@ -458,6 +503,7 @@
 		       OXYGEN_2WIRE_SPEED_FAST);
 
 	data->anti_pop_delay = 100;
+	data->dacs = chip->model.private_data == MODEL_HDAV_H6 ? 4 : 1;
 	data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
 	data->ext_power_reg = OXYGEN_GPI_DATA;
 	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
@@ -484,6 +530,36 @@
 	snd_component_add(chip->card, "CS5381");
 }
 
+static void xonar_stx_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+		       OXYGEN_2WIRE_LENGTH_8 |
+		       OXYGEN_2WIRE_INTERRUPT_MASK |
+		       OXYGEN_2WIRE_SPEED_FAST);
+
+	data->anti_pop_delay = 100;
+	data->dacs = 1;
+	data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
+	data->ext_power_reg = OXYGEN_GPI_DATA;
+	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+	data->ext_power_bit = GPI_DX_EXT_POWER;
+	data->pcm1796_oversampling = PCM1796_OS_64;
+
+	pcm1796_init(chip);
+
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+			    GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
+
+	xonar_common_init(chip);
+
+	snd_component_add(chip->card, "PCM1792A");
+	snd_component_add(chip->card, "CS5381");
+}
+
 static void xonar_disable_output(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
@@ -511,6 +587,11 @@
 	xonar_disable_output(chip);
 }
 
+static void xonar_st_cleanup(struct oxygen *chip)
+{
+	xonar_disable_output(chip);
+}
+
 static void xonar_d2_suspend(struct oxygen *chip)
 {
 	xonar_d2_cleanup(chip);
@@ -527,6 +608,11 @@
 	msleep(2);
 }
 
+static void xonar_st_suspend(struct oxygen *chip)
+{
+	xonar_st_cleanup(chip);
+}
+
 static void xonar_d2_resume(struct oxygen *chip)
 {
 	pcm1796_init(chip);
@@ -554,6 +640,12 @@
 	xonar_enable_output(chip);
 }
 
+static void xonar_st_resume(struct oxygen *chip)
+{
+	pcm1796_init(chip);
+	xonar_enable_output(chip);
+}
+
 static void xonar_hdav_pcm_hardware_filter(unsigned int channel,
 					   struct snd_pcm_hardware *hardware)
 {
@@ -733,6 +825,72 @@
 	.private_value = GPIO_DX_FRONT_PANEL,
 };
 
+static int st_output_switch_info(struct snd_kcontrol *ctl,
+				 struct snd_ctl_elem_info *info)
+{
+	static const char *const names[3] = {
+		"Speakers", "Headphones", "FP Headphones"
+	};
+
+	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	info->count = 1;
+	info->value.enumerated.items = 3;
+	if (info->value.enumerated.item >= 3)
+		info->value.enumerated.item = 2;
+	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+	return 0;
+}
+
+static int st_output_switch_get(struct snd_kcontrol *ctl,
+				struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	u16 gpio;
+
+	gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+	if (!(gpio & GPIO_ST_HP))
+		value->value.enumerated.item[0] = 0;
+	else if (gpio & GPIO_ST_HP_REAR)
+		value->value.enumerated.item[0] = 1;
+	else
+		value->value.enumerated.item[0] = 2;
+	return 0;
+}
+
+
+static int st_output_switch_put(struct snd_kcontrol *ctl,
+				struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	u16 gpio_old, gpio;
+
+	mutex_lock(&chip->mutex);
+	gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+	gpio = gpio_old;
+	switch (value->value.enumerated.item[0]) {
+	case 0:
+		gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR);
+		break;
+	case 1:
+		gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR;
+		break;
+	case 2:
+		gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR;
+		break;
+	}
+	oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio);
+	mutex_unlock(&chip->mutex);
+	return gpio != gpio_old;
+}
+
+static const struct snd_kcontrol_new st_output_switch = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Analog Output",
+	.info = st_output_switch_info,
+	.get = st_output_switch_get,
+	.put = st_output_switch_put,
+};
+
 static void xonar_line_mic_ac97_switch(struct oxygen *chip,
 				       unsigned int reg, unsigned int mute)
 {
@@ -745,8 +903,8 @@
 	}
 }
 
-static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0);
-static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -12700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -6000, 50, 0);
+static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
 
 static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
 {
@@ -763,6 +921,15 @@
 	return 0;
 }
 
+static int xonar_st_control_filter(struct snd_kcontrol_new *template)
+{
+	if (!strncmp(template->name, "CD Capture ", 11))
+		return 1; /* no CD input */
+	if (!strcmp(template->name, "Stereo Upmixing"))
+		return 1; /* stereo only - we don't need upmixing */
+	return 0;
+}
+
 static int xonar_d2_mixer_init(struct oxygen *chip)
 {
 	return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
@@ -773,51 +940,14 @@
 	return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
 }
 
-static int xonar_model_probe(struct oxygen *chip, unsigned long driver_data)
+static int xonar_st_mixer_init(struct oxygen *chip)
 {
-	static const char *const names[] = {
-		[MODEL_D1]	= "Xonar D1",
-		[MODEL_DX]	= "Xonar DX",
-		[MODEL_D2]	= "Xonar D2",
-		[MODEL_D2X]	= "Xonar D2X",
-		[MODEL_HDAV]	= "Xonar HDAV1.3",
-		[MODEL_HDAV_H6]	= "Xonar HDAV1.3+H6",
-	};
-	static const u8 dacs[] = {
-		[MODEL_D1]	= 2,
-		[MODEL_DX]	= 2,
-		[MODEL_D2]	= 4,
-		[MODEL_D2X]	= 4,
-		[MODEL_HDAV]	= 1,
-		[MODEL_HDAV_H6]	= 4,
-	};
-	struct xonar_data *data = chip->model_data;
-
-	data->model = driver_data;
-	if (data->model == MODEL_HDAV) {
-		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
-				    GPIO_HDAV_DB_MASK);
-		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
-			GPIO_HDAV_DB_MASK) {
-		case GPIO_HDAV_DB_H6:
-			data->model = MODEL_HDAV_H6;
-			break;
-		case GPIO_HDAV_DB_XX:
-			snd_printk(KERN_ERR "unknown daughterboard\n");
-			return -ENODEV;
-		}
-	}
-
-	data->dacs = dacs[data->model];
-	chip->model.shortname = names[data->model];
-	return 0;
+	return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip));
 }
 
 static const struct oxygen_model model_xonar_d2 = {
 	.longname = "Asus Virtuoso 200",
 	.chip = "AV200",
-	.owner = THIS_MODULE,
-	.probe = xonar_model_probe,
 	.init = xonar_d2_init,
 	.control_filter = xonar_d2_control_filter,
 	.mixer_init = xonar_d2_mixer_init,
@@ -837,8 +967,8 @@
 			 MIDI_OUTPUT |
 			 MIDI_INPUT,
 	.dac_channels = 8,
-	.dac_volume_min = 0x0f,
-	.dac_volume_max = 0xff,
+	.dac_volume_min = 255 - 2*60,
+	.dac_volume_max = 255,
 	.misc_flags = OXYGEN_MISC_MIDI,
 	.function_flags = OXYGEN_FUNCTION_SPI |
 			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
@@ -849,8 +979,6 @@
 static const struct oxygen_model model_xonar_d1 = {
 	.longname = "Asus Virtuoso 100",
 	.chip = "AV200",
-	.owner = THIS_MODULE,
-	.probe = xonar_model_probe,
 	.init = xonar_d1_init,
 	.control_filter = xonar_d1_control_filter,
 	.mixer_init = xonar_d1_mixer_init,
@@ -868,7 +996,7 @@
 			 PLAYBACK_1_TO_SPDIF |
 			 CAPTURE_0_FROM_I2S_2,
 	.dac_channels = 8,
-	.dac_volume_min = 0,
+	.dac_volume_min = 127 - 60,
 	.dac_volume_max = 127,
 	.function_flags = OXYGEN_FUNCTION_2WIRE,
 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
@@ -878,8 +1006,6 @@
 static const struct oxygen_model model_xonar_hdav = {
 	.longname = "Asus Virtuoso 200",
 	.chip = "AV200",
-	.owner = THIS_MODULE,
-	.probe = xonar_model_probe,
 	.init = xonar_hdav_init,
 	.cleanup = xonar_hdav_cleanup,
 	.suspend = xonar_hdav_suspend,
@@ -897,16 +1023,43 @@
 			 PLAYBACK_1_TO_SPDIF |
 			 CAPTURE_0_FROM_I2S_2,
 	.dac_channels = 8,
-	.dac_volume_min = 0x0f,
-	.dac_volume_max = 0xff,
+	.dac_volume_min = 255 - 2*60,
+	.dac_volume_max = 255,
 	.misc_flags = OXYGEN_MISC_MIDI,
 	.function_flags = OXYGEN_FUNCTION_2WIRE,
 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 
-static int __devinit xonar_probe(struct pci_dev *pci,
-				 const struct pci_device_id *pci_id)
+static const struct oxygen_model model_xonar_st = {
+	.longname = "Asus Virtuoso 100",
+	.chip = "AV200",
+	.init = xonar_stx_init,
+	.control_filter = xonar_st_control_filter,
+	.mixer_init = xonar_st_mixer_init,
+	.cleanup = xonar_st_cleanup,
+	.suspend = xonar_st_suspend,
+	.resume = xonar_st_resume,
+	.set_dac_params = set_pcm1796_params,
+	.set_adc_params = set_cs53x1_params,
+	.update_dac_volume = update_pcm1796_volume,
+	.update_dac_mute = update_pcm1796_mute,
+	.ac97_switch = xonar_line_mic_ac97_switch,
+	.dac_tlv = pcm1796_db_scale,
+	.model_data_size = sizeof(struct xonar_data),
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_2,
+	.dac_channels = 2,
+	.dac_volume_min = 255 - 2*60,
+	.dac_volume_max = 255,
+	.function_flags = OXYGEN_FUNCTION_2WIRE,
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static int __devinit get_xonar_model(struct oxygen *chip,
+				     const struct pci_device_id *id)
 {
 	static const struct oxygen_model *const models[] = {
 		[MODEL_D1]	= &model_xonar_d1,
@@ -914,7 +1067,57 @@
 		[MODEL_D2]	= &model_xonar_d2,
 		[MODEL_D2X]	= &model_xonar_d2,
 		[MODEL_HDAV]	= &model_xonar_hdav,
+		[MODEL_STX]	= &model_xonar_st,
 	};
+	static const char *const names[] = {
+		[MODEL_D1]	= "Xonar D1",
+		[MODEL_DX]	= "Xonar DX",
+		[MODEL_D2]	= "Xonar D2",
+		[MODEL_D2X]	= "Xonar D2X",
+		[MODEL_HDAV]	= "Xonar HDAV1.3",
+		[MODEL_HDAV_H6]	= "Xonar HDAV1.3+H6",
+		[MODEL_STX]	= "Xonar Essence STX",
+	};
+	unsigned int model = id->driver_data;
+
+	if (model >= ARRAY_SIZE(models) || !models[model])
+		return -EINVAL;
+	chip->model = *models[model];
+
+	switch (model) {
+	case MODEL_D2X:
+		chip->model.init = xonar_d2x_init;
+		break;
+	case MODEL_DX:
+		chip->model.init = xonar_dx_init;
+		break;
+	case MODEL_HDAV:
+		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+				    GPIO_HDAV_DB_MASK);
+		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+			GPIO_HDAV_DB_MASK) {
+		case GPIO_HDAV_DB_H6:
+			model = MODEL_HDAV_H6;
+			break;
+		case GPIO_HDAV_DB_XX:
+			snd_printk(KERN_ERR "unknown daughterboard\n");
+			return -ENODEV;
+		}
+		break;
+	case MODEL_STX:
+		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+				    GPIO_HDAV_DB_MASK);
+		break;
+	}
+
+	chip->model.shortname = names[model];
+	chip->model.private_data = model;
+	return 0;
+}
+
+static int __devinit xonar_probe(struct pci_dev *pci,
+				 const struct pci_device_id *pci_id)
+{
 	static int dev;
 	int err;
 
@@ -924,10 +1127,8 @@
 		++dev;
 		return -ENOENT;
 	}
-	BUG_ON(pci_id->driver_data >= ARRAY_SIZE(models));
-	err = oxygen_pci_probe(pci, index[dev], id[dev],
-			       models[pci_id->driver_data],
-			       pci_id->driver_data);
+	err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
+			       xonar_ids, get_xonar_model);
 	if (err >= 0)
 		++dev;
 	return err;