summaryrefslogtreecommitdiffstats
path: root/sound/pci/oxygen/virtuoso.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2009-03-26 15:23:24 -0700
committerDavid S. Miller <davem@davemloft.net>2009-03-26 15:23:24 -0700
commit08abe18af1f78ee80c3c3a5ac47c3e0ae0beadf6 (patch)
tree2be39bf8942edca1bcec735145e144a682ca9cd3 /sound/pci/oxygen/virtuoso.c
parentf0de70f8bb56952f6e016a65a8a8d006918f5bf6 (diff)
parent0384e2959127a56d0640505d004d8dd92f9c29f5 (diff)
downloadlinux-08abe18af1f78ee80c3c3a5ac47c3e0ae0beadf6.tar.bz2
Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
Conflicts: drivers/net/wimax/i2400m/usb-notif.c
Diffstat (limited to 'sound/pci/oxygen/virtuoso.c')
-rw-r--r--sound/pci/oxygen/virtuoso.c339
1 files changed, 270 insertions, 69 deletions
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 6c870c12a177..bc5ce11c8b14 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 @@ enum {
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 @@ static struct pci_device_id xonar_ids[] __devinitdata = {
{ 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 @@ MODULE_DEVICE_TABLE(pci, xonar_ids);
#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 @@ static void xonar_d2_init(struct oxygen *chip)
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 @@ static void xonar_d2_init(struct oxygen *chip)
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 @@ static void xonar_d1_init(struct oxygen *chip)
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 @@ static void xonar_d1_init(struct oxygen *chip)
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 @@ static void xonar_hdav_init(struct oxygen *chip)
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 @@ static void xonar_hdav_init(struct oxygen *chip)
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 @@ static void xonar_hdav_cleanup(struct oxygen *chip)
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 @@ static void xonar_hdav_suspend(struct oxygen *chip)
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 @@ static void xonar_hdav_resume(struct oxygen *chip)
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 @@ static const struct snd_kcontrol_new front_panel_switch = {
.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 void xonar_line_mic_ac97_switch(struct oxygen *chip,
}
}
-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 @@ static int xonar_d1_control_filter(struct snd_kcontrol_new *template)
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 @@ static int xonar_d1_mixer_init(struct oxygen *chip)
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 @@ static const struct oxygen_model model_xonar_d2 = {
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_d2 = {
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 @@ static const struct oxygen_model model_xonar_d1 = {
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_d1 = {
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 @@ static const struct oxygen_model model_xonar_hdav = {
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 @@ static int __devinit xonar_probe(struct pci_dev *pci,
[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 @@ static int __devinit xonar_probe(struct pci_dev *pci,
++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;