summaryrefslogtreecommitdiffstats
path: root/sound/pci/oxygen
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/oxygen')
-rw-r--r--sound/pci/oxygen/cs4362a.h69
-rw-r--r--sound/pci/oxygen/cs4398.h69
-rw-r--r--sound/pci/oxygen/hifier.c36
-rw-r--r--sound/pci/oxygen/oxygen.c129
-rw-r--r--sound/pci/oxygen/oxygen.h23
-rw-r--r--sound/pci/oxygen/oxygen_io.c23
-rw-r--r--sound/pci/oxygen/oxygen_lib.c113
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c217
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c78
-rw-r--r--sound/pci/oxygen/pcm1796.h58
-rw-r--r--sound/pci/oxygen/virtuoso.c594
-rw-r--r--sound/pci/oxygen/wm8785.h45
12 files changed, 1039 insertions, 415 deletions
diff --git a/sound/pci/oxygen/cs4362a.h b/sound/pci/oxygen/cs4362a.h
new file mode 100644
index 00000000000..6a4fedf5e1e
--- /dev/null
+++ b/sound/pci/oxygen/cs4362a.h
@@ -0,0 +1,69 @@
+/* register 01h */
+#define CS4362A_PDN 0x01
+#define CS4362A_DAC1_DIS 0x02
+#define CS4362A_DAC2_DIS 0x04
+#define CS4362A_DAC3_DIS 0x08
+#define CS4362A_MCLKDIV 0x20
+#define CS4362A_FREEZE 0x40
+#define CS4362A_CPEN 0x80
+/* register 02h */
+#define CS4362A_DIF_MASK 0x70
+#define CS4362A_DIF_LJUST 0x00
+#define CS4362A_DIF_I2S 0x10
+#define CS4362A_DIF_RJUST_16 0x20
+#define CS4362A_DIF_RJUST_24 0x30
+#define CS4362A_DIF_RJUST_20 0x40
+#define CS4362A_DIF_RJUST_18 0x50
+/* register 03h */
+#define CS4362A_MUTEC_MASK 0x03
+#define CS4362A_MUTEC_6 0x00
+#define CS4362A_MUTEC_1 0x01
+#define CS4362A_MUTEC_3 0x03
+#define CS4362A_AMUTE 0x04
+#define CS4362A_MUTEC_POL 0x08
+#define CS4362A_RMP_UP 0x10
+#define CS4362A_SNGLVOL 0x20
+#define CS4362A_ZERO_CROSS 0x40
+#define CS4362A_SOFT_RAMP 0x80
+/* register 04h */
+#define CS4362A_RMP_DN 0x01
+#define CS4362A_DEM_MASK 0x06
+#define CS4362A_DEM_NONE 0x00
+#define CS4362A_DEM_44100 0x02
+#define CS4362A_DEM_48000 0x04
+#define CS4362A_DEM_32000 0x06
+#define CS4362A_FILT_SEL 0x10
+/* register 05h */
+#define CS4362A_INV_A1 0x01
+#define CS4362A_INV_B1 0x02
+#define CS4362A_INV_A2 0x04
+#define CS4362A_INV_B2 0x08
+#define CS4362A_INV_A3 0x10
+#define CS4362A_INV_B3 0x20
+/* register 06h */
+#define CS4362A_FM_MASK 0x03
+#define CS4362A_FM_SINGLE 0x00
+#define CS4362A_FM_DOUBLE 0x01
+#define CS4362A_FM_QUAD 0x02
+#define CS4362A_FM_DSD 0x03
+#define CS4362A_ATAPI_MASK 0x7c
+#define CS4362A_ATAPI_B_MUTE 0x00
+#define CS4362A_ATAPI_B_R 0x04
+#define CS4362A_ATAPI_B_L 0x08
+#define CS4362A_ATAPI_B_LR 0x0c
+#define CS4362A_ATAPI_A_MUTE 0x00
+#define CS4362A_ATAPI_A_R 0x10
+#define CS4362A_ATAPI_A_L 0x20
+#define CS4362A_ATAPI_A_LR 0x30
+#define CS4362A_ATAPI_MIX_LR_VOL 0x40
+#define CS4362A_A_EQ_B 0x80
+/* register 07h */
+#define CS4362A_VOL_MASK 0x7f
+#define CS4362A_MUTE 0x80
+/* register 08h: like 07h */
+/* registers 09h..0Bh: like 06h..08h */
+/* registers 0Ch..0Eh: like 06h..08h */
+/* register 12h */
+#define CS4362A_REV_MASK 0x07
+#define CS4362A_PART_MASK 0xf8
+#define CS4362A_PART_CS4362A 0x50
diff --git a/sound/pci/oxygen/cs4398.h b/sound/pci/oxygen/cs4398.h
new file mode 100644
index 00000000000..5faf5efc882
--- /dev/null
+++ b/sound/pci/oxygen/cs4398.h
@@ -0,0 +1,69 @@
+/* register 1 */
+#define CS4398_REV_MASK 0x07
+#define CS4398_PART_MASK 0xf8
+#define CS4398_PART_CS4398 0x70
+/* register 2 */
+#define CS4398_FM_MASK 0x03
+#define CS4398_FM_SINGLE 0x00
+#define CS4398_FM_DOUBLE 0x01
+#define CS4398_FM_QUAD 0x02
+#define CS4398_FM_DSD 0x03
+#define CS4398_DEM_MASK 0x0c
+#define CS4398_DEM_NONE 0x00
+#define CS4398_DEM_44100 0x04
+#define CS4398_DEM_48000 0x08
+#define CS4398_DEM_32000 0x0c
+#define CS4398_DIF_MASK 0x70
+#define CS4398_DIF_LJUST 0x00
+#define CS4398_DIF_I2S 0x10
+#define CS4398_DIF_RJUST_16 0x20
+#define CS4398_DIF_RJUST_24 0x30
+#define CS4398_DIF_RJUST_20 0x40
+#define CS4398_DIF_RJUST_18 0x50
+#define CS4398_DSD_SRC 0x80
+/* register 3 */
+#define CS4398_ATAPI_MASK 0x1f
+#define CS4398_ATAPI_B_MUTE 0x00
+#define CS4398_ATAPI_B_R 0x01
+#define CS4398_ATAPI_B_L 0x02
+#define CS4398_ATAPI_B_LR 0x03
+#define CS4398_ATAPI_A_MUTE 0x00
+#define CS4398_ATAPI_A_R 0x04
+#define CS4398_ATAPI_A_L 0x08
+#define CS4398_ATAPI_A_LR 0x0c
+#define CS4398_ATAPI_MIX_LR_VOL 0x10
+#define CS4398_INVERT_B 0x20
+#define CS4398_INVERT_A 0x40
+#define CS4398_VOL_B_EQ_A 0x80
+/* register 4 */
+#define CS4398_MUTEP_MASK 0x03
+#define CS4398_MUTEP_AUTO 0x00
+#define CS4398_MUTEP_LOW 0x02
+#define CS4398_MUTEP_HIGH 0x03
+#define CS4398_MUTE_B 0x08
+#define CS4398_MUTE_A 0x10
+#define CS4398_MUTEC_A_EQ_B 0x20
+#define CS4398_DAMUTE 0x40
+#define CS4398_PAMUTE 0x80
+/* register 5 */
+#define CS4398_VOL_A_MASK 0xff
+/* register 6 */
+#define CS4398_VOL_B_MASK 0xff
+/* register 7 */
+#define CS4398_DIR_DSD 0x01
+#define CS4398_FILT_SEL 0x04
+#define CS4398_RMP_DN 0x10
+#define CS4398_RMP_UP 0x20
+#define CS4398_ZERO_CROSS 0x40
+#define CS4398_SOFT_RAMP 0x80
+/* register 8 */
+#define CS4398_MCLKDIV3 0x08
+#define CS4398_MCLKDIV2 0x10
+#define CS4398_FREEZE 0x20
+#define CS4398_CPEN 0x40
+#define CS4398_PDN 0x80
+/* register 9 */
+#define CS4398_DSD_PM_EN 0x01
+#define CS4398_DSD_PM_MODE 0x02
+#define CS4398_INVALID_DSD 0x04
+#define CS4398_STATIC_DSD 0x08
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
index 666f69a3312..090dd4354a2 100644
--- a/sound/pci/oxygen/hifier.c
+++ b/sound/pci/oxygen/hifier.c
@@ -66,12 +66,12 @@ static void hifier_init(struct oxygen *chip)
{
struct hifier_data *data = chip->model_data;
- data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
- ak4396_write(chip, AK4396_LCH_ATT, 0xff);
- ak4396_write(chip, AK4396_RCH_ATT, 0xff);
+ ak4396_write(chip, AK4396_LCH_ATT, 0);
+ ak4396_write(chip, AK4396_RCH_ATT, 0);
snd_component_add(chip->card, "AK4396");
snd_component_add(chip->card, "CS5340");
@@ -127,22 +127,8 @@ static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static int hifier_control_filter(struct snd_kcontrol_new *template)
{
- if (!strcmp(template->name, "Master Playback Volume")) {
- template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- template->tlv.p = ak4396_db_scale;
- } else if (!strcmp(template->name, "Stereo Upmixing")) {
+ if (!strcmp(template->name, "Stereo Upmixing"))
return 1; /* stereo only - we don't need upmixing */
- } else if (!strcmp(template->name,
- SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK)) ||
- !strcmp(template->name,
- SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT))) {
- return 1; /* no digital input */
- }
- return 0;
-}
-
-static int hifier_mixer_init(struct oxygen *chip)
-{
return 0;
}
@@ -153,18 +139,20 @@ static const struct oxygen_model model_hifier = {
.owner = THIS_MODULE,
.init = hifier_init,
.control_filter = hifier_control_filter,
- .mixer_init = hifier_mixer_init,
.cleanup = hifier_cleanup,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_cs5340_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
+ .dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct hifier_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_1,
.dac_channels = 2,
- .used_channels = OXYGEN_CHANNEL_A |
- OXYGEN_CHANNEL_SPDIF |
- OXYGEN_CHANNEL_MULTICH,
- .function_flags = 0,
+ .dac_volume_min = 0,
+ .dac_volume_max = 255,
+ .function_flags = OXYGEN_FUNCTION_SPI,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@@ -181,7 +169,7 @@ static int __devinit hifier_probe(struct pci_dev *pci,
++dev;
return -ENOENT;
}
- err = oxygen_pci_probe(pci, index[dev], id[dev], 0, &model_hifier);
+ err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier);
if (err >= 0)
++dev;
return err;
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index 9a9941bb046..63f185c1ed1 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -39,7 +39,7 @@
#include <sound/tlv.h>
#include "oxygen.h"
#include "ak4396.h"
-#include "cm9780.h"
+#include "wm8785.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 driver");
@@ -78,49 +78,6 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
#define GPIO_AK5385_DFS_DOUBLE 0x0001
#define GPIO_AK5385_DFS_QUAD 0x0002
-#define GPIO_LINE_MUTE CM9780_GPO0
-
-#define WM8785_R0 0
-#define WM8785_R1 1
-#define WM8785_R2 2
-#define WM8785_R7 7
-
-/* R0 */
-#define WM8785_MCR_MASK 0x007
-#define WM8785_MCR_SLAVE 0x000
-#define WM8785_MCR_MASTER_128 0x001
-#define WM8785_MCR_MASTER_192 0x002
-#define WM8785_MCR_MASTER_256 0x003
-#define WM8785_MCR_MASTER_384 0x004
-#define WM8785_MCR_MASTER_512 0x005
-#define WM8785_MCR_MASTER_768 0x006
-#define WM8785_OSR_MASK 0x018
-#define WM8785_OSR_SINGLE 0x000
-#define WM8785_OSR_DOUBLE 0x008
-#define WM8785_OSR_QUAD 0x010
-#define WM8785_FORMAT_MASK 0x060
-#define WM8785_FORMAT_RJUST 0x000
-#define WM8785_FORMAT_LJUST 0x020
-#define WM8785_FORMAT_I2S 0x040
-#define WM8785_FORMAT_DSP 0x060
-/* R1 */
-#define WM8785_WL_MASK 0x003
-#define WM8785_WL_16 0x000
-#define WM8785_WL_20 0x001
-#define WM8785_WL_24 0x002
-#define WM8785_WL_32 0x003
-#define WM8785_LRP 0x004
-#define WM8785_BCLKINV 0x008
-#define WM8785_LRSWAP 0x010
-#define WM8785_DEVNO_MASK 0x0e0
-/* R2 */
-#define WM8785_HPFR 0x001
-#define WM8785_HPFL 0x002
-#define WM8785_SDODIS 0x004
-#define WM8785_PWRDNR 0x008
-#define WM8785_PWRDNL 0x010
-#define WM8785_TDM_MASK 0x1c0
-
struct generic_data {
u8 ak4396_ctl2;
};
@@ -155,7 +112,7 @@ static void ak4396_init(struct oxygen *chip)
struct generic_data *data = chip->model_data;
unsigned int i;
- data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
for (i = 0; i < 4; ++i) {
ak4396_write(chip, i,
AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
@@ -163,8 +120,8 @@ static void ak4396_init(struct oxygen *chip)
AK4396_CONTROL_2, data->ak4396_ctl2);
ak4396_write(chip, i,
AK4396_CONTROL_3, AK4396_PCM);
- ak4396_write(chip, i, AK4396_LCH_ATT, 0xff);
- ak4396_write(chip, i, AK4396_RCH_ATT, 0xff);
+ ak4396_write(chip, i, AK4396_LCH_ATT, 0);
+ ak4396_write(chip, i, AK4396_RCH_ATT, 0);
}
snd_component_add(chip->card, "AK4396");
}
@@ -185,23 +142,16 @@ static void wm8785_init(struct oxygen *chip)
snd_component_add(chip->card, "WM8785");
}
-static void cmi9780_init(struct oxygen *chip)
-{
- oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE);
-}
-
static void generic_init(struct oxygen *chip)
{
ak4396_init(chip);
wm8785_init(chip);
- cmi9780_init(chip);
}
static void meridian_init(struct oxygen *chip)
{
ak4396_init(chip);
ak5385_init(chip);
- cmi9780_init(chip);
}
static void generic_cleanup(struct oxygen *chip)
@@ -297,59 +247,32 @@ static void set_ak5385_params(struct oxygen *chip,
value, GPIO_AK5385_DFS_MASK);
}
-static void cmi9780_switch_hook(struct oxygen *chip, unsigned int codec,
- unsigned int reg, int mute)
-{
- if (codec != 0)
- return;
- switch (reg) {
- case AC97_LINE:
- oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
- mute ? GPIO_LINE_MUTE : 0,
- GPIO_LINE_MUTE);
- break;
- case AC97_MIC:
- case AC97_CD:
- case AC97_AUX:
- if (!mute)
- oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS,
- GPIO_LINE_MUTE);
- break;
- }
-}
-
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
-static int ak4396_control_filter(struct snd_kcontrol_new *template)
-{
- if (!strcmp(template->name, "Master Playback Volume")) {
- template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- template->tlv.p = ak4396_db_scale;
- }
- return 0;
-}
-
static const struct oxygen_model model_generic = {
.shortname = "C-Media CMI8788",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788",
.owner = THIS_MODULE,
.init = generic_init,
- .control_filter = ak4396_control_filter,
.cleanup = generic_cleanup,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_wm8785_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
- .ac97_switch_hook = cmi9780_switch_hook,
+ .dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct generic_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ PLAYBACK_2_TO_AC97_1 |
+ CAPTURE_0_FROM_I2S_1 |
+ CAPTURE_1_FROM_SPDIF |
+ CAPTURE_2_FROM_AC97_1,
.dac_channels = 8,
- .used_channels = OXYGEN_CHANNEL_A |
- OXYGEN_CHANNEL_C |
- OXYGEN_CHANNEL_SPDIF |
- OXYGEN_CHANNEL_MULTICH |
- OXYGEN_CHANNEL_AC97,
- .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_volume_min = 0,
+ .dac_volume_max = 255,
+ .function_flags = OXYGEN_FUNCTION_SPI |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@@ -359,21 +282,25 @@ static const struct oxygen_model model_meridian = {
.chip = "CMI8788",
.owner = THIS_MODULE,
.init = meridian_init,
- .control_filter = ak4396_control_filter,
.cleanup = generic_cleanup,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_ak5385_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
- .ac97_switch_hook = cmi9780_switch_hook,
+ .dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct generic_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ PLAYBACK_2_TO_AC97_1 |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF |
+ CAPTURE_2_FROM_AC97_1,
.dac_channels = 8,
- .used_channels = OXYGEN_CHANNEL_B |
- OXYGEN_CHANNEL_C |
- OXYGEN_CHANNEL_SPDIF |
- OXYGEN_CHANNEL_MULTICH |
- OXYGEN_CHANNEL_AC97,
- .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_volume_min = 0,
+ .dac_volume_max = 255,
+ .misc_flags = OXYGEN_MISC_MIDI,
+ .function_flags = OXYGEN_FUNCTION_SPI |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@@ -392,7 +319,7 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci,
return -ENOENT;
}
is_meridian = pci_id->driver_data;
- err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian,
+ err = oxygen_pci_probe(pci, index[dev], id[dev],
is_meridian ? &model_meridian : &model_generic);
if (err >= 0)
++dev;
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index ad50fb8b206..a71c6e05926 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -16,6 +16,16 @@
#define PCM_AC97 5
#define PCM_COUNT 6
+/* model-specific configuration of outputs/inputs */
+#define PLAYBACK_0_TO_I2S 0x001
+#define PLAYBACK_1_TO_SPDIF 0x004
+#define PLAYBACK_2_TO_AC97_1 0x008
+#define CAPTURE_0_FROM_I2S_1 0x010
+#define CAPTURE_0_FROM_I2S_2 0x020
+#define CAPTURE_1_FROM_SPDIF 0x080
+#define CAPTURE_2_FROM_I2S_2 0x100
+#define CAPTURE_2_FROM_AC97_1 0x200
+
enum {
CONTROL_SPDIF_PCM,
CONTROL_SPDIF_INPUT_BITS,
@@ -87,12 +97,16 @@ struct oxygen_model {
struct snd_pcm_hw_params *params);
void (*update_dac_volume)(struct oxygen *chip);
void (*update_dac_mute)(struct oxygen *chip);
- void (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec,
- unsigned int reg, int mute);
void (*gpio_changed)(struct oxygen *chip);
+ void (*ac97_switch)(struct oxygen *chip,
+ unsigned int reg, unsigned int mute);
+ const unsigned int *dac_tlv;
size_t model_data_size;
+ unsigned int pcm_dev_cfg;
u8 dac_channels;
- u8 used_channels;
+ u8 dac_volume_min;
+ u8 dac_volume_max;
+ u8 misc_flags;
u8 function_flags;
u16 dac_i2s_format;
u16 adc_i2s_format;
@@ -100,7 +114,7 @@ struct oxygen_model {
/* oxygen_lib.c */
-int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, int midi,
+int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
const struct oxygen_model *model);
void oxygen_pci_remove(struct pci_dev *pci);
@@ -137,6 +151,7 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
unsigned int index, u16 data, u16 mask);
void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
+void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);
static inline void oxygen_set_bits8(struct oxygen *chip,
unsigned int reg, u8 value)
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index 74e23ef9c94..5569606ee87 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -190,12 +190,31 @@ void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
--count;
}
- spin_lock_irq(&chip->reg_lock);
oxygen_write8(chip, OXYGEN_SPI_DATA1, data);
oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8);
if (control & OXYGEN_SPI_DATA_LENGTH_3)
oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16);
oxygen_write8(chip, OXYGEN_SPI_CONTROL, control);
- spin_unlock_irq(&chip->reg_lock);
}
EXPORT_SYMBOL(oxygen_write_spi);
+
+void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data)
+{
+ unsigned long timeout;
+
+ /* should not need more than about 300 us */
+ timeout = jiffies + msecs_to_jiffies(1);
+ do {
+ if (!(oxygen_read16(chip, OXYGEN_2WIRE_BUS_STATUS)
+ & OXYGEN_2WIRE_BUSY))
+ break;
+ udelay(1);
+ cond_resched();
+ } while (time_after_eq(timeout, jiffies));
+
+ oxygen_write8(chip, OXYGEN_2WIRE_MAP, map);
+ oxygen_write8(chip, OXYGEN_2WIRE_DATA, data);
+ oxygen_write8(chip, OXYGEN_2WIRE_CONTROL,
+ device | OXYGEN_2WIRE_DIR_WRITE);
+}
+EXPORT_SYMBOL(oxygen_write_i2c);
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 78c21155218..897697d4350 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -221,7 +221,8 @@ static void oxygen_init(struct oxygen *chip)
chip->dac_routing = 1;
for (i = 0; i < 8; ++i)
- chip->dac_volume[i] = 0xff;
+ chip->dac_volume[i] = chip->model->dac_volume_min;
+ chip->dac_mute = 1;
chip->spdif_playback_enable = 1;
chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL |
(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
@@ -240,12 +241,12 @@ static void oxygen_init(struct oxygen *chip)
chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0;
chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0;
- oxygen_set_bits8(chip, OXYGEN_FUNCTION,
- OXYGEN_FUNCTION_RESET_CODEC |
- chip->model->function_flags);
oxygen_write8_masked(chip, OXYGEN_FUNCTION,
- OXYGEN_FUNCTION_SPI,
- OXYGEN_FUNCTION_2WIRE_SPI_MASK);
+ OXYGEN_FUNCTION_RESET_CODEC |
+ chip->model->function_flags,
+ OXYGEN_FUNCTION_RESET_CODEC |
+ OXYGEN_FUNCTION_2WIRE_SPI_MASK |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5);
oxygen_write8(chip, OXYGEN_DMA_STATUS, 0);
oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0);
oxygen_write8(chip, OXYGEN_PLAY_CHANNELS,
@@ -253,11 +254,13 @@ static void oxygen_init(struct oxygen *chip)
OXYGEN_DMA_A_BURST_8 |
OXYGEN_DMA_MULTICH_BURST_8);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
- oxygen_write8_masked(chip, OXYGEN_MISC, 0,
+ oxygen_write8_masked(chip, OXYGEN_MISC,
+ chip->model->misc_flags,
OXYGEN_MISC_WRITE_PCI_SUBID |
OXYGEN_MISC_REC_C_FROM_SPDIF |
OXYGEN_MISC_REC_B_FROM_AC97 |
- OXYGEN_MISC_REC_A_FROM_MULTICH);
+ OXYGEN_MISC_REC_A_FROM_MULTICH |
+ OXYGEN_MISC_MIDI);
oxygen_write8(chip, OXYGEN_REC_FORMAT,
(OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) |
(OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) |
@@ -267,35 +270,49 @@ static void oxygen_init(struct oxygen *chip)
(OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
- OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
- OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
- OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
- oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
- OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
- OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
- OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
- oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
- OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
- OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_RATE_48000 | chip->model->dac_i2s_format |
+ OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1)
+ oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
+ OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
+ OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ else
+ oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
+ if (chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_2_FROM_I2S_2))
+ oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
+ OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
+ OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+ else
+ oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
- OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
- OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
- OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
- oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
- OXYGEN_SPDIF_SENSE_MASK |
- OXYGEN_SPDIF_LOCK_MASK |
- OXYGEN_SPDIF_RATE_MASK |
- OXYGEN_SPDIF_LOCK_PAR |
- OXYGEN_SPDIF_IN_CLOCK_96,
- OXYGEN_SPDIF_OUT_ENABLE |
- OXYGEN_SPDIF_LOOPBACK |
- OXYGEN_SPDIF_SENSE_MASK |
- OXYGEN_SPDIF_LOCK_MASK |
- OXYGEN_SPDIF_RATE_MASK |
- OXYGEN_SPDIF_SENSE_PAR |
- OXYGEN_SPDIF_LOCK_PAR |
- OXYGEN_SPDIF_IN_CLOCK_MASK);
+ OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
+ oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
+ OXYGEN_SPDIF_OUT_ENABLE |
+ OXYGEN_SPDIF_LOOPBACK);
+ if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF)
+ oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
+ OXYGEN_SPDIF_SENSE_MASK |
+ OXYGEN_SPDIF_LOCK_MASK |
+ OXYGEN_SPDIF_RATE_MASK |
+ OXYGEN_SPDIF_LOCK_PAR |
+ OXYGEN_SPDIF_IN_CLOCK_96,
+ OXYGEN_SPDIF_SENSE_MASK |
+ OXYGEN_SPDIF_LOCK_MASK |
+ OXYGEN_SPDIF_RATE_MASK |
+ OXYGEN_SPDIF_SENSE_PAR |
+ OXYGEN_SPDIF_LOCK_PAR |
+ OXYGEN_SPDIF_IN_CLOCK_MASK);
+ else
+ oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
+ OXYGEN_SPDIF_SENSE_MASK |
+ OXYGEN_SPDIF_LOCK_MASK |
+ OXYGEN_SPDIF_RATE_MASK);
oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK);
oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0);
@@ -318,9 +335,12 @@ static void oxygen_init(struct oxygen *chip)
(2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) |
(3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT));
- oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK,
- OXYGEN_AC97_INT_READ_DONE |
- OXYGEN_AC97_INT_WRITE_DONE);
+ if (chip->has_ac97_0 | chip->has_ac97_1)
+ oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK,
+ OXYGEN_AC97_INT_READ_DONE |
+ OXYGEN_AC97_INT_WRITE_DONE);
+ else
+ oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, 0);
oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0);
oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0);
if (!(chip->has_ac97_0 | chip->has_ac97_1))
@@ -351,6 +371,8 @@ static void oxygen_init(struct oxygen *chip)
oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000);
oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080);
oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080);
+ oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS,
+ CM9780_GPO0);
/* power down unused ADCs and DACs */
oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN,
AC97_PD_PR0 | AC97_PD_PR1);
@@ -388,10 +410,8 @@ static void oxygen_card_free(struct snd_card *card)
oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
spin_unlock_irq(&chip->reg_lock);
- if (chip->irq >= 0) {
+ if (chip->irq >= 0)
free_irq(chip->irq, chip);
- synchronize_irq(chip->irq);
- }
flush_scheduled_work();
chip->model->cleanup(chip);
mutex_destroy(&chip->mutex);
@@ -400,7 +420,7 @@ static void oxygen_card_free(struct snd_card *card)
}
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
- int midi, const struct oxygen_model *model)
+ const struct oxygen_model *model)
{
struct snd_card *card;
struct oxygen *chip;
@@ -472,9 +492,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
if (err < 0)
goto err_card;
- oxygen_write8_masked(chip, OXYGEN_MISC,
- midi ? OXYGEN_MISC_MIDI : 0, OXYGEN_MISC_MIDI);
- if (midi) {
+ if (model->misc_flags & OXYGEN_MISC_MIDI) {
err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
chip->addr + OXYGEN_MPU401,
MPU401_INFO_INTEGRATED, 0, 0,
@@ -486,7 +504,10 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
oxygen_proc_init(chip);
spin_lock_irq(&chip->reg_lock);
- chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT | OXYGEN_INT_AC97;
+ if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF)
+ chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
+ if (chip->has_ac97_0 | chip->has_ac97_1)
+ chip->interrupt_mask |= OXYGEN_INT_AC97;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
spin_unlock_irq(&chip->reg_lock);
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index a8e4623415d..cc0cddadd58 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -32,8 +32,8 @@ static int dac_volume_info(struct snd_kcontrol *ctl,
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
info->count = chip->model->dac_channels;
- info->value.integer.min = 0;
- info->value.integer.max = 0xff;
+ info->value.integer.min = chip->model->dac_volume_min;
+ info->value.integer.max = chip->model->dac_volume_max;
return 0;
}
@@ -446,6 +446,50 @@ static int spdif_loopback_put(struct snd_kcontrol *ctl,
return changed;
}
+static int monitor_volume_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 1;
+ info->value.integer.min = 0;
+ info->value.integer.max = 1;
+ return 0;
+}
+
+static int monitor_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u8 bit = ctl->private_value;
+ int invert = ctl->private_value & (1 << 8);
+
+ value->value.integer.value[0] =
+ !!invert ^ !!(oxygen_read8(chip, OXYGEN_ADC_MONITOR) & bit);
+ return 0;
+}
+
+static int monitor_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u8 bit = ctl->private_value;
+ int invert = ctl->private_value & (1 << 8);
+ u8 oldreg, newreg;
+ int changed;
+
+ spin_lock_irq(&chip->reg_lock);
+ oldreg = oxygen_read8(chip, OXYGEN_ADC_MONITOR);
+ if ((!!value->value.integer.value[0] ^ !!invert) != 0)
+ newreg = oldreg | bit;
+ else
+ newreg = oldreg & ~bit;
+ changed = newreg != oldreg;
+ if (changed)
+ oxygen_write8(chip, OXYGEN_ADC_MONITOR, newreg);
+ spin_unlock_irq(&chip->reg_lock);
+ return changed;
+}
+
static int ac97_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
@@ -466,6 +510,21 @@ static int ac97_switch_get(struct snd_kcontrol *ctl,
return 0;
}
+static void mute_ac97_ctl(struct oxygen *chip, unsigned int control)
+{
+ unsigned int priv_idx = chip->controls[control]->private_value & 0xff;
+ u16 value;
+
+ value = oxygen_read_ac97(chip, 0, priv_idx);
+ if (!(value & 0x8000)) {
+ oxygen_write_ac97(chip, 0, priv_idx, value | 0x8000);
+ if (chip->model->ac97_switch)
+ chip->model->ac97_switch(chip, priv_idx, 0x8000);
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->controls[control]->id);
+ }
+}
+
static int ac97_switch_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
@@ -487,9 +546,24 @@ static int ac97_switch_put(struct snd_kcontrol *ctl,
change = newreg != oldreg;
if (change) {
oxygen_write_ac97(chip, codec, index, newreg);
- if (bitnr == 15 && chip->model->ac97_switch_hook)
- chip->model->ac97_switch_hook(chip, codec, index,
- newreg & 0x8000);
+ if (codec == 0 && chip->model->ac97_switch)
+ chip->model->ac97_switch(chip, index, newreg & 0x8000);
+ if (index == AC97_LINE) {
+ oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
+ newreg & 0x8000 ?
+ CM9780_GPO0 : 0, CM9780_GPO0);
+ if (!(newreg & 0x8000)) {
+ mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH);
+ mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH);
+ mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH);
+ }
+ } else if ((index == AC97_MIC || index == AC97_CD ||
+ index == AC97_VIDEO || index == AC97_AUX) &&
+ bitnr == 15 && !(newreg & 0x8000)) {
+ mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH);
+ oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
+ CM9780_GPO0, CM9780_GPO0);
+ }
}
mutex_unlock(&chip->mutex);
return change;
@@ -608,6 +682,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl,
.private_value = ((codec) << 24) | (index), \
}
+static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0);
static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0);
static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0);
@@ -667,6 +742,9 @@ static const struct snd_kcontrol_new controls[] = {
.get = spdif_pcm_get,
.put = spdif_pcm_put,
},
+};
+
+static const struct snd_kcontrol_new spdif_input_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.device = 1,
@@ -692,11 +770,118 @@ static const struct snd_kcontrol_new controls[] = {
},
};
+static const struct {
+ unsigned int pcm_dev;
+ struct snd_kcontrol_new controls[2];
+} monitor_controls[] = {
+ {
+ .pcm_dev = CAPTURE_0_FROM_I2S_1,
+ .controls = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_A,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = monitor_volume_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_A_HALF_VOL
+ | (1 << 8),
+ .tlv = { .p = monitor_db_scale, },
+ },
+ },
+ },
+ {
+ .pcm_dev = CAPTURE_0_FROM_I2S_2,
+ .controls = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_B,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = monitor_volume_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL
+ | (1 << 8),
+ .tlv = { .p = monitor_db_scale, },
+ },
+ },
+ },
+ {
+ .pcm_dev = CAPTURE_2_FROM_I2S_2,
+ .controls = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Switch",
+ .index = 1,
+ .info = snd_ctl_boolean_mono_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_B,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Input Monitor Volume",
+ .index = 1,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = monitor_volume_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL
+ | (1 << 8),
+ .tlv = { .p = monitor_db_scale, },
+ },
+ },
+ },
+ {
+ .pcm_dev = CAPTURE_1_FROM_SPDIF,
+ .controls = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Input Monitor Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_C,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Input Monitor Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = monitor_volume_info,
+ .get = monitor_get,
+ .put = monitor_put,
+ .private_value = OXYGEN_ADC_MONITOR_C_HALF_VOL
+ | (1 << 8),
+ .tlv = { .p = monitor_db_scale, },
+ },
+ },
+ },
+};
+
static const struct snd_kcontrol_new ac97_controls[] = {
AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC),
AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0),
- AC97_VOLUME("Line Capture Volume", 0, AC97_LINE),
AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
AC97_VOLUME("CD Capture Volume", 0, AC97_CD),
AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
@@ -756,6 +941,11 @@ static int add_controls(struct oxygen *chip,
return err;
if (err == 1)
continue;
+ if (!strcmp(template.name, "Master Playback Volume") &&
+ chip->model->dac_tlv) {
+ template.tlv.p = chip->model->dac_tlv;
+ template.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ }
ctl = snd_ctl_new1(&template, chip);
if (!ctl)
return -ENOMEM;
@@ -773,11 +963,26 @@ static int add_controls(struct oxygen *chip,
int oxygen_mixer_init(struct oxygen *chip)
{
+ unsigned int i;
int err;
err = add_controls(chip, controls, ARRAY_SIZE(controls));
if (err < 0)
return err;
+ if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) {
+ err = add_controls(chip, spdif_input_controls,
+ ARRAY_SIZE(spdif_input_controls));
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < ARRAY_SIZE(monitor_controls); ++i) {
+ if (!(chip->model->pcm_dev_cfg & monitor_controls[i].pcm_dev))
+ continue;
+ err = add_controls(chip, monitor_controls[i].controls,
+ ARRAY_SIZE(monitor_controls[i].controls));
+ if (err < 0)
+ return err;
+ }
if (chip->has_ac97_0) {
err = add_controls(chip, ac97_controls,
ARRAY_SIZE(ac97_controls));
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index b70046aca65..b17c405e069 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -119,7 +119,7 @@ static int oxygen_open(struct snd_pcm_substream *substream,
runtime->private_data = (void *)(uintptr_t)channel;
if (channel == PCM_B && chip->has_ac97_1 &&
- (chip->model->used_channels & OXYGEN_CHANNEL_AC97))
+ (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1))
runtime->hw = oxygen_ac97_hardware;
else
runtime->hw = *oxygen_hardware[channel];
@@ -365,7 +365,7 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
return err;
is_ac97 = chip->has_ac97_1 &&
- (chip->model->used_channels & OXYGEN_CHANNEL_AC97);
+ (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1);
spin_lock_irq(&chip->reg_lock);
oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
@@ -640,34 +640,39 @@ int oxygen_pcm_init(struct oxygen *chip)
int outs, ins;
int err;
- outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */
- ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A |
- OXYGEN_CHANNEL_B));
- err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm);
- if (err < 0)
- return err;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops);
- if (chip->model->used_channels & OXYGEN_CHANNEL_A)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &oxygen_rec_a_ops);
- else if (chip->model->used_channels & OXYGEN_CHANNEL_B)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &oxygen_rec_b_ops);
- pcm->private_data = chip;
- pcm->private_free = oxygen_pcm_free;
- strcpy(pcm->name, "Analog");
- snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 512 * 1024, 2048 * 1024);
- if (ins)
- snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 128 * 1024, 256 * 1024);
-
- outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF);
- ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C);
+ outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_0_TO_I2S);
+ ins = !!(chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_1 |
+ CAPTURE_0_FROM_I2S_2));
+ if (outs | ins) {
+ err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm);
+ if (err < 0)
+ return err;
+ if (outs)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &oxygen_multich_ops);
+ if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &oxygen_rec_a_ops);
+ else if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_2)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &oxygen_rec_b_ops);
+ pcm->private_data = chip;
+ pcm->private_free = oxygen_pcm_free;
+ strcpy(pcm->name, "Analog");
+ if (outs)
+ snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 512 * 1024, 2048 * 1024);
+ if (ins)
+ snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 128 * 1024, 256 * 1024);
+ }
+
+ outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF);
+ ins = !!(chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF);
if (outs | ins) {
err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
if (err < 0)
@@ -686,12 +691,13 @@ int oxygen_pcm_init(struct oxygen *chip)
128 * 1024, 256 * 1024);
}
- outs = chip->has_ac97_1 &&
- (chip->model->used_channels & OXYGEN_CHANNEL_AC97);
- ins = outs ||
- (chip->model->used_channels & (OXYGEN_CHANNEL_A |
- OXYGEN_CHANNEL_B))
- == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B);
+ if (chip->has_ac97_1) {
+ outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_2_TO_AC97_1);
+ ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1);
+ } else {
+ outs = 0;
+ ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_I2S_2);
+ }
if (outs | ins) {
err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
2, outs, ins, &pcm);
diff --git a/sound/pci/oxygen/pcm1796.h b/sound/pci/oxygen/pcm1796.h
new file mode 100644
index 00000000000..698bf46c710
--- /dev/null
+++ b/sound/pci/oxygen/pcm1796.h
@@ -0,0 +1,58 @@
+#ifndef PCM1796_H_INCLUDED
+#define PCM1796_H_INCLUDED
+
+/* register 16 */
+#define PCM1796_ATL_MASK 0xff
+/* register 17 */
+#define PCM1796_ATR_MASK 0xff
+/* register 18 */
+#define PCM1796_MUTE 0x01
+#define PCM1796_DME 0x02
+#define PCM1796_DMF_MASK 0x0c
+#define PCM1796_DMF_DISABLED 0x00
+#define PCM1796_DMF_48 0x04
+#define PCM1796_DMF_441 0x08
+#define PCM1796_DMF_32 0x0c
+#define PCM1796_FMT_MASK 0x70
+#define PCM1796_FMT_16_RJUST 0x00
+#define PCM1796_FMT_20_RJUST 0x10
+#define PCM1796_FMT_24_RJUST 0x20
+#define PCM1796_FMT_24_LJUST 0x30
+#define PCM1796_FMT_16_I2S 0x40
+#define PCM1796_FMT_24_I2S 0x50
+#define PCM1796_ATLD 0x80
+/* register 19 */
+#define PCM1796_INZD 0x01
+#define PCM1796_FLT_MASK 0x02
+#define PCM1796_FLT_SHARP 0x00
+#define PCM1796_FLT_SLOW 0x02
+#define PCM1796_DFMS 0x04
+#define PCM1796_OPE 0x10
+#define PCM1796_ATS_MASK 0x60
+#define PCM1796_ATS_1 0x00
+#define PCM1796_ATS_2 0x20
+#define PCM1796_ATS_4 0x40
+#define PCM1796_ATS_8 0x60
+#define PCM1796_REV 0x80
+/* register 20 */
+#define PCM1796_OS_MASK 0x03
+#define PCM1796_OS_64 0x00
+#define PCM1796_OS_32 0x01
+#define PCM1796_OS_128 0x02
+#define PCM1796_CHSL_MASK 0x04
+#define PCM1796_CHSL_LEFT 0x00
+#define PCM1796_CHSL_RIGHT 0x04
+#define PCM1796_MONO 0x08
+#define PCM1796_DFTH 0x10
+#define PCM1796_DSD 0x20
+#define PCM1796_SRST 0x40
+/* register 21 */
+#define PCM1796_PCMZ 0x01
+#define PCM1796_DZ_MASK 0x06
+/* register 22 */
+#define PCM1796_ZFGL 0x01
+#define PCM1796_ZFGR 0x02
+/* register 23 */
+#define PCM1796_ID_MASK 0x1f
+
+#endif
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index d163397b85c..7f84fa5deca 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -18,6 +18,9 @@
*/
/*
+ * Xonar D2/D2X
+ * ------------
+ *
* CMI8788:
*
* SPI 0 -> 1st PCM1796 (front)
@@ -30,10 +33,33 @@
* GPIO 5 <- external power present (D2X only)
* GPIO 7 -> ALT
* GPIO 8 -> enable output to speakers
+ */
+
+/*
+ * Xonar DX
+ * --------
+ *
+ * CMI8788:
+ *
+ * I²C <-> CS4398 (front)
+ * <-> CS4362A (surround, center/LFE, back)
+ *
+ * GPI 0 <- external power present
*
- * CM9780:
+ * GPIO 0 -> enable output to speakers
+ * GPIO 1 -> enable front panel I/O
+ * GPIO 2 -> M0 of CS5361
+ * GPIO 3 -> M1 of CS5361
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
*
- * GPIO 0 -> enable AC'97 bypass (line in -> ADC)
+ * CS4398:
+ *
+ * AD0 <- 1
+ * AD1 <- 1
+ *
+ * CS4362A:
+ *
+ * AD0 <- 0
*/
#include <linux/pci.h>
@@ -47,11 +73,14 @@
#include <sound/tlv.h>
#include "oxygen.h"
#include "cm9780.h"
+#include "pcm1796.h"
+#include "cs4398.h"
+#include "cs4362a.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_DESCRIPTION("Asus AV200 driver");
+MODULE_DESCRIPTION("Asus AVx00 driver");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Asus,AV200}}");
+MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -64,80 +93,44 @@ MODULE_PARM_DESC(id, "ID string");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable card");
+enum {
+ MODEL_D2,
+ MODEL_D2X,
+ MODEL_DX,
+};
+
static struct pci_device_id xonar_ids[] __devinitdata = {
- { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, /* Asus Xonar D2 */
- { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, /* Asus Xonar D2X */
+ { OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 },
+ { OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX },
+ { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
{ }
};
MODULE_DEVICE_TABLE(pci, xonar_ids);
-#define GPIO_CS5381_M_MASK 0x000c
-#define GPIO_CS5381_M_SINGLE 0x0000
-#define GPIO_CS5381_M_DOUBLE 0x0004
-#define GPIO_CS5381_M_QUAD 0x0008
-#define GPIO_EXT_POWER 0x0020
-#define GPIO_ALT 0x0080
-#define GPIO_OUTPUT_ENABLE 0x0100
-
-#define GPIO_LINE_MUTE CM9780_GPO0
-
-/* register 16 */
-#define PCM1796_ATL_MASK 0xff
-/* register 17 */
-#define PCM1796_ATR_MASK 0xff
-/* register 18 */
-#define PCM1796_MUTE 0x01
-#define PCM1796_DME 0x02
-#define PCM1796_DMF_MASK 0x0c
-#define PCM1796_DMF_DISABLED 0x00
-#define PCM1796_DMF_48 0x04
-#define PCM1796_DMF_441 0x08
-#define PCM1796_DMF_32 0x0c
-#define PCM1796_FMT_MASK 0x70
-#define PCM1796_FMT_16_RJUST 0x00
-#define PCM1796_FMT_20_RJUST 0x10
-#define PCM1796_FMT_24_RJUST 0x20
-#define PCM1796_FMT_24_LJUST 0x30
-#define PCM1796_FMT_16_I2S 0x40
-#define PCM1796_FMT_24_I2S 0x50
-#define PCM1796_ATLD 0x80
-/* register 19 */
-#define PCM1796_INZD 0x01
-#define PCM1796_FLT_MASK 0x02
-#define PCM1796_FLT_SHARP 0x00
-#define PCM1796_FLT_SLOW 0x02
-#define PCM1796_DFMS 0x04
-#define PCM1796_OPE 0x10
-#define PCM1796_ATS_MASK 0x60
-#define PCM1796_ATS_1 0x00
-#define PCM1796_ATS_2 0x20
-#define PCM1796_ATS_4 0x40
-#define PCM1796_ATS_8 0x60
-#define PCM1796_REV 0x80
-/* register 20 */
-#define PCM1796_OS_MASK 0x03
-#define PCM1796_OS_64 0x00
-#define PCM1796_OS_32 0x01
-#define PCM1796_OS_128 0x02
-#define PCM1796_CHSL_MASK 0x04
-#define PCM1796_CHSL_LEFT 0x00
-#define PCM1796_CHSL_RIGHT 0x04
-#define PCM1796_MONO 0x08
-#define PCM1796_DFTH 0x10
-#define PCM1796_DSD 0x20
-#define PCM1796_SRST 0x40
-/* register 21 */
-#define PCM1796_PCMZ 0x01
-#define PCM1796_DZ_MASK 0x06
-/* register 22 */
-#define PCM1796_ZFGL 0x01
-#define PCM1796_ZFGR 0x02
-/* register 23 */
-#define PCM1796_ID_MASK 0x1f
+#define GPIO_CS53x1_M_MASK 0x000c
+#define GPIO_CS53x1_M_SINGLE 0x0000
+#define GPIO_CS53x1_M_DOUBLE 0x0004
+#define GPIO_CS53x1_M_QUAD 0x0008
+
+#define GPIO_D2X_EXT_POWER 0x0020
+#define GPIO_D2_ALT 0x0080
+#define GPIO_D2_OUTPUT_ENABLE 0x0100
+
+#define GPI_DX_EXT_POWER 0x01
+#define GPIO_DX_OUTPUT_ENABLE 0x0001
+#define GPIO_DX_FRONT_PANEL 0x0002
+#define GPIO_DX_INPUT_ROUTE 0x0100
+
+#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 {
- u8 is_d2x;
+ unsigned int anti_pop_delay;
+ u16 output_enable_bit;
+ u8 ext_power_reg;
+ u8 ext_power_int_reg;
+ u8 ext_power_bit;
u8 has_power;
};
@@ -156,62 +149,157 @@ static void pcm1796_write(struct oxygen *chip, unsigned int codec,
(reg << 8) | value);
}
-static void xonar_init(struct oxygen *chip)
+static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
+{
+ oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
+}
+
+static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
+{
+ oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
+}
+
+static void xonar_common_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ if (data->ext_power_reg) {
+ oxygen_set_bits8(chip, data->ext_power_int_reg,
+ data->ext_power_bit);
+ chip->interrupt_mask |= OXYGEN_INT_GPIO;
+ data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
+ & data->ext_power_bit);
+ }
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
+ oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
+ msleep(data->anti_pop_delay);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
+static void xonar_d2_init(struct oxygen *chip)
{
struct xonar_data *data = chip->model_data;
unsigned int i;
- data->is_d2x = chip->pci->subsystem_device == 0x82b7;
+ data->anti_pop_delay = 300;
+ data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
for (i = 0; i < 4; ++i) {
- pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD);
+ pcm1796_write(chip, i, 18, PCM1796_MUTE | PCM1796_DMF_DISABLED |
+ PCM1796_FMT_24_LJUST | PCM1796_ATLD);
pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
pcm1796_write(chip, i, 20, PCM1796_OS_64);
pcm1796_write(chip, i, 21, 0);
- pcm1796_write(chip, i, 16, 0xff); /* set ATL/ATR after ATLD */
- pcm1796_write(chip, i, 17, 0xff);
+ pcm1796_write(chip, i, 16, 0x0f); /* set ATL/ATR after ATLD */
+ pcm1796_write(chip, i, 17, 0x0f);
}
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_CS5381_M_MASK | GPIO_ALT);
- oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
- GPIO_CS5381_M_SINGLE,
- GPIO_CS5381_M_MASK | GPIO_ALT);
- if (data->is_d2x) {
- oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_EXT_POWER);
- oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK,
- GPIO_EXT_POWER);
- chip->interrupt_mask |= OXYGEN_INT_GPIO;
- data->has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
- & GPIO_EXT_POWER);
- }
- oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
- oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE);
- msleep(300);
- oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE);
- oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT);
+
+ xonar_common_init(chip);
snd_component_add(chip->card, "PCM1796");
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 xonar_dx_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ data->anti_pop_delay = 800;
+ 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;
+
+ oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+ OXYGEN_2WIRE_LENGTH_8 |
+ OXYGEN_2WIRE_INTERRUPT_MASK |
+ OXYGEN_2WIRE_SPEED_FAST);
+
+ /* set CPEN (control port mode) and power down */
+ cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
+ cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
+ /* configure */
+ cs4398_write(chip, 2, CS4398_FM_SINGLE |
+ CS4398_DEM_NONE | CS4398_DIF_LJUST);
+ cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
+ cs4398_write(chip, 4, CS4398_MUTEP_LOW | CS4398_PAMUTE);
+ cs4398_write(chip, 5, 0xfe);
+ cs4398_write(chip, 6, 0xfe);
+ cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP |
+ CS4398_ZERO_CROSS | CS4398_SOFT_RAMP);
+ cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
+ cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
+ CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
+ cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE);
+ cs4362a_write(chip, 0x05, 0);
+ cs4362a_write(chip, 0x06, CS4362A_FM_SINGLE |
+ CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
+ cs4362a_write(chip, 0x07, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x08, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x09, CS4362A_FM_SINGLE |
+ CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
+ cs4362a_write(chip, 0x0a, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x0b, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x0c, CS4362A_FM_SINGLE |
+ CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
+ cs4362a_write(chip, 0x0d, 0x7f | CS4362A_MUTE);
+ cs4362a_write(chip, 0x0e, 0x7f | CS4362A_MUTE);
+ /* clear power down */
+ cs4398_write(chip, 8, CS4398_CPEN);
+ cs4362a_write(chip, 0x01, CS4362A_CPEN);
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+ GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
+
+ xonar_common_init(chip);
+
+ snd_component_add(chip->card, "CS4398");
+ snd_component_add(chip->card, "CS4362A");
+ snd_component_add(chip->card, "CS5361");
+}
+
static void xonar_cleanup(struct oxygen *chip)
{
- oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
+ struct xonar_data *data = chip->model_data;
+
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
+static void xonar_dx_cleanup(struct oxygen *chip)
+{
+ xonar_cleanup(chip);
+ cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
+ oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
}
static void set_pcm1796_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
-#if 0
unsigned int i;
u8 value;
value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
for (i = 0; i < 4; ++i)
pcm1796_write(chip, i, 20, value);
-#endif
}
static void update_pcm1796_volume(struct oxygen *chip)
@@ -236,19 +324,73 @@ static void update_pcm1796_mute(struct oxygen *chip)
pcm1796_write(chip, i, 18, value);
}
-static void set_cs5381_params(struct oxygen *chip,
+static void set_cs53x1_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
unsigned int value;
if (params_rate(params) <= 54000)
- value = GPIO_CS5381_M_SINGLE;
+ value = GPIO_CS53x1_M_SINGLE;
else if (params_rate(params) <= 108000)
- value = GPIO_CS5381_M_DOUBLE;
+ value = GPIO_CS53x1_M_DOUBLE;
else
- value = GPIO_CS5381_M_QUAD;
+ value = GPIO_CS53x1_M_QUAD;
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
- value, GPIO_CS5381_M_MASK);
+ value, GPIO_CS53x1_M_MASK);
+}
+
+static void set_cs43xx_params(struct oxygen *chip,
+ struct snd_pcm_hw_params *params)
+{
+ u8 fm_cs4398, fm_cs4362a;
+
+ fm_cs4398 = CS4398_DEM_NONE | CS4398_DIF_LJUST;
+ fm_cs4362a = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+ if (params_rate(params) <= 50000) {
+ fm_cs4398 |= CS4398_FM_SINGLE;
+ fm_cs4362a |= CS4362A_FM_SINGLE;
+ } else if (params_rate(params) <= 100000) {
+ fm_cs4398 |= CS4398_FM_DOUBLE;
+ fm_cs4362a |= CS4362A_FM_DOUBLE;
+ } else {
+ fm_cs4398 |= CS4398_FM_QUAD;
+ fm_cs4362a |= CS4362A_FM_QUAD;
+ }
+ cs4398_write(chip, 2, fm_cs4398);
+ cs4362a_write(chip, 0x06, fm_cs4362a);
+ cs4362a_write(chip, 0x09, fm_cs4362a);
+ cs4362a_write(chip, 0x0c, fm_cs4362a);
+}
+
+static void update_cs4362a_volumes(struct oxygen *chip)
+{
+ u8 mute;
+
+ mute = chip->dac_mute ? CS4362A_MUTE : 0;
+ cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute);
+ cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute);
+ cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute);
+ cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute);
+ cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute);
+ cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute);
+}
+
+static void update_cs43xx_volume(struct oxygen *chip)
+{
+ cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2);
+ cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2);
+ update_cs4362a_volumes(chip);
+}
+
+static void update_cs43xx_mute(struct oxygen *chip)
+{
+ u8 reg;
+
+ reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
+ if (chip->dac_mute)
+ reg |= CS4398_MUTE_B | CS4398_MUTE_A;
+ cs4398_write(chip, 4, reg);
+ update_cs4362a_volumes(chip);
}
static void xonar_gpio_changed(struct oxygen *chip)
@@ -256,10 +398,8 @@ static void xonar_gpio_changed(struct oxygen *chip)
struct xonar_data *data = chip->model_data;
u8 has_power;
- if (!data->is_d2x)
- return;
- has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
- & GPIO_EXT_POWER);
+ has_power = !!(oxygen_read8(chip, data->ext_power_reg)
+ & data->ext_power_bit);
if (has_power != data->has_power) {
data->has_power = has_power;
if (has_power) {
@@ -272,66 +412,13 @@ static void xonar_gpio_changed(struct oxygen *chip)
}
}
-static void mute_ac97_ctl(struct oxygen *chip, unsigned int control)
-{
- unsigned int index = chip->controls[control]->private_value & 0xff;
- u16 value;
-
- value = oxygen_read_ac97(chip, 0, index);
- if (!(value & 0x8000)) {
- oxygen_write_ac97(chip, 0, index, value | 0x8000);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->controls[control]->id);
- }
-}
-
-static void xonar_ac97_switch_hook(struct oxygen *chip, unsigned int codec,
- unsigned int reg, int mute)
-{
- if (codec != 0)
- return;
- /* line-in is exclusive */
- switch (reg) {
- case AC97_LINE:
- oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
- mute ? GPIO_LINE_MUTE : 0,
- GPIO_LINE_MUTE);
- if (!mute) {
- mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH);
- mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH);
- mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH);
- }
- break;
- case AC97_MIC:
- case AC97_CD:
- case AC97_VIDEO:
- case AC97_AUX:
- if (!mute) {
- oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS,
- GPIO_LINE_MUTE);
- mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH);
- }
- break;
- }
-}
-
-static int pcm1796_volume_info(struct snd_kcontrol *ctl,
- struct snd_ctl_elem_info *info)
-{
- info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- info->count = 8;
- info->value.integer.min = 0x0f;
- info->value.integer.max = 0xff;
- return 0;
-}
-
static int alt_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
value->value.integer.value[0] =
- !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_ALT);
+ !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_D2_ALT);
return 0;
}
@@ -345,9 +432,9 @@ static int alt_switch_put(struct snd_kcontrol *ctl,
spin_lock_irq(&chip->reg_lock);
old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
if (value->value.integer.value[0])
- new_bits = old_bits | GPIO_ALT;
+ new_bits = old_bits | GPIO_D2_ALT;
else
- new_bits = old_bits & ~GPIO_ALT;
+ new_bits = old_bits & ~GPIO_D2_ALT;
changed = new_bits != old_bits;
if (changed)
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
@@ -363,20 +450,68 @@ static const struct snd_kcontrol_new alt_switch = {
.put = alt_switch_put,
};
+static int front_panel_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+
+ value->value.integer.value[0] =
+ !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DX_FRONT_PANEL);
+ return 0;
+}
+
+static int front_panel_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 old_reg, new_reg;
+
+ spin_lock_irq(&chip->reg_lock);
+ old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ if (value->value.integer.value[0])
+ new_reg = old_reg | GPIO_DX_FRONT_PANEL;
+ else
+ new_reg = old_reg & ~GPIO_DX_FRONT_PANEL;
+ oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
+ spin_unlock_irq(&chip->reg_lock);
+ return old_reg != new_reg;
+}
+
+static const struct snd_kcontrol_new front_panel_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Front Panel Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = front_panel_get,
+ .put = front_panel_put,
+};
+
+static void xonar_dx_ac97_switch(struct oxygen *chip,
+ unsigned int reg, unsigned int mute)
+{
+ if (reg == AC97_LINE) {
+ spin_lock_irq(&chip->reg_lock);
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ mute ? GPIO_DX_INPUT_ROUTE : 0,
+ GPIO_DX_INPUT_ROUTE);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+}
+
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 int xonar_control_filter(struct snd_kcontrol_new *template)
+static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
{
- if (!strcmp(template->name, "Master Playback Volume")) {
- template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- template->info = pcm1796_volume_info,
- template->tlv.p = pcm1796_db_scale;
- } else if (!strncmp(template->name, "CD Capture ", 11)) {
+ if (!strncmp(template->name, "CD Capture ", 11))
/* CD in is actually connected to the video in pin */
template->private_value ^= AC97_CD ^ AC97_VIDEO;
- } else if (!strcmp(template->name, "Line Capture Volume")) {
- return 1; /* line-in bypasses the AC'97 mixer */
- }
+ return 0;
+}
+
+static int xonar_dx_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strncmp(template->name, "CD Capture ", 11))
+ return 1; /* no CD input */
return 0;
}
@@ -385,30 +520,96 @@ static int xonar_mixer_init(struct oxygen *chip)
return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
}
-static const struct oxygen_model model_xonar = {
- .shortname = "Asus AV200",
- .longname = "Asus Virtuoso 200",
- .chip = "AV200",
- .owner = THIS_MODULE,
- .init = xonar_init,
- .control_filter = xonar_control_filter,
- .mixer_init = xonar_mixer_init,
- .cleanup = xonar_cleanup,
- .set_dac_params = set_pcm1796_params,
- .set_adc_params = set_cs5381_params,
- .update_dac_volume = update_pcm1796_volume,
- .update_dac_mute = update_pcm1796_mute,
- .ac97_switch_hook = xonar_ac97_switch_hook,
- .gpio_changed = xonar_gpio_changed,
- .model_data_size = sizeof(struct xonar_data),
- .dac_channels = 8,
- .used_channels = OXYGEN_CHANNEL_B |
- OXYGEN_CHANNEL_C |
- OXYGEN_CHANNEL_SPDIF |
- OXYGEN_CHANNEL_MULTICH,
- .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
- .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
- .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+static int xonar_dx_mixer_init(struct oxygen *chip)
+{
+ return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
+}
+
+static const struct oxygen_model xonar_models[] = {
+ [MODEL_D2] = {
+ .shortname = "Xonar D2",
+ .longname = "Asus Virtuoso 200",
+ .chip = "AV200",
+ .owner = THIS_MODULE,
+ .init = xonar_d2_init,
+ .control_filter = xonar_d2_control_filter,
+ .mixer_init = xonar_mixer_init,
+ .cleanup = xonar_cleanup,
+ .set_dac_params = set_pcm1796_params,
+ .set_adc_params = set_cs53x1_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .dac_tlv = pcm1796_db_scale,
+ .model_data_size = sizeof(struct xonar_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF,
+ .dac_channels = 8,
+ .dac_volume_min = 0x0f,
+ .dac_volume_max = 0xff,
+ .misc_flags = OXYGEN_MISC_MIDI,
+ .function_flags = OXYGEN_FUNCTION_SPI |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ },
+ [MODEL_D2X] = {
+ .shortname = "Xonar D2X",
+ .longname = "Asus Virtuoso 200",
+ .chip = "AV200",
+ .owner = THIS_MODULE,
+ .init = xonar_d2x_init,
+ .control_filter = xonar_d2_control_filter,
+ .mixer_init = xonar_mixer_init,
+ .cleanup = xonar_cleanup,
+ .set_dac_params = set_pcm1796_params,
+ .set_adc_params = set_cs53x1_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .gpio_changed = xonar_gpio_changed,
+ .dac_tlv = pcm1796_db_scale,
+ .model_data_size = sizeof(struct xonar_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF,
+ .dac_channels = 8,
+ .dac_volume_min = 0x0f,
+ .dac_volume_max = 0xff,
+ .misc_flags = OXYGEN_MISC_MIDI,
+ .function_flags = OXYGEN_FUNCTION_SPI |
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ },
+ [MODEL_DX] = {
+ .shortname = "Xonar DX",
+ .longname = "Asus Virtuoso 100",
+ .chip = "AV200",
+ .owner = THIS_MODULE,
+ .init = xonar_dx_init,
+ .control_filter = xonar_dx_control_filter,
+ .mixer_init = xonar_dx_mixer_init,
+ .cleanup = xonar_dx_cleanup,
+ .set_dac_params = set_cs43xx_params,
+ .set_adc_params = set_cs53x1_params,
+ .update_dac_volume = update_cs43xx_volume,
+ .update_dac_mute = update_cs43xx_mute,
+ .gpio_changed = xonar_gpio_changed,
+ .ac97_switch = xonar_dx_ac97_switch,
+ .dac_tlv = cs4362a_db_scale,
+ .model_data_size = sizeof(struct xonar_data),
+ .pcm_dev_cfg = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2,
+ .dac_channels = 8,
+ .dac_volume_min = 0,
+ .dac_volume_max = 127,
+ .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,
@@ -423,7 +624,8 @@ static int __devinit xonar_probe(struct pci_dev *pci,
++dev;
return -ENOENT;
}
- err = oxygen_pci_probe(pci, index[dev], id[dev], 1, &model_xonar);
+ err = oxygen_pci_probe(pci, index[dev], id[dev],
+ &xonar_models[pci_id->driver_data]);
if (err >= 0)
++dev;
return err;
diff --git a/sound/pci/oxygen/wm8785.h b/sound/pci/oxygen/wm8785.h
new file mode 100644
index 00000000000..8c23e315ae6
--- /dev/null
+++ b/sound/pci/oxygen/wm8785.h
@@ -0,0 +1,45 @@
+#ifndef WM8785_H_INCLUDED
+#define WM8785_H_INCLUDED
+
+#define WM8785_R0 0
+#define WM8785_R1 1
+#define WM8785_R2 2
+#define WM8785_R7 7
+
+/* R0 */
+#define WM8785_MCR_MASK 0x007
+#define WM8785_MCR_SLAVE 0x000
+#define WM8785_MCR_MASTER_128 0x001
+#define WM8785_MCR_MASTER_192 0x002
+#define WM8785_MCR_MASTER_256 0x003
+#define WM8785_MCR_MASTER_384 0x004
+#define WM8785_MCR_MASTER_512 0x005
+#define WM8785_MCR_MASTER_768 0x006
+#define WM8785_OSR_MASK 0x018
+#define WM8785_OSR_SINGLE 0x000
+#define WM8785_OSR_DOUBLE 0x008
+#define WM8785_OSR_QUAD 0x010
+#define WM8785_FORMAT_MASK 0x060
+#define WM8785_FORMAT_RJUST 0x000
+#define WM8785_FORMAT_LJUST 0x020
+#define WM8785_FORMAT_I2S 0x040
+#define WM8785_FORMAT_DSP 0x060
+/* R1 */
+#define WM8785_WL_MASK 0x003
+#define WM8785_WL_16 0x000
+#define WM8785_WL_20 0x001
+#define WM8785_WL_24 0x002
+#define WM8785_WL_32 0x003
+#define WM8785_LRP 0x004
+#define WM8785_BCLKINV 0x008
+#define WM8785_LRSWAP 0x010
+#define WM8785_DEVNO_MASK 0x0e0
+/* R2 */
+#define WM8785_HPFR 0x001
+#define WM8785_HPFL 0x002
+#define WM8785_SDODIS 0x004
+#define WM8785_PWRDNR 0x008
+#define WM8785_PWRDNL 0x010
+#define WM8785_TDM_MASK 0x1c0
+
+#endif