Merge branch 'topic/oxygen' into for-linus

This commit is contained in:
Takashi Iwai 2009-03-24 00:36:17 +01:00
commit b54fc8dd2c
8 changed files with 507 additions and 116 deletions

View file

@ -1859,7 +1859,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
-------------------
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.

View file

@ -764,7 +764,8 @@ config SND_VIRTUOSO
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

View file

@ -45,6 +45,7 @@ MODULE_PARM_DESC(enable, "enable card");
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 @@ static const struct oxygen_model model_hifier = {
.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 @@ static const struct oxygen_model model_hifier = {
.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 @@ static int __devinit hifier_probe(struct pci_dev *pci,
++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;

View file

@ -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 @@ MODULE_PARM_DESC(enable, "enable card");
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 @@ static struct pci_device_id oxygen_ids[] __devinitdata = {
{ 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 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
#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 @@ static void meridian_init(struct oxygen *chip)
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 @@ static void meridian_resume(struct oxygen *chip)
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 void set_ak5385_params(struct oxygen *chip,
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 @@ static const struct oxygen_model model_generic = {
.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 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci,
++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;

View file

@ -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 @@ enum {
.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 @@ struct oxygen_model {
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 @@ struct oxygen_model {
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 @@ struct oxygen {
/* 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_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);
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)
{

View file

@ -254,3 +254,34 @@ void oxygen_write_uart(struct oxygen *chip, u8 data)
_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");
}

View file

@ -34,6 +34,7 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
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 @@ static void oxygen_proc_init(struct oxygen *chip)
#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 @@ static void oxygen_card_free(struct snd_card *card)
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 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
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 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
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 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
}
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;

View file

@ -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;