mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 19:56:18 +00:00
Merge branch 'topic/oxygen' into for-linus
This commit is contained in:
commit
b54fc8dd2c
8 changed files with 507 additions and 116 deletions
|
@ -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,
|
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.
|
This module supports autoprobe and multiple cards.
|
||||||
|
|
||||||
|
|
|
@ -764,7 +764,8 @@ config SND_VIRTUOSO
|
||||||
select SND_OXYGEN_LIB
|
select SND_OXYGEN_LIB
|
||||||
help
|
help
|
||||||
Say Y here to include support for sound cards based on the
|
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.
|
Support for the HDAV1.3 (Deluxe) is very experimental.
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module
|
To compile this driver as a module, choose M here: the module
|
||||||
|
|
|
@ -45,6 +45,7 @@ MODULE_PARM_DESC(enable, "enable card");
|
||||||
static struct pci_device_id hifier_ids[] __devinitdata = {
|
static struct pci_device_id hifier_ids[] __devinitdata = {
|
||||||
{ OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
|
{ OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
|
||||||
{ OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
|
{ OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
|
||||||
|
{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pci, hifier_ids);
|
MODULE_DEVICE_TABLE(pci, hifier_ids);
|
||||||
|
@ -151,7 +152,6 @@ static const struct oxygen_model model_hifier = {
|
||||||
.shortname = "C-Media CMI8787",
|
.shortname = "C-Media CMI8787",
|
||||||
.longname = "C-Media Oxygen HD Audio",
|
.longname = "C-Media Oxygen HD Audio",
|
||||||
.chip = "CMI8788",
|
.chip = "CMI8788",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.init = hifier_init,
|
.init = hifier_init,
|
||||||
.control_filter = hifier_control_filter,
|
.control_filter = hifier_control_filter,
|
||||||
.cleanup = hifier_cleanup,
|
.cleanup = hifier_cleanup,
|
||||||
|
@ -173,6 +173,13 @@ static const struct oxygen_model model_hifier = {
|
||||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
.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,
|
static int __devinit hifier_probe(struct pci_dev *pci,
|
||||||
const struct pci_device_id *pci_id)
|
const struct pci_device_id *pci_id)
|
||||||
{
|
{
|
||||||
|
@ -185,7 +192,8 @@ static int __devinit hifier_probe(struct pci_dev *pci,
|
||||||
++dev;
|
++dev;
|
||||||
return -ENOENT;
|
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)
|
if (err >= 0)
|
||||||
++dev;
|
++dev;
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -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>
|
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||||
*
|
*
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
*
|
*
|
||||||
* GPIO 0 -> DFS0 of AK5385
|
* GPIO 0 -> DFS0 of AK5385
|
||||||
* GPIO 1 -> DFS1 of AK5385
|
* GPIO 1 -> DFS1 of AK5385
|
||||||
|
* GPIO 8 -> enable headphone amplifier on HT-Omega models
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
@ -61,7 +62,8 @@ MODULE_PARM_DESC(enable, "enable card");
|
||||||
enum {
|
enum {
|
||||||
MODEL_CMEDIA_REF, /* C-Media's reference design */
|
MODEL_CMEDIA_REF, /* C-Media's reference design */
|
||||||
MODEL_MERIDIAN, /* AuzenTech X-Meridian */
|
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 = {
|
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(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
|
||||||
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .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(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
|
||||||
{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CMEDIA_REF },
|
{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
|
||||||
{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_HALO },
|
{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pci, oxygen_ids);
|
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_DOUBLE 0x0001
|
||||||
#define GPIO_AK5385_DFS_QUAD 0x0002
|
#define GPIO_AK5385_DFS_QUAD 0x0002
|
||||||
|
|
||||||
|
#define GPIO_CLARO_HP 0x0100
|
||||||
|
|
||||||
struct generic_data {
|
struct generic_data {
|
||||||
u8 ak4396_ctl2;
|
u8 ak4396_ctl2;
|
||||||
u16 saved_wm8785_registers[2];
|
u16 saved_wm8785_registers[2];
|
||||||
|
@ -196,10 +200,46 @@ static void meridian_init(struct oxygen *chip)
|
||||||
ak5385_init(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 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)
|
static void generic_resume(struct oxygen *chip)
|
||||||
{
|
{
|
||||||
ak4396_registers_init(chip);
|
ak4396_registers_init(chip);
|
||||||
|
@ -211,6 +251,12 @@ static void meridian_resume(struct oxygen *chip)
|
||||||
ak4396_registers_init(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,
|
static void set_ak4396_params(struct oxygen *chip,
|
||||||
struct snd_pcm_hw_params *params)
|
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 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 = {
|
static const struct oxygen_model model_generic = {
|
||||||
.shortname = "C-Media CMI8788",
|
.shortname = "C-Media CMI8788",
|
||||||
.longname = "C-Media Oxygen HD Audio",
|
.longname = "C-Media Oxygen HD Audio",
|
||||||
.chip = "CMI8788",
|
.chip = "CMI8788",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.probe = generic_probe,
|
|
||||||
.init = generic_init,
|
.init = generic_init,
|
||||||
.cleanup = generic_cleanup,
|
.cleanup = generic_cleanup,
|
||||||
.resume = generic_resume,
|
.resume = generic_resume,
|
||||||
|
@ -341,6 +367,42 @@ static const struct oxygen_model model_generic = {
|
||||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
.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,
|
static int __devinit generic_oxygen_probe(struct pci_dev *pci,
|
||||||
const struct pci_device_id *pci_id)
|
const struct pci_device_id *pci_id)
|
||||||
{
|
{
|
||||||
|
@ -353,8 +415,8 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci,
|
||||||
++dev;
|
++dev;
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
err = oxygen_pci_probe(pci, index[dev], id[dev],
|
err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
|
||||||
&model_generic, pci_id->driver_data);
|
oxygen_ids, get_oxygen_model);
|
||||||
if (err >= 0)
|
if (err >= 0)
|
||||||
++dev;
|
++dev;
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#define OXYGEN_IO_SIZE 0x100
|
#define OXYGEN_IO_SIZE 0x100
|
||||||
|
|
||||||
|
#define OXYGEN_EEPROM_ID 0x434d /* "CM" */
|
||||||
|
|
||||||
/* model-specific configuration of outputs/inputs */
|
/* model-specific configuration of outputs/inputs */
|
||||||
#define PLAYBACK_0_TO_I2S 0x0001
|
#define PLAYBACK_0_TO_I2S 0x0001
|
||||||
/* PLAYBACK_0_TO_AC97_0 not implemented */
|
/* PLAYBACK_0_TO_AC97_0 not implemented */
|
||||||
|
@ -49,7 +51,13 @@ enum {
|
||||||
.subvendor = sv, \
|
.subvendor = sv, \
|
||||||
.subdevice = sd
|
.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_dev;
|
||||||
|
struct pci_device_id;
|
||||||
struct snd_card;
|
struct snd_card;
|
||||||
struct snd_pcm_substream;
|
struct snd_pcm_substream;
|
||||||
struct snd_pcm_hardware;
|
struct snd_pcm_hardware;
|
||||||
|
@ -62,8 +70,6 @@ struct oxygen_model {
|
||||||
const char *shortname;
|
const char *shortname;
|
||||||
const char *longname;
|
const char *longname;
|
||||||
const char *chip;
|
const char *chip;
|
||||||
struct module *owner;
|
|
||||||
int (*probe)(struct oxygen *chip, unsigned long driver_data);
|
|
||||||
void (*init)(struct oxygen *chip);
|
void (*init)(struct oxygen *chip);
|
||||||
int (*control_filter)(struct snd_kcontrol_new *template);
|
int (*control_filter)(struct snd_kcontrol_new *template);
|
||||||
int (*mixer_init)(struct oxygen *chip);
|
int (*mixer_init)(struct oxygen *chip);
|
||||||
|
@ -83,6 +89,7 @@ struct oxygen_model {
|
||||||
void (*ac97_switch)(struct oxygen *chip,
|
void (*ac97_switch)(struct oxygen *chip,
|
||||||
unsigned int reg, unsigned int mute);
|
unsigned int reg, unsigned int mute);
|
||||||
const unsigned int *dac_tlv;
|
const unsigned int *dac_tlv;
|
||||||
|
unsigned long private_data;
|
||||||
size_t model_data_size;
|
size_t model_data_size;
|
||||||
unsigned int device_config;
|
unsigned int device_config;
|
||||||
u8 dac_channels;
|
u8 dac_channels;
|
||||||
|
@ -134,8 +141,12 @@ struct oxygen {
|
||||||
/* oxygen_lib.c */
|
/* oxygen_lib.c */
|
||||||
|
|
||||||
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
|
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
|
||||||
const struct oxygen_model *model,
|
struct module *owner,
|
||||||
unsigned long driver_data);
|
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);
|
void oxygen_pci_remove(struct pci_dev *pci);
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
|
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_reset_uart(struct oxygen *chip);
|
||||||
void oxygen_write_uart(struct oxygen *chip, u8 data);
|
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,
|
static inline void oxygen_set_bits8(struct oxygen *chip,
|
||||||
unsigned int reg, u8 value)
|
unsigned int reg, u8 value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -254,3 +254,34 @@ void oxygen_write_uart(struct oxygen *chip, u8 data)
|
||||||
_write_uart(chip, 0, data);
|
_write_uart(chip, 0, data);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(oxygen_write_uart);
|
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");
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||||
MODULE_DESCRIPTION("C-Media CMI8788 helper library");
|
MODULE_DESCRIPTION("C-Media CMI8788 helper library");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
||||||
|
#define DRIVER "oxygen"
|
||||||
|
|
||||||
static inline int oxygen_uart_input_ready(struct oxygen *chip)
|
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)
|
#define oxygen_proc_init(chip)
|
||||||
#endif
|
#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)
|
static void oxygen_init(struct oxygen *chip)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
@ -446,21 +503,26 @@ static void oxygen_card_free(struct snd_card *card)
|
||||||
free_irq(chip->irq, chip);
|
free_irq(chip->irq, chip);
|
||||||
flush_scheduled_work();
|
flush_scheduled_work();
|
||||||
chip->model.cleanup(chip);
|
chip->model.cleanup(chip);
|
||||||
|
kfree(chip->model_data);
|
||||||
mutex_destroy(&chip->mutex);
|
mutex_destroy(&chip->mutex);
|
||||||
pci_release_regions(chip->pci);
|
pci_release_regions(chip->pci);
|
||||||
pci_disable_device(chip->pci);
|
pci_disable_device(chip->pci);
|
||||||
}
|
}
|
||||||
|
|
||||||
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
|
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
|
||||||
const struct oxygen_model *model,
|
struct module *owner,
|
||||||
unsigned long driver_data)
|
const struct pci_device_id *ids,
|
||||||
|
int (*get_model)(struct oxygen *chip,
|
||||||
|
const struct pci_device_id *id
|
||||||
|
)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
struct snd_card *card;
|
struct snd_card *card;
|
||||||
struct oxygen *chip;
|
struct oxygen *chip;
|
||||||
|
const struct pci_device_id *pci_id;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = snd_card_create(index, id, model->owner,
|
err = snd_card_create(index, id, owner, sizeof(*chip), &card);
|
||||||
sizeof(*chip) + model->model_data_size, &card);
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -468,8 +530,6 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
|
||||||
chip->card = card;
|
chip->card = card;
|
||||||
chip->pci = pci;
|
chip->pci = pci;
|
||||||
chip->irq = -1;
|
chip->irq = -1;
|
||||||
chip->model = *model;
|
|
||||||
chip->model_data = chip + 1;
|
|
||||||
spin_lock_init(&chip->reg_lock);
|
spin_lock_init(&chip->reg_lock);
|
||||||
mutex_init(&chip->mutex);
|
mutex_init(&chip->mutex);
|
||||||
INIT_WORK(&chip->spdif_input_bits_work,
|
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)
|
if (err < 0)
|
||||||
goto err_card;
|
goto err_card;
|
||||||
|
|
||||||
err = pci_request_regions(pci, model->chip);
|
err = pci_request_regions(pci, DRIVER);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
snd_printk(KERN_ERR "cannot reserve PCI resources\n");
|
snd_printk(KERN_ERR "cannot reserve PCI resources\n");
|
||||||
goto err_pci_enable;
|
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);
|
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);
|
pci_set_master(pci);
|
||||||
snd_card_set_dev(card, &pci->dev);
|
snd_card_set_dev(card, &pci->dev);
|
||||||
card->private_free = oxygen_card_free;
|
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);
|
oxygen_init(chip);
|
||||||
chip->model.init(chip);
|
chip->model.init(chip);
|
||||||
|
|
||||||
err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
|
err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
|
||||||
chip->model.chip, chip);
|
DRIVER, chip);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
|
snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
|
||||||
goto err_card;
|
goto err_card;
|
||||||
|
|
|
@ -112,6 +112,34 @@
|
||||||
* CS4362A: AD0 <- 0
|
* 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/pci.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
@ -152,6 +180,7 @@ enum {
|
||||||
MODEL_DX,
|
MODEL_DX,
|
||||||
MODEL_HDAV, /* without daughterboard */
|
MODEL_HDAV, /* without daughterboard */
|
||||||
MODEL_HDAV_H6, /* with H6 daughterboard */
|
MODEL_HDAV_H6, /* with H6 daughterboard */
|
||||||
|
MODEL_STX,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct pci_device_id xonar_ids[] __devinitdata = {
|
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, 0x82b7), .driver_data = MODEL_D2X },
|
||||||
{ OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
|
{ OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
|
||||||
{ OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
|
{ 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);
|
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_H6 0x0000
|
||||||
#define GPIO_HDAV_DB_XX 0x0020
|
#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_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_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
|
||||||
#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */
|
#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */
|
||||||
|
|
||||||
struct xonar_data {
|
struct xonar_data {
|
||||||
unsigned int model;
|
|
||||||
unsigned int anti_pop_delay;
|
unsigned int anti_pop_delay;
|
||||||
unsigned int dacs;
|
unsigned int dacs;
|
||||||
u16 output_enable_bit;
|
u16 output_enable_bit;
|
||||||
|
@ -334,15 +367,9 @@ static void xonar_d2_init(struct oxygen *chip)
|
||||||
struct xonar_data *data = chip->model_data;
|
struct xonar_data *data = chip->model_data;
|
||||||
|
|
||||||
data->anti_pop_delay = 300;
|
data->anti_pop_delay = 300;
|
||||||
|
data->dacs = 4;
|
||||||
data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
|
data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
|
||||||
data->pcm1796_oversampling = PCM1796_OS_64;
|
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);
|
pcm1796_init(chip);
|
||||||
|
|
||||||
|
@ -355,6 +382,18 @@ static void xonar_d2_init(struct oxygen *chip)
|
||||||
snd_component_add(chip->card, "CS5381");
|
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)
|
static void update_cs4362a_volumes(struct oxygen *chip)
|
||||||
{
|
{
|
||||||
u8 mute;
|
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->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
|
||||||
data->cs4362a_fm = CS4362A_FM_SINGLE |
|
data->cs4362a_fm = CS4362A_FM_SINGLE |
|
||||||
CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
|
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_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
|
||||||
OXYGEN_2WIRE_LENGTH_8 |
|
OXYGEN_2WIRE_LENGTH_8 |
|
||||||
|
@ -447,6 +481,17 @@ static void xonar_d1_init(struct oxygen *chip)
|
||||||
snd_component_add(chip->card, "CS5361");
|
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)
|
static void xonar_hdav_init(struct oxygen *chip)
|
||||||
{
|
{
|
||||||
struct xonar_data *data = chip->model_data;
|
struct xonar_data *data = chip->model_data;
|
||||||
|
@ -458,6 +503,7 @@ static void xonar_hdav_init(struct oxygen *chip)
|
||||||
OXYGEN_2WIRE_SPEED_FAST);
|
OXYGEN_2WIRE_SPEED_FAST);
|
||||||
|
|
||||||
data->anti_pop_delay = 100;
|
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->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
|
||||||
data->ext_power_reg = OXYGEN_GPI_DATA;
|
data->ext_power_reg = OXYGEN_GPI_DATA;
|
||||||
data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
|
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");
|
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)
|
static void xonar_disable_output(struct oxygen *chip)
|
||||||
{
|
{
|
||||||
struct xonar_data *data = chip->model_data;
|
struct xonar_data *data = chip->model_data;
|
||||||
|
@ -511,6 +587,11 @@ static void xonar_hdav_cleanup(struct oxygen *chip)
|
||||||
xonar_disable_output(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)
|
static void xonar_d2_suspend(struct oxygen *chip)
|
||||||
{
|
{
|
||||||
xonar_d2_cleanup(chip);
|
xonar_d2_cleanup(chip);
|
||||||
|
@ -527,6 +608,11 @@ static void xonar_hdav_suspend(struct oxygen *chip)
|
||||||
msleep(2);
|
msleep(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void xonar_st_suspend(struct oxygen *chip)
|
||||||
|
{
|
||||||
|
xonar_st_cleanup(chip);
|
||||||
|
}
|
||||||
|
|
||||||
static void xonar_d2_resume(struct oxygen *chip)
|
static void xonar_d2_resume(struct oxygen *chip)
|
||||||
{
|
{
|
||||||
pcm1796_init(chip);
|
pcm1796_init(chip);
|
||||||
|
@ -554,6 +640,12 @@ static void xonar_hdav_resume(struct oxygen *chip)
|
||||||
xonar_enable_output(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,
|
static void xonar_hdav_pcm_hardware_filter(unsigned int channel,
|
||||||
struct snd_pcm_hardware *hardware)
|
struct snd_pcm_hardware *hardware)
|
||||||
{
|
{
|
||||||
|
@ -733,6 +825,72 @@ static const struct snd_kcontrol_new front_panel_switch = {
|
||||||
.private_value = GPIO_DX_FRONT_PANEL,
|
.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,
|
static void xonar_line_mic_ac97_switch(struct oxygen *chip,
|
||||||
unsigned int reg, unsigned int mute)
|
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(pcm1796_db_scale, -6000, 50, 0);
|
||||||
static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -12700, 100, 0);
|
static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
|
||||||
|
|
||||||
static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
|
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;
|
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)
|
static int xonar_d2_mixer_init(struct oxygen *chip)
|
||||||
{
|
{
|
||||||
return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, 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));
|
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[] = {
|
return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip));
|
||||||
[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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct oxygen_model model_xonar_d2 = {
|
static const struct oxygen_model model_xonar_d2 = {
|
||||||
.longname = "Asus Virtuoso 200",
|
.longname = "Asus Virtuoso 200",
|
||||||
.chip = "AV200",
|
.chip = "AV200",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.probe = xonar_model_probe,
|
|
||||||
.init = xonar_d2_init,
|
.init = xonar_d2_init,
|
||||||
.control_filter = xonar_d2_control_filter,
|
.control_filter = xonar_d2_control_filter,
|
||||||
.mixer_init = xonar_d2_mixer_init,
|
.mixer_init = xonar_d2_mixer_init,
|
||||||
|
@ -837,8 +967,8 @@ static const struct oxygen_model model_xonar_d2 = {
|
||||||
MIDI_OUTPUT |
|
MIDI_OUTPUT |
|
||||||
MIDI_INPUT,
|
MIDI_INPUT,
|
||||||
.dac_channels = 8,
|
.dac_channels = 8,
|
||||||
.dac_volume_min = 0x0f,
|
.dac_volume_min = 255 - 2*60,
|
||||||
.dac_volume_max = 0xff,
|
.dac_volume_max = 255,
|
||||||
.misc_flags = OXYGEN_MISC_MIDI,
|
.misc_flags = OXYGEN_MISC_MIDI,
|
||||||
.function_flags = OXYGEN_FUNCTION_SPI |
|
.function_flags = OXYGEN_FUNCTION_SPI |
|
||||||
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
|
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 = {
|
static const struct oxygen_model model_xonar_d1 = {
|
||||||
.longname = "Asus Virtuoso 100",
|
.longname = "Asus Virtuoso 100",
|
||||||
.chip = "AV200",
|
.chip = "AV200",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.probe = xonar_model_probe,
|
|
||||||
.init = xonar_d1_init,
|
.init = xonar_d1_init,
|
||||||
.control_filter = xonar_d1_control_filter,
|
.control_filter = xonar_d1_control_filter,
|
||||||
.mixer_init = xonar_d1_mixer_init,
|
.mixer_init = xonar_d1_mixer_init,
|
||||||
|
@ -868,7 +996,7 @@ static const struct oxygen_model model_xonar_d1 = {
|
||||||
PLAYBACK_1_TO_SPDIF |
|
PLAYBACK_1_TO_SPDIF |
|
||||||
CAPTURE_0_FROM_I2S_2,
|
CAPTURE_0_FROM_I2S_2,
|
||||||
.dac_channels = 8,
|
.dac_channels = 8,
|
||||||
.dac_volume_min = 0,
|
.dac_volume_min = 127 - 60,
|
||||||
.dac_volume_max = 127,
|
.dac_volume_max = 127,
|
||||||
.function_flags = OXYGEN_FUNCTION_2WIRE,
|
.function_flags = OXYGEN_FUNCTION_2WIRE,
|
||||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
.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 = {
|
static const struct oxygen_model model_xonar_hdav = {
|
||||||
.longname = "Asus Virtuoso 200",
|
.longname = "Asus Virtuoso 200",
|
||||||
.chip = "AV200",
|
.chip = "AV200",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.probe = xonar_model_probe,
|
|
||||||
.init = xonar_hdav_init,
|
.init = xonar_hdav_init,
|
||||||
.cleanup = xonar_hdav_cleanup,
|
.cleanup = xonar_hdav_cleanup,
|
||||||
.suspend = xonar_hdav_suspend,
|
.suspend = xonar_hdav_suspend,
|
||||||
|
@ -897,16 +1023,43 @@ static const struct oxygen_model model_xonar_hdav = {
|
||||||
PLAYBACK_1_TO_SPDIF |
|
PLAYBACK_1_TO_SPDIF |
|
||||||
CAPTURE_0_FROM_I2S_2,
|
CAPTURE_0_FROM_I2S_2,
|
||||||
.dac_channels = 8,
|
.dac_channels = 8,
|
||||||
.dac_volume_min = 0x0f,
|
.dac_volume_min = 255 - 2*60,
|
||||||
.dac_volume_max = 0xff,
|
.dac_volume_max = 255,
|
||||||
.misc_flags = OXYGEN_MISC_MIDI,
|
.misc_flags = OXYGEN_MISC_MIDI,
|
||||||
.function_flags = OXYGEN_FUNCTION_2WIRE,
|
.function_flags = OXYGEN_FUNCTION_2WIRE,
|
||||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __devinit xonar_probe(struct pci_dev *pci,
|
static const struct oxygen_model model_xonar_st = {
|
||||||
const struct pci_device_id *pci_id)
|
.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[] = {
|
static const struct oxygen_model *const models[] = {
|
||||||
[MODEL_D1] = &model_xonar_d1,
|
[MODEL_D1] = &model_xonar_d1,
|
||||||
|
@ -914,7 +1067,57 @@ static int __devinit xonar_probe(struct pci_dev *pci,
|
||||||
[MODEL_D2] = &model_xonar_d2,
|
[MODEL_D2] = &model_xonar_d2,
|
||||||
[MODEL_D2X] = &model_xonar_d2,
|
[MODEL_D2X] = &model_xonar_d2,
|
||||||
[MODEL_HDAV] = &model_xonar_hdav,
|
[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;
|
static int dev;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -924,10 +1127,8 @@ static int __devinit xonar_probe(struct pci_dev *pci,
|
||||||
++dev;
|
++dev;
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
BUG_ON(pci_id->driver_data >= ARRAY_SIZE(models));
|
err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
|
||||||
err = oxygen_pci_probe(pci, index[dev], id[dev],
|
xonar_ids, get_xonar_model);
|
||||||
models[pci_id->driver_data],
|
|
||||||
pci_id->driver_data);
|
|
||||||
if (err >= 0)
|
if (err >= 0)
|
||||||
++dev;
|
++dev;
|
||||||
return err;
|
return err;
|
||||||
|
|
Loading…
Reference in a new issue