diff options
Diffstat (limited to 'sound/pci/oxygen')
| -rw-r--r-- | sound/pci/oxygen/Makefile | 4 | ||||
| -rw-r--r-- | sound/pci/oxygen/cs4245.h | 110 | ||||
| -rw-r--r-- | sound/pci/oxygen/hifier.c | 239 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen.c | 401 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen.h | 28 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen_io.c | 34 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen_lib.c | 116 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen_mixer.c | 138 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen_pcm.c | 61 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen_regs.h | 17 | ||||
| -rw-r--r-- | sound/pci/oxygen/virtuoso.c | 36 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar.h | 2 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_cs43xx.c | 89 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_dg.c | 295 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_dg.h | 56 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_dg_mixer.c | 477 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_hdmi.c | 4 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_lib.c | 10 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_pcm179x.c | 483 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_wm87x6.c | 332 | 
20 files changed, 2152 insertions, 780 deletions
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile index acd8f15f7bf..8f4c409f7e4 100644 --- a/sound/pci/oxygen/Makefile +++ b/sound/pci/oxygen/Makefile @@ -1,10 +1,8 @@  snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o -snd-hifier-objs := hifier.o -snd-oxygen-objs := oxygen.o +snd-oxygen-objs := oxygen.o xonar_dg_mixer.o xonar_dg.o  snd-virtuoso-objs := virtuoso.o xonar_lib.o \  	xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o  obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o -obj-$(CONFIG_SND_HIFIER) += snd-hifier.o  obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o  obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o diff --git a/sound/pci/oxygen/cs4245.h b/sound/pci/oxygen/cs4245.h new file mode 100644 index 00000000000..99098657695 --- /dev/null +++ b/sound/pci/oxygen/cs4245.h @@ -0,0 +1,110 @@ +#define CS4245_CHIP_ID		0x01 +#define CS4245_POWER_CTRL	0x02 +#define CS4245_DAC_CTRL_1	0x03 +#define CS4245_ADC_CTRL		0x04 +#define CS4245_MCLK_FREQ	0x05 +#define CS4245_SIGNAL_SEL	0x06 +#define CS4245_PGA_B_CTRL	0x07 +#define CS4245_PGA_A_CTRL	0x08 +#define CS4245_ANALOG_IN	0x09 +#define CS4245_DAC_A_CTRL	0x0a +#define CS4245_DAC_B_CTRL	0x0b +#define CS4245_DAC_CTRL_2	0x0c +#define CS4245_INT_STATUS	0x0d +#define CS4245_INT_MASK		0x0e +#define CS4245_INT_MODE_MSB	0x0f +#define CS4245_INT_MODE_LSB	0x10 + +/* Chip ID */ +#define CS4245_CHIP_PART_MASK	0xf0 +#define CS4245_CHIP_REV_MASK	0x0f + +/* Power Control */ +#define CS4245_FREEZE		0x80 +#define CS4245_PDN_MIC		0x08 +#define CS4245_PDN_ADC		0x04 +#define CS4245_PDN_DAC		0x02 +#define CS4245_PDN		0x01 + +/* DAC Control */ +#define CS4245_DAC_FM_MASK	0xc0 +#define CS4245_DAC_FM_SINGLE	0x00 +#define CS4245_DAC_FM_DOUBLE	0x40 +#define CS4245_DAC_FM_QUAD	0x80 +#define CS4245_DAC_DIF_MASK	0x30 +#define CS4245_DAC_DIF_LJUST	0x00 +#define CS4245_DAC_DIF_I2S	0x10 +#define CS4245_DAC_DIF_RJUST_16	0x20 +#define CS4245_DAC_DIF_RJUST_24	0x30 +#define CS4245_RESERVED_1	0x08 +#define CS4245_MUTE_DAC		0x04 +#define CS4245_DEEMPH		0x02 +#define CS4245_DAC_MASTER	0x01 + +/* ADC Control */ +#define CS4245_ADC_FM_MASK	0xc0 +#define CS4245_ADC_FM_SINGLE	0x00 +#define CS4245_ADC_FM_DOUBLE	0x40 +#define CS4245_ADC_FM_QUAD	0x80 +#define CS4245_ADC_DIF_MASK	0x10 +#define CS4245_ADC_DIF_LJUST	0x00 +#define CS4245_ADC_DIF_I2S	0x10 +#define CS4245_MUTE_ADC		0x04 +#define CS4245_HPF_FREEZE	0x02 +#define CS4245_ADC_MASTER	0x01 + +/* MCLK Frequency */ +#define CS4245_MCLK1_MASK	0x70 +#define CS4245_MCLK1_SHIFT	4 +#define CS4245_MCLK2_MASK	0x07 +#define CS4245_MCLK2_SHIFT	0 +#define CS4245_MCLK_1		0 +#define CS4245_MCLK_1_5		1 +#define CS4245_MCLK_2		2 +#define CS4245_MCLK_3		3 +#define CS4245_MCLK_4		4 + +/* Signal Selection */ +#define CS4245_A_OUT_SEL_MASK	0x60 +#define CS4245_A_OUT_SEL_HIZ	0x00 +#define CS4245_A_OUT_SEL_DAC	0x20 +#define CS4245_A_OUT_SEL_PGA	0x40 +#define CS4245_LOOP		0x02 +#define CS4245_ASYNCH		0x01 + +/* Channel B/A PGA Control */ +#define CS4245_PGA_GAIN_MASK	0x3f + +/* ADC Input Control */ +#define CS4245_PGA_SOFT		0x10 +#define CS4245_PGA_ZERO		0x08 +#define CS4245_SEL_MASK		0x07 +#define CS4245_SEL_MIC		0x00 +#define CS4245_SEL_INPUT_1	0x01 +#define CS4245_SEL_INPUT_2	0x02 +#define CS4245_SEL_INPUT_3	0x03 +#define CS4245_SEL_INPUT_4	0x04 +#define CS4245_SEL_INPUT_5	0x05 +#define CS4245_SEL_INPUT_6	0x06 + +/* DAC Channel A/B Volume Control */ +#define CS4245_VOL_MASK		0xff + +/* DAC Control 2 */ +#define CS4245_DAC_SOFT		0x80 +#define CS4245_DAC_ZERO		0x40 +#define CS4245_INVERT_DAC	0x20 +#define CS4245_INT_ACTIVE_HIGH	0x01 + +/* Interrupt Status/Mask/Mode */ +#define CS4245_ADC_CLK_ERR	0x08 +#define CS4245_DAC_CLK_ERR	0x04 +#define CS4245_ADC_OVFL		0x02 +#define CS4245_ADC_UNDRFL	0x01 + +#define CS4245_SPI_ADDRESS_S	(0x9e << 16) +#define CS4245_SPI_WRITE_S	(0 << 16) + +#define CS4245_SPI_ADDRESS	0x9e +#define CS4245_SPI_WRITE	0 +#define CS4245_SPI_READ		1 diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c deleted file mode 100644 index 5a87d683691..00000000000 --- a/sound/pci/oxygen/hifier.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * C-Media CMI8788 driver for the MediaTek/TempoTec HiFier Fantasia - * - * Copyright (c) Clemens Ladisch <clemens@ladisch.de> - * - * - *  This driver is free software; you can redistribute it and/or modify - *  it under the terms of the GNU General Public License, version 2. - * - *  This driver is distributed in the hope that it will be useful, - *  but WITHOUT ANY WARRANTY; without even the implied warranty of - *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *  GNU General Public License for more details. - * - *  You should have received a copy of the GNU General Public License - *  along with this driver; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA - */ - -/* - * CMI8788: - * - * SPI 0 -> AK4396 - */ - -#include <linux/delay.h> -#include <linux/pci.h> -#include <sound/control.h> -#include <sound/core.h> -#include <sound/initval.h> -#include <sound/pcm.h> -#include <sound/tlv.h> -#include "oxygen.h" -#include "ak4396.h" - -MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); -MODULE_DESCRIPTION("TempoTec HiFier driver"); -MODULE_LICENSE("GPL v2"); - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; - -module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "card index"); -module_param_array(id, charp, NULL, 0444); -MODULE_PARM_DESC(id, "ID string"); -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(enable, "enable card"); - -static DEFINE_PCI_DEVICE_TABLE(hifier_ids) = { -	{ OXYGEN_PCI_SUBID(0x14c3, 0x1710) }, -	{ OXYGEN_PCI_SUBID(0x14c3, 0x1711) }, -	{ OXYGEN_PCI_SUBID_BROKEN_EEPROM }, -	{ } -}; -MODULE_DEVICE_TABLE(pci, hifier_ids); - -struct hifier_data { -	u8 ak4396_regs[5]; -}; - -static void ak4396_write(struct oxygen *chip, u8 reg, u8 value) -{ -	struct hifier_data *data = chip->model_data; - -	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER  | -			 OXYGEN_SPI_DATA_LENGTH_2 | -			 OXYGEN_SPI_CLOCK_160 | -			 (0 << OXYGEN_SPI_CODEC_SHIFT) | -			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI, -			 AK4396_WRITE | (reg << 8) | value); -	data->ak4396_regs[reg] = value; -} - -static void ak4396_write_cached(struct oxygen *chip, u8 reg, u8 value) -{ -	struct hifier_data *data = chip->model_data; - -	if (value != data->ak4396_regs[reg]) -		ak4396_write(chip, reg, value); -} - -static void hifier_registers_init(struct oxygen *chip) -{ -	struct hifier_data *data = chip->model_data; - -	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); -	ak4396_write(chip, AK4396_CONTROL_2, -		     data->ak4396_regs[AK4396_CONTROL_2]); -	ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM); -	ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]); -	ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]); -} - -static void hifier_init(struct oxygen *chip) -{ -	struct hifier_data *data = chip->model_data; - -	data->ak4396_regs[AK4396_CONTROL_2] = -		AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; -	hifier_registers_init(chip); - -	snd_component_add(chip->card, "AK4396"); -	snd_component_add(chip->card, "CS5340"); -} - -static void hifier_cleanup(struct oxygen *chip) -{ -} - -static void hifier_resume(struct oxygen *chip) -{ -	hifier_registers_init(chip); -} - -static void set_ak4396_params(struct oxygen *chip, -			       struct snd_pcm_hw_params *params) -{ -	struct hifier_data *data = chip->model_data; -	u8 value; - -	value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_DFS_MASK; -	if (params_rate(params) <= 54000) -		value |= AK4396_DFS_NORMAL; -	else if (params_rate(params) <= 108000) -		value |= AK4396_DFS_DOUBLE; -	else -		value |= AK4396_DFS_QUAD; - -	msleep(1); /* wait for the new MCLK to become stable */ - -	if (value != data->ak4396_regs[AK4396_CONTROL_2]) { -		ak4396_write(chip, AK4396_CONTROL_1, -			     AK4396_DIF_24_MSB); -		ak4396_write(chip, AK4396_CONTROL_2, value); -		ak4396_write(chip, AK4396_CONTROL_1, -			     AK4396_DIF_24_MSB | AK4396_RSTN); -	} -} - -static void update_ak4396_volume(struct oxygen *chip) -{ -	ak4396_write_cached(chip, AK4396_LCH_ATT, chip->dac_volume[0]); -	ak4396_write_cached(chip, AK4396_RCH_ATT, chip->dac_volume[1]); -} - -static void update_ak4396_mute(struct oxygen *chip) -{ -	struct hifier_data *data = chip->model_data; -	u8 value; - -	value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_SMUTE; -	if (chip->dac_mute) -		value |= AK4396_SMUTE; -	ak4396_write_cached(chip, AK4396_CONTROL_2, value); -} - -static void set_cs5340_params(struct oxygen *chip, -			      struct snd_pcm_hw_params *params) -{ -} - -static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); - -static const struct oxygen_model model_hifier = { -	.shortname = "C-Media CMI8787", -	.longname = "C-Media Oxygen HD Audio", -	.chip = "CMI8788", -	.init = hifier_init, -	.cleanup = hifier_cleanup, -	.resume = hifier_resume, -	.get_i2s_mclk = oxygen_default_i2s_mclk, -	.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), -	.device_config = PLAYBACK_0_TO_I2S | -			 PLAYBACK_1_TO_SPDIF | -			 CAPTURE_0_FROM_I2S_1, -	.dac_channels = 2, -	.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, -}; - -static int __devinit get_hifier_model(struct oxygen *chip, -				      const struct pci_device_id *id) -{ -	chip->model = model_hifier; -	return 0; -} - -static int __devinit hifier_probe(struct pci_dev *pci, -				  const struct pci_device_id *pci_id) -{ -	static int dev; -	int err; - -	if (dev >= SNDRV_CARDS) -		return -ENODEV; -	if (!enable[dev]) { -		++dev; -		return -ENOENT; -	} -	err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE, -			       hifier_ids, get_hifier_model); -	if (err >= 0) -		++dev; -	return err; -} - -static struct pci_driver hifier_driver = { -	.name = "CMI8787HiFier", -	.id_table = hifier_ids, -	.probe = hifier_probe, -	.remove = __devexit_p(oxygen_pci_remove), -#ifdef CONFIG_PM -	.suspend = oxygen_pci_suspend, -	.resume = oxygen_pci_resume, -#endif -}; - -static int __init alsa_card_hifier_init(void) -{ -	return pci_register_driver(&hifier_driver); -} - -static void __exit alsa_card_hifier_exit(void) -{ -	pci_unregister_driver(&hifier_driver); -} - -module_init(alsa_card_hifier_init) -module_exit(alsa_card_hifier_exit) diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 98a8eb3c92f..ada6c256378 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -20,43 +20,61 @@  /*   * CMI8788:   * - * SPI 0 -> 1st AK4396 (front) - * SPI 1 -> 2nd AK4396 (surround) - * SPI 2 -> 3rd AK4396 (center/LFE) - * SPI 3 -> WM8785 - * SPI 4 -> 4th AK4396 (back) + *   SPI 0 -> 1st AK4396 (front) + *   SPI 1 -> 2nd AK4396 (surround) + *   SPI 2 -> 3rd AK4396 (center/LFE) + *   SPI 3 -> WM8785 + *   SPI 4 -> 4th AK4396 (back)   * - * GPIO 0 -> DFS0 of AK5385 - * GPIO 1 -> DFS1 of AK5385 - * GPIO 8 -> enable headphone amplifier on HT-Omega models + *   GPIO 0 -> DFS0 of AK5385 + *   GPIO 1 -> DFS1 of AK5385 + * + * X-Meridian models: + *   GPIO 4 -> enable extension S/PDIF input + *   GPIO 6 -> enable on-board S/PDIF input + * + * Claro models: + *   GPIO 6 -> S/PDIF from optical (0) or coaxial (1) input + *   GPIO 8 -> enable headphone amplifier   *   * CM9780:   * - * GPO 0 -> route line-in (0) or AC97 output (1) to ADC input + *   LINE_OUT -> input of ADC + * + *   AUX_IN <- aux + *   CD_IN  <- CD + *   MIC_IN <- mic + * + *   GPO 0 -> route line-in (0) or AC97 output (1) to ADC input   */  #include <linux/delay.h>  #include <linux/mutex.h>  #include <linux/pci.h> +#include <linux/module.h>  #include <sound/ac97_codec.h>  #include <sound/control.h>  #include <sound/core.h> +#include <sound/info.h>  #include <sound/initval.h>  #include <sound/pcm.h>  #include <sound/pcm_params.h>  #include <sound/tlv.h>  #include "oxygen.h" +#include "xonar_dg.h"  #include "ak4396.h"  #include "wm8785.h"  MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");  MODULE_DESCRIPTION("C-Media CMI8788 driver");  MODULE_LICENSE("GPL v2"); -MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}"); +MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8786}" +			",{C-Media,CMI8787}" +			",{C-Media,CMI8788}}");  static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;  static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;  module_param_array(index, int, NULL, 0444);  MODULE_PARM_DESC(index, "card index"); @@ -66,24 +84,49 @@ module_param_array(enable, bool, NULL, 0444);  MODULE_PARM_DESC(enable, "enable card");  enum { -	MODEL_CMEDIA_REF,	/* C-Media's reference design */ -	MODEL_MERIDIAN,		/* AuzenTech X-Meridian */ -	MODEL_CLARO,		/* HT-Omega Claro */ -	MODEL_CLARO_HALO,	/* HT-Omega Claro halo */ +	MODEL_CMEDIA_REF, +	MODEL_MERIDIAN, +	MODEL_MERIDIAN_2G, +	MODEL_CLARO, +	MODEL_CLARO_HALO, +	MODEL_FANTASIA, +	MODEL_SERENADE, +	MODEL_2CH_OUTPUT, +	MODEL_HG2PCI, +	MODEL_XONAR_DG, +	MODEL_XONAR_DGX,  };  static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = { +	/* C-Media's reference design */  	{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF }, +	{ OXYGEN_PCI_SUBID(0x10b0, 0x0217), .driver_data = MODEL_CMEDIA_REF },  	{ OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },  	{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },  	{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },  	{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },  	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF }, -	{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .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 }, +	/* Asus Xonar DG */ +	{ OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG }, +	/* Asus Xonar DGX */ +	{ OXYGEN_PCI_SUBID(0x1043, 0x8521), .driver_data = MODEL_XONAR_DGX }, +	/* PCI 2.0 HD Audio */ +	{ OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT }, +	/* Kuroutoshikou CMI8787-HG2PCI */ +	{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_HG2PCI }, +	/* TempoTec HiFier Fantasia */ +	{ OXYGEN_PCI_SUBID(0x14c3, 0x1710), .driver_data = MODEL_FANTASIA }, +	/* TempoTec HiFier Serenade */ +	{ OXYGEN_PCI_SUBID(0x14c3, 0x1711), .driver_data = MODEL_SERENADE }, +	/* AuzenTech X-Meridian */  	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN }, +	/* AuzenTech X-Meridian 2G */ +	{ OXYGEN_PCI_SUBID(0x5431, 0x017a), .driver_data = MODEL_MERIDIAN_2G }, +	/* HT-Omega Claro */  	{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO }, +	/* HT-Omega Claro halo */  	{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },  	{ }  }; @@ -95,9 +138,15 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);  #define GPIO_AK5385_DFS_DOUBLE	0x0001  #define GPIO_AK5385_DFS_QUAD	0x0002 +#define GPIO_MERIDIAN_DIG_MASK	0x0050 +#define GPIO_MERIDIAN_DIG_EXT	0x0010 +#define GPIO_MERIDIAN_DIG_BOARD	0x0040 + +#define GPIO_CLARO_DIG_COAX	0x0040  #define GPIO_CLARO_HP		0x0100  struct generic_data { +	unsigned int dacs;  	u8 ak4396_regs[4][5];  	u16 wm8785_regs[3];  }; @@ -148,7 +197,7 @@ static void ak4396_registers_init(struct oxygen *chip)  	struct generic_data *data = chip->model_data;  	unsigned int i; -	for (i = 0; i < 4; ++i) { +	for (i = 0; i < data->dacs; ++i) {  		ak4396_write(chip, i, AK4396_CONTROL_1,  			     AK4396_DIF_24_MSB | AK4396_RSTN);  		ak4396_write(chip, i, AK4396_CONTROL_2, @@ -166,6 +215,7 @@ static void ak4396_init(struct oxygen *chip)  {  	struct generic_data *data = chip->model_data; +	data->dacs = chip->model.dac_channels_pcm / 2;  	data->ak4396_regs[0][AK4396_CONTROL_2] =  		AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;  	ak4396_registers_init(chip); @@ -207,6 +257,10 @@ static void generic_init(struct oxygen *chip)  static void meridian_init(struct oxygen *chip)  { +	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, +			  GPIO_MERIDIAN_DIG_MASK); +	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, +			      GPIO_MERIDIAN_DIG_BOARD, GPIO_MERIDIAN_DIG_MASK);  	ak4396_init(chip);  	ak5385_init(chip);  } @@ -220,6 +274,8 @@ static void claro_enable_hp(struct oxygen *chip)  static void claro_init(struct oxygen *chip)  { +	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX); +	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);  	ak4396_init(chip);  	wm8785_init(chip);  	claro_enable_hp(chip); @@ -227,11 +283,24 @@ static void claro_init(struct oxygen *chip)  static void claro_halo_init(struct oxygen *chip)  { +	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX); +	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);  	ak4396_init(chip);  	ak5385_init(chip);  	claro_enable_hp(chip);  } +static void fantasia_init(struct oxygen *chip) +{ +	ak4396_init(chip); +	snd_component_add(chip->card, "CS5340"); +} + +static void stereo_output_init(struct oxygen *chip) +{ +	ak4396_init(chip); +} +  static void generic_cleanup(struct oxygen *chip)  {  } @@ -268,6 +337,11 @@ static void claro_resume(struct oxygen *chip)  	claro_enable_hp(chip);  } +static void stereo_resume(struct oxygen *chip) +{ +	ak4396_registers_init(chip); +} +  static void set_ak4396_params(struct oxygen *chip,  			      struct snd_pcm_hw_params *params)  { @@ -286,7 +360,7 @@ static void set_ak4396_params(struct oxygen *chip,  	msleep(1); /* wait for the new MCLK to become stable */  	if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) { -		for (i = 0; i < 4; ++i) { +		for (i = 0; i < data->dacs; ++i) {  			ak4396_write(chip, i, AK4396_CONTROL_1,  				     AK4396_DIF_24_MSB);  			ak4396_write(chip, i, AK4396_CONTROL_2, value); @@ -298,9 +372,10 @@ static void set_ak4396_params(struct oxygen *chip,  static void update_ak4396_volume(struct oxygen *chip)  { +	struct generic_data *data = chip->model_data;  	unsigned int i; -	for (i = 0; i < 4; ++i) { +	for (i = 0; i < data->dacs; ++i) {  		ak4396_write_cached(chip, i, AK4396_LCH_ATT,  				    chip->dac_volume[i * 2]);  		ak4396_write_cached(chip, i, AK4396_RCH_ATT, @@ -317,7 +392,7 @@ static void update_ak4396_mute(struct oxygen *chip)  	value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;  	if (chip->dac_mute)  		value |= AK4396_SMUTE; -	for (i = 0; i < 4; ++i) +	for (i = 0; i < data->dacs; ++i)  		ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);  } @@ -356,6 +431,10 @@ static void set_ak5385_params(struct oxygen *chip,  			      value, GPIO_AK5385_DFS_MASK);  } +static void set_no_params(struct oxygen *chip, struct snd_pcm_hw_params *params) +{ +} +  static int rolloff_info(struct snd_kcontrol *ctl,  			struct snd_ctl_elem_info *info)  { @@ -363,13 +442,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,  		"Sharp Roll-off", "Slow Roll-off"  	}; -	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	info->count = 1; -	info->value.enumerated.items = 2; -	if (info->value.enumerated.item >= 2) -		info->value.enumerated.item = 1; -	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); -	return 0; +	return snd_ctl_enum_info(info, 1, 2, names);  }  static int rolloff_get(struct snd_kcontrol *ctl, @@ -400,7 +473,7 @@ static int rolloff_put(struct snd_kcontrol *ctl,  		reg &= ~AK4396_SLOW;  	changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];  	if (changed) { -		for (i = 0; i < 4; ++i) +		for (i = 0; i < data->dacs; ++i)  			ak4396_write(chip, i, AK4396_CONTROL_2, reg);  	}  	mutex_unlock(&chip->mutex); @@ -421,13 +494,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)  		"None", "High-pass Filter"  	}; -	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	info->count = 1; -	info->value.enumerated.items = 2; -	if (info->value.enumerated.item >= 2) -		info->value.enumerated.item = 1; -	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); -	return 0; +	return snd_ctl_enum_info(info, 1, 2, names);  }  static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) @@ -466,6 +533,100 @@ static const struct snd_kcontrol_new hpf_control = {  	.put = hpf_put,  }; +static int meridian_dig_source_info(struct snd_kcontrol *ctl, +				    struct snd_ctl_elem_info *info) +{ +	static const char *const names[2] = { "On-board", "Extension" }; + +	return snd_ctl_enum_info(info, 1, 2, names); +} + +static int claro_dig_source_info(struct snd_kcontrol *ctl, +				 struct snd_ctl_elem_info *info) +{ +	static const char *const names[2] = { "Optical", "Coaxial" }; + +	return snd_ctl_enum_info(info, 1, 2, names); +} + +static int meridian_dig_source_get(struct snd_kcontrol *ctl, +				   struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; + +	value->value.enumerated.item[0] = +		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & +		   GPIO_MERIDIAN_DIG_EXT); +	return 0; +} + +static int claro_dig_source_get(struct snd_kcontrol *ctl, +				struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; + +	value->value.enumerated.item[0] = +		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & +		   GPIO_CLARO_DIG_COAX); +	return 0; +} + +static int meridian_dig_source_put(struct snd_kcontrol *ctl, +				   struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	u16 old_reg, new_reg; +	int changed; + +	mutex_lock(&chip->mutex); +	old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA); +	new_reg = old_reg & ~GPIO_MERIDIAN_DIG_MASK; +	if (value->value.enumerated.item[0] == 0) +		new_reg |= GPIO_MERIDIAN_DIG_BOARD; +	else +		new_reg |= GPIO_MERIDIAN_DIG_EXT; +	changed = new_reg != old_reg; +	if (changed) +		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg); +	mutex_unlock(&chip->mutex); +	return changed; +} + +static int claro_dig_source_put(struct snd_kcontrol *ctl, +				struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	u16 old_reg, new_reg; +	int changed; + +	mutex_lock(&chip->mutex); +	old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA); +	new_reg = old_reg & ~GPIO_CLARO_DIG_COAX; +	if (value->value.enumerated.item[0]) +		new_reg |= GPIO_CLARO_DIG_COAX; +	changed = new_reg != old_reg; +	if (changed) +		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg); +	mutex_unlock(&chip->mutex); +	return changed; +} + +static const struct snd_kcontrol_new meridian_dig_source_control = { +	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	.name = "IEC958 Source Capture Enum", +	.info = meridian_dig_source_info, +	.get = meridian_dig_source_get, +	.put = meridian_dig_source_put, +}; + +static const struct snd_kcontrol_new claro_dig_source_control = { +	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	.name = "IEC958 Source Capture Enum", +	.info = claro_dig_source_info, +	.get = claro_dig_source_get, +	.put = claro_dig_source_put, +}; +  static int generic_mixer_init(struct oxygen *chip)  {  	return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); @@ -484,6 +645,81 @@ static int generic_wm8785_mixer_init(struct oxygen *chip)  	return 0;  } +static int meridian_mixer_init(struct oxygen *chip) +{ +	int err; + +	err = generic_mixer_init(chip); +	if (err < 0) +		return err; +	err = snd_ctl_add(chip->card, +			  snd_ctl_new1(&meridian_dig_source_control, chip)); +	if (err < 0) +		return err; +	return 0; +} + +static int claro_mixer_init(struct oxygen *chip) +{ +	int err; + +	err = generic_wm8785_mixer_init(chip); +	if (err < 0) +		return err; +	err = snd_ctl_add(chip->card, +			  snd_ctl_new1(&claro_dig_source_control, chip)); +	if (err < 0) +		return err; +	return 0; +} + +static int claro_halo_mixer_init(struct oxygen *chip) +{ +	int err; + +	err = generic_mixer_init(chip); +	if (err < 0) +		return err; +	err = snd_ctl_add(chip->card, +			  snd_ctl_new1(&claro_dig_source_control, chip)); +	if (err < 0) +		return err; +	return 0; +} + +static void dump_ak4396_registers(struct oxygen *chip, +				  struct snd_info_buffer *buffer) +{ +	struct generic_data *data = chip->model_data; +	unsigned int dac, i; + +	for (dac = 0; dac < data->dacs; ++dac) { +		snd_iprintf(buffer, "\nAK4396 %u:", dac + 1); +		for (i = 0; i < 5; ++i) +			snd_iprintf(buffer, " %02x", data->ak4396_regs[dac][i]); +	} +	snd_iprintf(buffer, "\n"); +} + +static void dump_wm8785_registers(struct oxygen *chip, +				  struct snd_info_buffer *buffer) +{ +	struct generic_data *data = chip->model_data; +	unsigned int i; + +	snd_iprintf(buffer, "\nWM8785:"); +	for (i = 0; i < 3; ++i) +		snd_iprintf(buffer, " %03x", data->wm8785_regs[i]); +	snd_iprintf(buffer, "\n"); +} + +static void dump_oxygen_registers(struct oxygen *chip, +				  struct snd_info_buffer *buffer) +{ +	dump_ak4396_registers(chip, buffer); +	dump_wm8785_registers(chip, buffer); +} +  static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);  static const struct oxygen_model model_generic = { @@ -494,11 +730,11 @@ static const struct oxygen_model model_generic = {  	.mixer_init = generic_wm8785_mixer_init,  	.cleanup = generic_cleanup,  	.resume = generic_resume, -	.get_i2s_mclk = oxygen_default_i2s_mclk,  	.set_dac_params = set_ak4396_params,  	.set_adc_params = set_wm8785_params,  	.update_dac_volume = update_ak4396_volume,  	.update_dac_mute = update_ak4396_mute, +	.dump_registers = dump_oxygen_registers,  	.dac_tlv = ak4396_db_scale,  	.model_data_size = sizeof(struct generic_data),  	.device_config = PLAYBACK_0_TO_I2S | @@ -508,59 +744,112 @@ static const struct oxygen_model model_generic = {  			 CAPTURE_1_FROM_SPDIF |  			 CAPTURE_2_FROM_AC97_1 |  			 AC97_CD_INPUT, -	.dac_channels = 8, +	.dac_channels_pcm = 8, +	.dac_channels_mixer = 8,  	.dac_volume_min = 0,  	.dac_volume_max = 255,  	.function_flags = OXYGEN_FUNCTION_SPI |  			  OXYGEN_FUNCTION_ENABLE_SPI_4_5, +	.dac_mclks = OXYGEN_MCLKS(256, 128, 128), +	.adc_mclks = OXYGEN_MCLKS(256, 256, 128),  	.dac_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) -{ +static int get_oxygen_model(struct oxygen *chip, +			    const struct pci_device_id *id) +{ +	static const char *const names[] = { +		[MODEL_MERIDIAN]	= "AuzenTech X-Meridian", +		[MODEL_MERIDIAN_2G]	= "AuzenTech X-Meridian 2G", +		[MODEL_CLARO]		= "HT-Omega Claro", +		[MODEL_CLARO_HALO]	= "HT-Omega Claro halo", +		[MODEL_FANTASIA]	= "TempoTec HiFier Fantasia", +		[MODEL_SERENADE]	= "TempoTec HiFier Serenade", +		[MODEL_HG2PCI]		= "CMI8787-HG2PCI", +	}; +  	chip->model = model_generic;  	switch (id->driver_data) {  	case MODEL_MERIDIAN: +	case MODEL_MERIDIAN_2G:  		chip->model.init = meridian_init; -		chip->model.mixer_init = generic_mixer_init; +		chip->model.mixer_init = meridian_mixer_init;  		chip->model.resume = meridian_resume;  		chip->model.set_adc_params = set_ak5385_params; +		chip->model.dump_registers = dump_ak4396_registers;  		chip->model.device_config = PLAYBACK_0_TO_I2S |  					    PLAYBACK_1_TO_SPDIF |  					    CAPTURE_0_FROM_I2S_2 |  					    CAPTURE_1_FROM_SPDIF; +		if (id->driver_data == MODEL_MERIDIAN) +			chip->model.device_config |= AC97_CD_INPUT;  		break;  	case MODEL_CLARO:  		chip->model.init = claro_init; +		chip->model.mixer_init = claro_mixer_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.mixer_init = generic_mixer_init; +		chip->model.mixer_init = claro_halo_mixer_init;  		chip->model.cleanup = claro_cleanup;  		chip->model.suspend = claro_suspend;  		chip->model.resume = claro_resume;  		chip->model.set_adc_params = set_ak5385_params; +		chip->model.dump_registers = dump_ak4396_registers;  		chip->model.device_config = PLAYBACK_0_TO_I2S |  					    PLAYBACK_1_TO_SPDIF |  					    CAPTURE_0_FROM_I2S_2 |  					    CAPTURE_1_FROM_SPDIF;  		break; +	case MODEL_FANTASIA: +	case MODEL_SERENADE: +	case MODEL_2CH_OUTPUT: +	case MODEL_HG2PCI: +		chip->model.shortname = "C-Media CMI8787"; +		chip->model.chip = "CMI8787"; +		if (id->driver_data == MODEL_FANTASIA) +			chip->model.init = fantasia_init; +		else +			chip->model.init = stereo_output_init; +		chip->model.resume = stereo_resume; +		chip->model.mixer_init = generic_mixer_init; +		chip->model.set_adc_params = set_no_params; +		chip->model.dump_registers = dump_ak4396_registers; +		chip->model.device_config = PLAYBACK_0_TO_I2S | +					    PLAYBACK_1_TO_SPDIF; +		if (id->driver_data == MODEL_FANTASIA) { +			chip->model.device_config |= CAPTURE_0_FROM_I2S_1; +			chip->model.adc_mclks = OXYGEN_MCLKS(256, 128, 128); +		} +		chip->model.dac_channels_pcm = 2; +		chip->model.dac_channels_mixer = 2; +		break; +	case MODEL_XONAR_DG: +		chip->model = model_xonar_dg; +		chip->model.shortname = "Xonar DG"; +		break; +	case MODEL_XONAR_DGX: +		chip->model = model_xonar_dg; +		chip->model.shortname = "Xonar DGX"; +		break;  	}  	if (id->driver_data == MODEL_MERIDIAN || +	    id->driver_data == MODEL_MERIDIAN_2G ||  	    id->driver_data == MODEL_CLARO_HALO) {  		chip->model.misc_flags = OXYGEN_MISC_MIDI;  		chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;  	} +	if (id->driver_data < ARRAY_SIZE(names) && names[id->driver_data]) +		chip->model.shortname = names[id->driver_data];  	return 0;  } -static int __devinit generic_oxygen_probe(struct pci_dev *pci, -					  const struct pci_device_id *pci_id) +static int generic_oxygen_probe(struct pci_dev *pci, +				const struct pci_device_id *pci_id)  {  	static int dev;  	int err; @@ -579,25 +868,15 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci,  }  static struct pci_driver oxygen_driver = { -	.name = "CMI8788", +	.name = KBUILD_MODNAME,  	.id_table = oxygen_ids,  	.probe = generic_oxygen_probe, -	.remove = __devexit_p(oxygen_pci_remove), -#ifdef CONFIG_PM -	.suspend = oxygen_pci_suspend, -	.resume = oxygen_pci_resume, +	.remove = oxygen_pci_remove, +#ifdef CONFIG_PM_SLEEP +	.driver = { +		.pm = &oxygen_pci_pm, +	},  #endif  }; -static int __init alsa_card_oxygen_init(void) -{ -	return pci_register_driver(&oxygen_driver); -} - -static void __exit alsa_card_oxygen_exit(void) -{ -	pci_unregister_driver(&oxygen_driver); -} - -module_init(alsa_card_oxygen_init) -module_exit(alsa_card_oxygen_exit) +module_pci_driver(oxygen_driver); diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index 7d5222caa0a..c10ab077afd 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -16,6 +16,10 @@  #define PCM_AC97	5  #define PCM_COUNT	6 +#define OXYGEN_MCLKS(f_single, f_double, f_quad) ((MCLK_##f_single << 0) | \ +						  (MCLK_##f_double << 2) | \ +						  (MCLK_##f_quad   << 4)) +  #define OXYGEN_IO_SIZE	0x100  #define OXYGEN_EEPROM_ID	0x434d	/* "CM" */ @@ -35,6 +39,7 @@  #define MIDI_OUTPUT		0x0800  #define MIDI_INPUT		0x1000  #define AC97_CD_INPUT		0x2000 +#define AC97_FMIC_SWITCH	0x4000  enum {  	CONTROL_SPDIF_PCM, @@ -65,6 +70,7 @@ struct snd_pcm_hardware;  struct snd_pcm_hw_params;  struct snd_kcontrol_new;  struct snd_rawmidi; +struct snd_info_buffer;  struct oxygen;  struct oxygen_model { @@ -79,8 +85,6 @@ struct oxygen_model {  	void (*resume)(struct oxygen *chip);  	void (*pcm_hardware_filter)(unsigned int channel,  				    struct snd_pcm_hardware *hardware); -	unsigned int (*get_i2s_mclk)(struct oxygen *chip, unsigned int channel, -				     struct snd_pcm_hw_params *hw_params);  	void (*set_dac_params)(struct oxygen *chip,  			       struct snd_pcm_hw_params *params);  	void (*set_adc_params)(struct oxygen *chip, @@ -88,19 +92,25 @@ struct oxygen_model {  	void (*update_dac_volume)(struct oxygen *chip);  	void (*update_dac_mute)(struct oxygen *chip);  	void (*update_center_lfe_mix)(struct oxygen *chip, bool mixed); +	unsigned int (*adjust_dac_routing)(struct oxygen *chip, +					   unsigned int play_routing);  	void (*gpio_changed)(struct oxygen *chip);  	void (*uart_input)(struct oxygen *chip);  	void (*ac97_switch)(struct oxygen *chip,  			    unsigned int reg, unsigned int mute); +	void (*dump_registers)(struct oxygen *chip, +			       struct snd_info_buffer *buffer);  	const unsigned int *dac_tlv; -	unsigned long private_data;  	size_t model_data_size;  	unsigned int device_config; -	u8 dac_channels; +	u8 dac_channels_pcm; +	u8 dac_channels_mixer;  	u8 dac_volume_min;  	u8 dac_volume_max;  	u8 misc_flags;  	u8 function_flags; +	u8 dac_mclks; +	u8 adc_mclks;  	u16 dac_i2s_format;  	u16 adc_i2s_format;  }; @@ -121,7 +131,6 @@ struct oxygen {  	u8 pcm_running;  	u8 dac_routing;  	u8 spdif_playback_enable; -	u8 revision;  	u8 has_ac97_0;  	u8 has_ac97_1;  	u32 spdif_bits; @@ -152,9 +161,8 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  				     )  		    );  void oxygen_pci_remove(struct pci_dev *pci); -#ifdef CONFIG_PM -int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state); -int oxygen_pci_resume(struct pci_dev *pci); +#ifdef CONFIG_PM_SLEEP +extern const struct dev_pm_ops oxygen_pci_pm;  #endif  void oxygen_pci_shutdown(struct pci_dev *pci); @@ -167,8 +175,6 @@ void oxygen_update_spdif_source(struct oxygen *chip);  /* oxygen_pcm.c */  int oxygen_pcm_init(struct oxygen *chip); -unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, unsigned int channel, -				     struct snd_pcm_hw_params *hw_params);  /* oxygen_io.c */ @@ -192,7 +198,7 @@ void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,  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); +int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);  void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);  void oxygen_reset_uart(struct oxygen *chip); diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index 09b2b2a36df..4b8a32c37e3 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -19,6 +19,7 @@  #include <linux/delay.h>  #include <linux/sched.h> +#include <linux/export.h>  #include <sound/core.h>  #include <sound/mpu401.h>  #include <asm/io.h> @@ -146,7 +147,7 @@ void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,  			return;  		}  	} -	snd_printk(KERN_ERR "AC'97 write timeout\n"); +	dev_err(chip->card->dev, "AC'97 write timeout\n");  }  EXPORT_SYMBOL(oxygen_write_ac97); @@ -178,7 +179,7 @@ u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,  			reg ^= 0xffff;  		}  	} -	snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec); +	dev_err(chip->card->dev, "AC'97 read timeout on codec %u\n", codec);  	return 0;  }  EXPORT_SYMBOL(oxygen_read_ac97); @@ -193,23 +194,36 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,  }  EXPORT_SYMBOL(oxygen_write_ac97_masked); -void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) +static int oxygen_wait_spi(struct oxygen *chip)  {  	unsigned int count; -	/* should not need more than 7.68 us (24 * 320 ns) */ -	count = 10; -	while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) -	       && count > 0) { -		udelay(1); -		--count; +	/* +	 * Higher timeout to be sure: 200 us; +	 * actual transaction should not need more than 40 us. +	 */ +	for (count = 50; count > 0; count--) { +		udelay(4); +		if ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & +						OXYGEN_SPI_BUSY) == 0) +			return 0;  	} +	dev_err(chip->card->dev, "oxygen: SPI wait timeout\n"); +	return -EIO; +} +int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) +{ +	/* +	 * We need to wait AFTER initiating the SPI transaction, +	 * otherwise read operations will not work. +	 */  	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); +	return oxygen_wait_spi(chip);  }  EXPORT_SYMBOL(oxygen_write_spi); @@ -274,5 +288,5 @@ void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value)  		      & OXYGEN_EEPROM_BUSY))  			return;  	} -	snd_printk(KERN_ERR "EEPROM write timeout\n"); +	dev_err(chip->card->dev, "EEPROM write timeout\n");  } diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index e5ebe56fb0c..b67e3060247 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -22,6 +22,7 @@  #include <linux/mutex.h>  #include <linux/pci.h>  #include <linux/slab.h> +#include <linux/module.h>  #include <sound/ac97_codec.h>  #include <sound/asoundef.h>  #include <sound/core.h> @@ -202,7 +203,13 @@ static void oxygen_proc_read(struct snd_info_entry *entry,  	struct oxygen *chip = entry->private_data;  	int i, j; -	snd_iprintf(buffer, "CMI8788\n\n"); +	switch (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_PACKAGE_ID_MASK) { +	case OXYGEN_PACKAGE_ID_8786: i = '6'; break; +	case OXYGEN_PACKAGE_ID_8787: i = '7'; break; +	case OXYGEN_PACKAGE_ID_8788: i = '8'; break; +	default:                     i = '?'; break; +	} +	snd_iprintf(buffer, "CMI878%c:\n", i);  	for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {  		snd_iprintf(buffer, "%02x:", i);  		for (j = 0; j < 0x10; ++j) @@ -212,7 +219,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,  	if (mutex_lock_interruptible(&chip->mutex) < 0)  		return;  	if (chip->has_ac97_0) { -		snd_iprintf(buffer, "\nAC97\n"); +		snd_iprintf(buffer, "\nAC97:\n");  		for (i = 0; i < 0x80; i += 0x10) {  			snd_iprintf(buffer, "%02x:", i);  			for (j = 0; j < 0x10; j += 2) @@ -222,7 +229,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,  		}  	}  	if (chip->has_ac97_1) { -		snd_iprintf(buffer, "\nAC97 2\n"); +		snd_iprintf(buffer, "\nAC97 2:\n");  		for (i = 0; i < 0x80; i += 0x10) {  			snd_iprintf(buffer, "%02x:", i);  			for (j = 0; j < 0x10; j += 2) @@ -232,13 +239,15 @@ static void oxygen_proc_read(struct snd_info_entry *entry,  		}  	}  	mutex_unlock(&chip->mutex); +	if (chip->model.dump_registers) +		chip->model.dump_registers(chip, buffer);  }  static void oxygen_proc_init(struct oxygen *chip)  {  	struct snd_info_entry *entry; -	if (!snd_card_proc_new(chip->card, "cmi8788", &entry)) +	if (!snd_card_proc_new(chip->card, "oxygen", &entry))  		snd_info_set_text_ops(entry, chip, oxygen_proc_read);  }  #else @@ -262,7 +271,7 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])  	 */  	subdevice = oxygen_read_eeprom(chip, 2);  	/* use default ID if EEPROM is missing */ -	if (subdevice == 0xffff) +	if (subdevice == 0xffff && oxygen_read_eeprom(chip, 1) == 0xffff)  		subdevice = 0x8788;  	/*  	 * We use only the subsystem device ID for searching because it is @@ -304,7 +313,7 @@ static void oxygen_restore_eeprom(struct oxygen *chip,  		oxygen_clear_bits8(chip, OXYGEN_MISC,  				   OXYGEN_MISC_WRITE_PCI_SUBID); -		snd_printk(KERN_INFO "EEPROM ID restored\n"); +		dev_info(chip->card->dev, "EEPROM ID restored\n");  	}  } @@ -364,12 +373,7 @@ static void oxygen_init(struct oxygen *chip)  		(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);  	chip->spdif_pcm_bits = chip->spdif_bits; -	if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2) -		chip->revision = 2; -	else -		chip->revision = 1; - -	if (chip->revision == 1) +	if (!(oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2))  		oxygen_set_bits8(chip, OXYGEN_MISC,  				 OXYGEN_MISC_PCI_MEM_W_1_CLOCK); @@ -406,28 +410,40 @@ 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 | chip->model.dac_i2s_format | -		       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | -		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); +		       OXYGEN_RATE_48000 | +		       chip->model.dac_i2s_format | +		       OXYGEN_I2S_MCLK(chip->model.dac_mclks) | +		       OXYGEN_I2S_BITS_16 | +		       OXYGEN_I2S_MASTER | +		       OXYGEN_I2S_BCLK_64);  	if (chip->model.device_config & 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); +			       OXYGEN_RATE_48000 | +			       chip->model.adc_i2s_format | +			       OXYGEN_I2S_MCLK(chip->model.adc_mclks) | +			       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); +			       OXYGEN_I2S_MASTER | +			       OXYGEN_I2S_MUTE_MCLK);  	if (chip->model.device_config & (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); +			       OXYGEN_RATE_48000 | +			       chip->model.adc_i2s_format | +			       OXYGEN_I2S_MCLK(chip->model.adc_mclks) | +			       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_I2S_MASTER | +			       OXYGEN_I2S_MUTE_MCLK);  	oxygen_write16(chip, OXYGEN_I2S_C_FORMAT, -		       OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); +		       OXYGEN_I2S_MASTER | +		       OXYGEN_I2S_MUTE_MCLK);  	oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,  			    OXYGEN_SPDIF_OUT_ENABLE |  			    OXYGEN_SPDIF_LOOPBACK); @@ -557,7 +573,8 @@ static void oxygen_card_free(struct snd_card *card)  	oxygen_shutdown(chip);  	if (chip->irq >= 0)  		free_irq(chip->irq, chip); -	flush_scheduled_work(); +	flush_work(&chip->spdif_input_bits_work); +	flush_work(&chip->gpio_work);  	chip->model.cleanup(chip);  	kfree(chip->model_data);  	mutex_destroy(&chip->mutex); @@ -578,7 +595,8 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  	const struct pci_device_id *pci_id;  	int err; -	err = snd_card_create(index, id, owner, sizeof(*chip), &card); +	err = snd_card_new(&pci->dev, index, id, owner, +			   sizeof(*chip), &card);  	if (err < 0)  		return err; @@ -599,13 +617,13 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  	err = pci_request_regions(pci, DRIVER);  	if (err < 0) { -		snd_printk(KERN_ERR "cannot reserve PCI resources\n"); +		dev_err(card->dev, "cannot reserve PCI resources\n");  		goto err_pci_enable;  	}  	if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||  	    pci_resource_len(pci, 0) < OXYGEN_IO_SIZE) { -		snd_printk(KERN_ERR "invalid PCI I/O range\n"); +		dev_err(card->dev, "invalid PCI I/O range\n");  		err = -ENXIO;  		goto err_pci_regions;  	} @@ -631,7 +649,6 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  	}  	pci_set_master(pci); -	snd_card_set_dev(card, &pci->dev);  	card->private_free = oxygen_card_free;  	configure_pcie_bridge(pci); @@ -639,17 +656,17 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  	chip->model.init(chip);  	err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED, -			  DRIVER, chip); +			  KBUILD_MODNAME, chip);  	if (err < 0) { -		snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq); +		dev_err(card->dev, "cannot grab interrupt %d\n", pci->irq);  		goto err_card;  	}  	chip->irq = pci->irq;  	strcpy(card->driver, chip->model.chip);  	strcpy(card->shortname, chip->model.shortname); -	sprintf(card->longname, "%s (rev %u) at %#lx, irq %i", -		chip->model.longname, chip->revision, chip->addr, chip->irq); +	sprintf(card->longname, "%s at %#lx, irq %i", +		chip->model.longname, chip->addr, chip->irq);  	strcpy(card->mixername, chip->model.chip);  	snd_component_add(card, chip->model.chip); @@ -662,15 +679,15 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  		goto err_card;  	if (chip->model.device_config & (MIDI_OUTPUT | MIDI_INPUT)) { -		unsigned int info_flags = MPU401_INFO_INTEGRATED; +		unsigned int info_flags = +				MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK;  		if (chip->model.device_config & MIDI_OUTPUT)  			info_flags |= MPU401_INFO_OUTPUT;  		if (chip->model.device_config & MIDI_INPUT)  			info_flags |= MPU401_INFO_INPUT;  		err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,  					  chip->addr + OXYGEN_MPU401, -					  info_flags, 0, 0, -					  &chip->midi); +					  info_flags, -1, &chip->midi);  		if (err < 0)  			goto err_card;  	} @@ -705,14 +722,14 @@ EXPORT_SYMBOL(oxygen_pci_probe);  void oxygen_pci_remove(struct pci_dev *pci)  {  	snd_card_free(pci_get_drvdata(pci)); -	pci_set_drvdata(pci, NULL);  }  EXPORT_SYMBOL(oxygen_pci_remove); -#ifdef CONFIG_PM -int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int oxygen_pci_suspend(struct device *dev)  { -	struct snd_card *card = pci_get_drvdata(pci); +	struct pci_dev *pci = to_pci_dev(dev); +	struct snd_card *card = dev_get_drvdata(dev);  	struct oxygen *chip = card->private_data;  	unsigned int i, saved_interrupt_mask; @@ -733,15 +750,15 @@ int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)  	spin_unlock_irq(&chip->reg_lock);  	synchronize_irq(chip->irq); -	flush_scheduled_work(); +	flush_work(&chip->spdif_input_bits_work); +	flush_work(&chip->gpio_work);  	chip->interrupt_mask = saved_interrupt_mask;  	pci_disable_device(pci);  	pci_save_state(pci); -	pci_set_power_state(pci, pci_choose_state(pci, state)); +	pci_set_power_state(pci, PCI_D3hot);  	return 0;  } -EXPORT_SYMBOL(oxygen_pci_suspend);  static const u32 registers_to_restore[OXYGEN_IO_SIZE / 32] = {  	0xffffffff, 0x00ff077f, 0x00011d08, 0x007f00ff, @@ -769,16 +786,17 @@ static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec)  					  chip->saved_ac97_registers[codec][i]);  } -int oxygen_pci_resume(struct pci_dev *pci) +static int oxygen_pci_resume(struct device *dev)  { -	struct snd_card *card = pci_get_drvdata(pci); +	struct pci_dev *pci = to_pci_dev(dev); +	struct snd_card *card = dev_get_drvdata(dev);  	struct oxygen *chip = card->private_data;  	unsigned int i;  	pci_set_power_state(pci, PCI_D0);  	pci_restore_state(pci);  	if (pci_enable_device(pci) < 0) { -		snd_printk(KERN_ERR "cannot reenable device"); +		dev_err(dev, "cannot reenable device");  		snd_card_disconnect(card);  		return -EIO;  	} @@ -802,8 +820,10 @@ int oxygen_pci_resume(struct pci_dev *pci)  	snd_power_change_state(card, SNDRV_CTL_POWER_D0);  	return 0;  } -EXPORT_SYMBOL(oxygen_pci_resume); -#endif /* CONFIG_PM */ + +SIMPLE_DEV_PM_OPS(oxygen_pci_pm, oxygen_pci_suspend, oxygen_pci_resume); +EXPORT_SYMBOL(oxygen_pci_pm); +#endif /* CONFIG_PM_SLEEP */  void oxygen_pci_shutdown(struct pci_dev *pci)  { diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 2849b36f5f7..5988e044c51 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -31,7 +31,7 @@ static int dac_volume_info(struct snd_kcontrol *ctl,  	struct oxygen *chip = ctl->private_data;  	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -	info->count = chip->model.dac_channels; +	info->count = chip->model.dac_channels_mixer;  	info->value.integer.min = chip->model.dac_volume_min;  	info->value.integer.max = chip->model.dac_volume_max;  	return 0; @@ -44,7 +44,7 @@ static int dac_volume_get(struct snd_kcontrol *ctl,  	unsigned int i;  	mutex_lock(&chip->mutex); -	for (i = 0; i < chip->model.dac_channels; ++i) +	for (i = 0; i < chip->model.dac_channels_mixer; ++i)  		value->value.integer.value[i] = chip->dac_volume[i];  	mutex_unlock(&chip->mutex);  	return 0; @@ -59,7 +59,7 @@ static int dac_volume_put(struct snd_kcontrol *ctl,  	changed = 0;  	mutex_lock(&chip->mutex); -	for (i = 0; i < chip->model.dac_channels; ++i) +	for (i = 0; i < chip->model.dac_channels_mixer; ++i)  		if (value->value.integer.value[i] != chip->dac_volume[i]) {  			chip->dac_volume[i] = value->value.integer.value[i];  			changed = 1; @@ -97,6 +97,16 @@ static int dac_mute_put(struct snd_kcontrol *ctl,  	return changed;  } +static unsigned int upmix_item_count(struct oxygen *chip) +{ +	if (chip->model.dac_channels_pcm < 8) +		return 2; +	else if (chip->model.update_center_lfe_mix) +		return 5; +	else +		return 3; +} +  static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)  {  	static const char *const names[5] = { @@ -107,15 +117,9 @@ static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)  		"Front+Surround+Center/LFE+Back",  	};  	struct oxygen *chip = ctl->private_data; -	unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3; +	unsigned int count = upmix_item_count(chip); -	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	info->count = 1; -	info->value.enumerated.items = count; -	if (info->value.enumerated.item >= count) -		info->value.enumerated.item = count - 1; -	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); -	return 0; +	return snd_ctl_enum_info(info, 1, count, names);  }  static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) @@ -176,6 +180,8 @@ void oxygen_update_dac_routing(struct oxygen *chip)  			    (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |  			    (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |  			    (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT); +	if (chip->model.adjust_dac_routing) +		reg_value = chip->model.adjust_dac_routing(chip, reg_value);  	oxygen_write16_masked(chip, OXYGEN_PLAY_ROUTING, reg_value,  			      OXYGEN_PLAY_DAC0_SOURCE_MASK |  			      OXYGEN_PLAY_DAC1_SOURCE_MASK | @@ -184,11 +190,12 @@ void oxygen_update_dac_routing(struct oxygen *chip)  	if (chip->model.update_center_lfe_mix)  		chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2);  } +EXPORT_SYMBOL(oxygen_update_dac_routing);  static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)  {  	struct oxygen *chip = ctl->private_data; -	unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3; +	unsigned int count = upmix_item_count(chip);  	int changed;  	if (value->value.enumerated.item[0] >= count) @@ -430,30 +437,31 @@ static int spdif_input_default_get(struct snd_kcontrol *ctl,  	return 0;  } -static int spdif_loopback_get(struct snd_kcontrol *ctl, -			      struct snd_ctl_elem_value *value) +static int spdif_bit_switch_get(struct snd_kcontrol *ctl, +				struct snd_ctl_elem_value *value)  {  	struct oxygen *chip = ctl->private_data; +	u32 bit = ctl->private_value;  	value->value.integer.value[0] = -		!!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL) -		   & OXYGEN_SPDIF_LOOPBACK); +		!!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL) & bit);  	return 0;  } -static int spdif_loopback_put(struct snd_kcontrol *ctl, -			      struct snd_ctl_elem_value *value) +static int spdif_bit_switch_put(struct snd_kcontrol *ctl, +				struct snd_ctl_elem_value *value)  {  	struct oxygen *chip = ctl->private_data; +	u32 bit = ctl->private_value;  	u32 oldreg, newreg;  	int changed;  	spin_lock_irq(&chip->reg_lock);  	oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);  	if (value->value.integer.value[0]) -		newreg = oldreg | OXYGEN_SPDIF_LOOPBACK; +		newreg = oldreg | bit;  	else -		newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK; +		newreg = oldreg & ~bit;  	changed = newreg != oldreg;  	if (changed)  		oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg); @@ -611,9 +619,12 @@ static int ac97_volume_get(struct snd_kcontrol *ctl,  	mutex_lock(&chip->mutex);  	reg = oxygen_read_ac97(chip, codec, index);  	mutex_unlock(&chip->mutex); -	value->value.integer.value[0] = 31 - (reg & 0x1f); -	if (stereo) -		value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f); +	if (!stereo) { +		value->value.integer.value[0] = 31 - (reg & 0x1f); +	} else { +		value->value.integer.value[0] = 31 - ((reg >> 8) & 0x1f); +		value->value.integer.value[1] = 31 - (reg & 0x1f); +	}  	return 0;  } @@ -629,14 +640,14 @@ static int ac97_volume_put(struct snd_kcontrol *ctl,  	mutex_lock(&chip->mutex);  	oldreg = oxygen_read_ac97(chip, codec, index); -	newreg = oldreg; -	newreg = (newreg & ~0x1f) | -		(31 - (value->value.integer.value[0] & 0x1f)); -	if (stereo) -		newreg = (newreg & ~0x1f00) | -			((31 - (value->value.integer.value[1] & 0x1f)) << 8); -	else -		newreg = (newreg & ~0x1f00) | ((newreg & 0x1f) << 8); +	if (!stereo) { +		newreg = oldreg & ~0x1f; +		newreg |= 31 - (value->value.integer.value[0] & 0x1f); +	} else { +		newreg = oldreg & ~0x1f1f; +		newreg |= (31 - (value->value.integer.value[0] & 0x1f)) << 8; +		newreg |= 31 - (value->value.integer.value[1] & 0x1f); +	}  	change = newreg != oldreg;  	if (change)  		oxygen_write_ac97(chip, codec, index, newreg); @@ -644,6 +655,46 @@ static int ac97_volume_put(struct snd_kcontrol *ctl,  	return change;  } +static int mic_fmic_source_info(struct snd_kcontrol *ctl, +			   struct snd_ctl_elem_info *info) +{ +	static const char *const names[] = { "Mic Jack", "Front Panel" }; + +	return snd_ctl_enum_info(info, 1, 2, names); +} + +static int mic_fmic_source_get(struct snd_kcontrol *ctl, +			       struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; + +	mutex_lock(&chip->mutex); +	value->value.enumerated.item[0] = +		!!(oxygen_read_ac97(chip, 0, CM9780_JACK) & CM9780_FMIC2MIC); +	mutex_unlock(&chip->mutex); +	return 0; +} + +static int mic_fmic_source_put(struct snd_kcontrol *ctl, +			       struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	u16 oldreg, newreg; +	int change; + +	mutex_lock(&chip->mutex); +	oldreg = oxygen_read_ac97(chip, 0, CM9780_JACK); +	if (value->value.enumerated.item[0]) +		newreg = oldreg | CM9780_FMIC2MIC; +	else +		newreg = oldreg & ~CM9780_FMIC2MIC; +	change = newreg != oldreg; +	if (change) +		oxygen_write_ac97(chip, 0, CM9780_JACK, newreg); +	mutex_unlock(&chip->mutex); +	return change; +} +  static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl,  				   struct snd_ctl_elem_info *info)  { @@ -791,8 +842,17 @@ static const struct snd_kcontrol_new spdif_input_controls[] = {  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH),  		.info = snd_ctl_boolean_mono_info, -		.get = spdif_loopback_get, -		.put = spdif_loopback_put, +		.get = spdif_bit_switch_get, +		.put = spdif_bit_switch_put, +		.private_value = OXYGEN_SPDIF_LOOPBACK, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = SNDRV_CTL_NAME_IEC958("Validity Check ",CAPTURE,SWITCH), +		.info = snd_ctl_boolean_mono_info, +		.get = spdif_bit_switch_get, +		.put = spdif_bit_switch_put, +		.private_value = OXYGEN_SPDIF_SPDVALID,  	},  }; @@ -908,6 +968,13 @@ static const struct snd_kcontrol_new ac97_controls[] = {  	AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC, 0),  	AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),  	AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Mic Source Capture Enum", +		.info = mic_fmic_source_info, +		.get = mic_fmic_source_get, +		.put = mic_fmic_source_put, +	},  	AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),  	AC97_VOLUME("CD Capture Volume", 0, AC97_CD, 1),  	AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1), @@ -970,7 +1037,10 @@ static int add_controls(struct oxygen *chip,  				continue;  		}  		if (!strcmp(template.name, "Stereo Upmixing") && -		    chip->model.dac_channels == 2) +		    chip->model.dac_channels_pcm == 2) +			continue; +		if (!strcmp(template.name, "Mic Source Capture Enum") && +		    !(chip->model.device_config & AC97_FMIC_SWITCH))  			continue;  		if (!strncmp(template.name, "CD Capture ", 11) &&  		    !(chip->model.device_config & AC97_CD_INPUT)) diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index 814667442eb..cc0bcd9f335 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -39,7 +39,8 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = {  		SNDRV_PCM_INFO_MMAP_VALID |  		SNDRV_PCM_INFO_INTERLEAVED |  		SNDRV_PCM_INFO_PAUSE | -		SNDRV_PCM_INFO_SYNC_START, +		SNDRV_PCM_INFO_SYNC_START | +		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,  	.formats = SNDRV_PCM_FMTBIT_S16_LE |  		   SNDRV_PCM_FMTBIT_S32_LE,  	.rates = SNDRV_PCM_RATE_32000 | @@ -65,7 +66,8 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = {  		SNDRV_PCM_INFO_MMAP_VALID |  		SNDRV_PCM_INFO_INTERLEAVED |  		SNDRV_PCM_INFO_PAUSE | -		SNDRV_PCM_INFO_SYNC_START, +		SNDRV_PCM_INFO_SYNC_START | +		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,  	.formats = SNDRV_PCM_FMTBIT_S16_LE |  		   SNDRV_PCM_FMTBIT_S32_LE,  	.rates = SNDRV_PCM_RATE_32000 | @@ -91,7 +93,8 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = {  		SNDRV_PCM_INFO_MMAP_VALID |  		SNDRV_PCM_INFO_INTERLEAVED |  		SNDRV_PCM_INFO_PAUSE | -		SNDRV_PCM_INFO_SYNC_START, +		SNDRV_PCM_INFO_SYNC_START | +		SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,  	.formats = SNDRV_PCM_FMTBIT_S16_LE,  	.rates = SNDRV_PCM_RATE_48000,  	.rate_min = 48000, @@ -140,7 +143,7 @@ static int oxygen_open(struct snd_pcm_substream *substream,  		runtime->hw.rate_min = 44100;  		break;  	case PCM_MULTICH: -		runtime->hw.channels_max = chip->model.dac_channels; +		runtime->hw.channels_max = chip->model.dac_channels_pcm;  		break;  	}  	if (chip->model.pcm_hardware_filter) @@ -165,12 +168,6 @@ static int oxygen_open(struct snd_pcm_substream *substream,  		if (err < 0)  			return err;  	} -	if (channel == PCM_MULTICH) { -		err = snd_pcm_hw_constraint_minmax -			(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0, 8192000); -		if (err < 0) -			return err; -	}  	snd_pcm_set_sync(substream);  	chip->streams[channel] = substream; @@ -271,17 +268,6 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)  	}  } -unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, -				     unsigned int channel, -				     struct snd_pcm_hw_params *hw_params) -{ -	if (params_rate(hw_params) <= 96000) -		return OXYGEN_I2S_MCLK_256; -	else -		return OXYGEN_I2S_MCLK_128; -} -EXPORT_SYMBOL(oxygen_default_i2s_mclk); -  static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)  {  	if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE) @@ -341,6 +327,26 @@ static int oxygen_hw_params(struct snd_pcm_substream *substream,  	return 0;  } +static u16 get_mclk(struct oxygen *chip, unsigned int channel, +		    struct snd_pcm_hw_params *params) +{ +	unsigned int mclks, shift; + +	if (channel == PCM_MULTICH) +		mclks = chip->model.dac_mclks; +	else +		mclks = chip->model.adc_mclks; + +	if (params_rate(params) <= 48000) +		shift = 0; +	else if (params_rate(params) <= 96000) +		shift = 2; +	else +		shift = 4; + +	return OXYGEN_I2S_MCLK(mclks >> shift); +} +  static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,  				  struct snd_pcm_hw_params *hw_params)  { @@ -357,8 +363,8 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,  			     OXYGEN_REC_FORMAT_A_MASK);  	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,  			      oxygen_rate(hw_params) | -			      chip->model.get_i2s_mclk(chip, PCM_A, hw_params) |  			      chip->model.adc_i2s_format | +			      get_mclk(chip, PCM_A, hw_params) |  			      oxygen_i2s_bits(hw_params),  			      OXYGEN_I2S_RATE_MASK |  			      OXYGEN_I2S_FORMAT_MASK | @@ -393,9 +399,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,  	if (!is_ac97)  		oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,  				      oxygen_rate(hw_params) | -				      chip->model.get_i2s_mclk(chip, PCM_B, -							       hw_params) |  				      chip->model.adc_i2s_format | +				      get_mclk(chip, PCM_B, hw_params) |  				      oxygen_i2s_bits(hw_params),  				      OXYGEN_I2S_RATE_MASK |  				      OXYGEN_I2S_FORMAT_MASK | @@ -476,8 +481,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,  	oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,  			      oxygen_rate(hw_params) |  			      chip->model.dac_i2s_format | -			      chip->model.get_i2s_mclk(chip, PCM_MULTICH, -						       hw_params) | +			      get_mclk(chip, PCM_MULTICH, hw_params) |  			      oxygen_i2s_bits(hw_params),  			      OXYGEN_I2S_RATE_MASK |  			      OXYGEN_I2S_FORMAT_MASK | @@ -530,7 +534,10 @@ static int oxygen_prepare(struct snd_pcm_substream *substream)  	oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);  	oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); -	chip->interrupt_mask |= channel_mask; +	if (substream->runtime->no_period_wakeup) +		chip->interrupt_mask &= ~channel_mask; +	else +		chip->interrupt_mask |= channel_mask;  	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);  	spin_unlock_irq(&chip->reg_lock);  	return 0; diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h index 4dcd41b7825..8c191badaae 100644 --- a/sound/pci/oxygen/oxygen_regs.h +++ b/sound/pci/oxygen/oxygen_regs.h @@ -139,9 +139,11 @@  #define  OXYGEN_I2S_FORMAT_I2S		0x0000  #define  OXYGEN_I2S_FORMAT_LJUST	0x0008  #define  OXYGEN_I2S_MCLK_MASK		0x0030	/* MCLK/LRCK */ -#define  OXYGEN_I2S_MCLK_128		0x0000 -#define  OXYGEN_I2S_MCLK_256		0x0010 -#define  OXYGEN_I2S_MCLK_512		0x0020 +#define  OXYGEN_I2S_MCLK_SHIFT		4 +#define  MCLK_128			0 +#define  MCLK_256			1 +#define  MCLK_512			2 +#define  OXYGEN_I2S_MCLK(f)		(((f) & 3) << OXYGEN_I2S_MCLK_SHIFT)  #define  OXYGEN_I2S_BITS_MASK		0x00c0  #define  OXYGEN_I2S_BITS_16		0x0000  #define  OXYGEN_I2S_BITS_20		0x0040 @@ -238,11 +240,11 @@  #define  OXYGEN_SPI_DATA_LENGTH_MASK	0x02  #define  OXYGEN_SPI_DATA_LENGTH_2	0x00  #define  OXYGEN_SPI_DATA_LENGTH_3	0x02 -#define  OXYGEN_SPI_CLOCK_MASK		0xc0 +#define  OXYGEN_SPI_CLOCK_MASK		0x0c  #define  OXYGEN_SPI_CLOCK_160		0x00	/* ns */ -#define  OXYGEN_SPI_CLOCK_320		0x40 -#define  OXYGEN_SPI_CLOCK_640		0x80 -#define  OXYGEN_SPI_CLOCK_1280		0xc0 +#define  OXYGEN_SPI_CLOCK_320		0x04 +#define  OXYGEN_SPI_CLOCK_640		0x08 +#define  OXYGEN_SPI_CLOCK_1280		0x0c  #define  OXYGEN_SPI_CODEC_MASK		0x70	/* 0..5 */  #define  OXYGEN_SPI_CODEC_SHIFT		4  #define  OXYGEN_SPI_CEN_MASK		0x80 @@ -316,6 +318,7 @@  #define  OXYGEN_PLAY_MUTE23		0x0002  #define  OXYGEN_PLAY_MUTE45		0x0004  #define  OXYGEN_PLAY_MUTE67		0x0008 +#define  OXYGEN_PLAY_MUTE_MASK		0x000f  #define  OXYGEN_PLAY_MULTICH_MASK	0x0010  #define  OXYGEN_PLAY_MULTICH_I2S_DAC	0x0000  #define  OXYGEN_PLAY_MULTICH_AC97	0x0010 diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 469010a8b84..64b9fda5f04 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -19,6 +19,7 @@  #include <linux/pci.h>  #include <linux/delay.h> +#include <linux/module.h>  #include <sound/core.h>  #include <sound/initval.h>  #include <sound/pcm.h> @@ -31,7 +32,7 @@ MODULE_SUPPORTED_DEVICE("{{Asus,AV66},{Asus,AV100},{Asus,AV200}}");  static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;  static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;  module_param_array(index, int, NULL, 0444);  MODULE_PARM_DESC(index, "card index"); @@ -51,13 +52,14 @@ static DEFINE_PCI_DEVICE_TABLE(xonar_ids) = {  	{ OXYGEN_PCI_SUBID(0x1043, 0x835d) },  	{ OXYGEN_PCI_SUBID(0x1043, 0x835e) },  	{ OXYGEN_PCI_SUBID(0x1043, 0x838e) }, +	{ OXYGEN_PCI_SUBID(0x1043, 0x8522) },  	{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },  	{ }  };  MODULE_DEVICE_TABLE(pci, xonar_ids); -static int __devinit get_xonar_model(struct oxygen *chip, -				     const struct pci_device_id *id) +static int get_xonar_model(struct oxygen *chip, +			   const struct pci_device_id *id)  {  	if (get_xonar_pcm179x_model(chip, id) >= 0)  		return 0; @@ -68,8 +70,8 @@ static int __devinit get_xonar_model(struct oxygen *chip,  	return -EINVAL;  } -static int __devinit xonar_probe(struct pci_dev *pci, -				 const struct pci_device_id *pci_id) +static int xonar_probe(struct pci_dev *pci, +		       const struct pci_device_id *pci_id)  {  	static int dev;  	int err; @@ -88,26 +90,16 @@ static int __devinit xonar_probe(struct pci_dev *pci,  }  static struct pci_driver xonar_driver = { -	.name = "AV200", +	.name = KBUILD_MODNAME,  	.id_table = xonar_ids,  	.probe = xonar_probe, -	.remove = __devexit_p(oxygen_pci_remove), -#ifdef CONFIG_PM -	.suspend = oxygen_pci_suspend, -	.resume = oxygen_pci_resume, +	.remove = oxygen_pci_remove, +#ifdef CONFIG_PM_SLEEP +	.driver = { +		.pm = &oxygen_pci_pm, +	},  #endif  	.shutdown = oxygen_pci_shutdown,  }; -static int __init alsa_card_xonar_init(void) -{ -	return pci_register_driver(&xonar_driver); -} - -static void __exit alsa_card_xonar_exit(void) -{ -	pci_unregister_driver(&xonar_driver); -} - -module_init(alsa_card_xonar_init) -module_exit(alsa_card_xonar_exit) +module_pci_driver(xonar_driver); diff --git a/sound/pci/oxygen/xonar.h b/sound/pci/oxygen/xonar.h index b35343b0a9a..0434c207e81 100644 --- a/sound/pci/oxygen/xonar.h +++ b/sound/pci/oxygen/xonar.h @@ -24,6 +24,8 @@ void xonar_init_ext_power(struct oxygen *chip);  void xonar_init_cs53x1(struct oxygen *chip);  void xonar_set_cs53x1_params(struct oxygen *chip,  			     struct snd_pcm_hw_params *params); + +#define XONAR_GPIO_BIT_INVERT	(1 << 16)  int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,  			      struct snd_ctl_elem_value *value);  int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl, diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index aa27c31049a..d231b93d6ab 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -22,29 +22,28 @@   *   * CMI8788:   * - * I²C <-> CS4398 (front) - *     <-> CS4362A (surround, center/LFE, back) + *   I²C <-> CS4398 (addr 1001111) (front) + *       <-> CS4362A (addr 0011000) (surround, center/LFE, back)   * - * GPI 0 <- external power present (DX only) + *   GPI 0 <- external power present (DX only)   * - * 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 output to speakers + *   GPIO 1 -> route output to front panel + *   GPIO 2 -> M0 of CS5361 + *   GPIO 3 -> M1 of CS5361 + *   GPIO 6 -> ? + *   GPIO 7 -> ? + *   GPIO 8 -> route input jack to line-in (0) or mic-in (1)   * - * CS4398: - * - * AD0 <- 1 - * AD1 <- 1 + * CM9780:   * - * CS4362A: + *   LINE_OUT -> input of ADC   * - * AD0 <- 0 + *   AUX_IN  <- aux + *   MIC_IN  <- mic + *   FMIC_IN <- front mic   * - * CM9780: - * - * GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input + *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input   */  #include <linux/pci.h> @@ -63,6 +62,7 @@  #define GPI_EXT_POWER		0x01  #define GPIO_D1_OUTPUT_ENABLE	0x0001  #define GPIO_D1_FRONT_PANEL	0x0002 +#define GPIO_D1_MAGIC		0x00c0  #define GPIO_D1_INPUT_ROUTE	0x0100  #define I2C_DEVICE_CS4398	0x9e	/* 10011, AD1=1, AD0=1, /W=0 */ @@ -169,12 +169,12 @@ static void xonar_d1_init(struct oxygen *chip)  	cs43xx_registers_init(chip);  	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, -			  GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE); +			  GPIO_D1_FRONT_PANEL | +			  GPIO_D1_MAGIC | +			  GPIO_D1_INPUT_ROUTE);  	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,  			    GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE); -	oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); -  	xonar_init_cs53x1(chip);  	xonar_enable_output(chip); @@ -284,7 +284,7 @@ static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed)  static const struct snd_kcontrol_new front_panel_switch = {  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -	.name = "Front Panel Switch", +	.name = "Front Panel Playback Switch",  	.info = snd_ctl_boolean_mono_info,  	.get = xonar_gpio_bit_switch_get,  	.put = xonar_gpio_bit_switch_put, @@ -298,13 +298,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,  		"Fast Roll-off", "Slow Roll-off"  	}; -	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	info->count = 1; -	info->value.enumerated.items = 2; -	if (info->value.enumerated.item >= 2) -		info->value.enumerated.item = 1; -	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); -	return 0; +	return snd_ctl_enum_info(info, 1, 2, names);  }  static int rolloff_get(struct snd_kcontrol *ctl, @@ -380,6 +374,30 @@ static int xonar_d1_mixer_init(struct oxygen *chip)  	return 0;  } +static void dump_cs4362a_registers(struct xonar_cs43xx *data, +				   struct snd_info_buffer *buffer) +{ +	unsigned int i; + +	snd_iprintf(buffer, "\nCS4362A:"); +	for (i = 1; i <= 14; ++i) +		snd_iprintf(buffer, " %02x", data->cs4362a_regs[i]); +	snd_iprintf(buffer, "\n"); +} + +static void dump_d1_registers(struct oxygen *chip, +			      struct snd_info_buffer *buffer) +{ +	struct xonar_cs43xx *data = chip->model_data; +	unsigned int i; + +	snd_iprintf(buffer, "\nCS4398: 7?"); +	for (i = 2; i < 8; ++i) +		snd_iprintf(buffer, " %02x", data->cs4398_regs[i]); +	snd_iprintf(buffer, "\n"); +	dump_cs4362a_registers(data, buffer); +} +  static const struct oxygen_model model_xonar_d1 = {  	.longname = "Asus Virtuoso 100",  	.chip = "AV200", @@ -388,28 +406,33 @@ static const struct oxygen_model model_xonar_d1 = {  	.cleanup = xonar_d1_cleanup,  	.suspend = xonar_d1_suspend,  	.resume = xonar_d1_resume, -	.get_i2s_mclk = oxygen_default_i2s_mclk,  	.set_dac_params = set_cs43xx_params,  	.set_adc_params = xonar_set_cs53x1_params,  	.update_dac_volume = update_cs43xx_volume,  	.update_dac_mute = update_cs43xx_mute,  	.update_center_lfe_mix = update_cs43xx_center_lfe_mix,  	.ac97_switch = xonar_d1_line_mic_ac97_switch, +	.dump_registers = dump_d1_registers,  	.dac_tlv = cs4362a_db_scale,  	.model_data_size = sizeof(struct xonar_cs43xx),  	.device_config = PLAYBACK_0_TO_I2S |  			 PLAYBACK_1_TO_SPDIF | -			 CAPTURE_0_FROM_I2S_2, -	.dac_channels = 8, +			 CAPTURE_0_FROM_I2S_2 | +			 CAPTURE_1_FROM_SPDIF | +			 AC97_FMIC_SWITCH, +	.dac_channels_pcm = 8, +	.dac_channels_mixer = 8,  	.dac_volume_min = 127 - 60,  	.dac_volume_max = 127,  	.function_flags = OXYGEN_FUNCTION_2WIRE, +	.dac_mclks = OXYGEN_MCLKS(256, 128, 128), +	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),  	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,  	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,  }; -int __devinit get_xonar_cs43xx_model(struct oxygen *chip, -				     const struct pci_device_id *id) +int get_xonar_cs43xx_model(struct oxygen *chip, +			   const struct pci_device_id *id)  {  	switch (id->subdevice) {  	case 0x834f: diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c new file mode 100644 index 00000000000..4cf3200e988 --- /dev/null +++ b/sound/pci/oxygen/xonar_dg.c @@ -0,0 +1,295 @@ +/* + * card driver for the Xonar DG/DGX + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Copyright (c) Roman Volkov <v1ron@mail.ru> + * + *  This driver is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License, version 2. + * + *  This driver is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this driver; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Xonar DG/DGX + * ------------ + * + * CS4245 and CS4361 both will mute all outputs if any clock ratio + * is invalid. + * + * CMI8788: + * + *   SPI 0 -> CS4245 + * + *   Playback: + *   I²S 1 -> CS4245 + *   I²S 2 -> CS4361 (center/LFE) + *   I²S 3 -> CS4361 (surround) + *   I²S 4 -> CS4361 (front) + *   Capture: + *   I²S ADC 1 <- CS4245 + * + *   GPIO 3 <- ? + *   GPIO 4 <- headphone detect + *   GPIO 5 -> enable ADC analog circuit for the left channel + *   GPIO 6 -> enable ADC analog circuit for the right channel + *   GPIO 7 -> switch green rear output jack between CS4245 and and the first + *             channel of CS4361 (mechanical relay) + *   GPIO 8 -> enable output to speakers + * + * CS4245: + * + *   input 0 <- mic + *   input 1 <- aux + *   input 2 <- front mic + *   input 4 <- line + *   DAC out -> headphones + *   aux out -> front panel headphones + */ + +#include <linux/pci.h> +#include <linux/delay.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/pcm.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "xonar_dg.h" +#include "cs4245.h" + +int cs4245_write_spi(struct oxygen *chip, u8 reg) +{ +	struct dg *data = chip->model_data; +	unsigned int packet; + +	packet = reg << 8; +	packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16; +	packet |= data->cs4245_shadow[reg]; + +	return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | +				OXYGEN_SPI_DATA_LENGTH_3 | +				OXYGEN_SPI_CLOCK_1280 | +				(0 << OXYGEN_SPI_CODEC_SHIFT) | +				OXYGEN_SPI_CEN_LATCH_CLOCK_HI, +				packet); +} + +int cs4245_read_spi(struct oxygen *chip, u8 addr) +{ +	struct dg *data = chip->model_data; +	int ret; + +	ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | +		OXYGEN_SPI_DATA_LENGTH_2 | +		OXYGEN_SPI_CEN_LATCH_CLOCK_HI | +		OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT), +		((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr); +	if (ret < 0) +		return ret; + +	ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | +		OXYGEN_SPI_DATA_LENGTH_2 | +		OXYGEN_SPI_CEN_LATCH_CLOCK_HI | +		OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT), +		(CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8); +	if (ret < 0) +		return ret; + +	data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1); + +	return 0; +} + +int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op) +{ +	struct dg *data = chip->model_data; +	unsigned char addr; +	int ret; + +	for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) { +		ret = (op == CS4245_SAVE_TO_SHADOW ? +			cs4245_read_spi(chip, addr) : +			cs4245_write_spi(chip, addr)); +		if (ret < 0) +			return ret; +	} +	return 0; +} + +static void cs4245_init(struct oxygen *chip) +{ +	struct dg *data = chip->model_data; + +	/* save the initial state: codec version, registers */ +	cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW); + +	/* +	 * Power up the CODEC internals, enable soft ramp & zero cross, work in +	 * async. mode, enable aux output from DAC. Invert DAC output as in the +	 * Windows driver. +	 */ +	data->cs4245_shadow[CS4245_POWER_CTRL] = 0; +	data->cs4245_shadow[CS4245_SIGNAL_SEL] = +		CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH; +	data->cs4245_shadow[CS4245_DAC_CTRL_1] = +		CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST; +	data->cs4245_shadow[CS4245_DAC_CTRL_2] = +		CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC; +	data->cs4245_shadow[CS4245_ADC_CTRL] = +		CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST; +	data->cs4245_shadow[CS4245_ANALOG_IN] = +		CS4245_PGA_SOFT | CS4245_PGA_ZERO; +	data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0; +	data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0; +	data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8; +	data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8; + +	cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); +	snd_component_add(chip->card, "CS4245"); +} + +void dg_init(struct oxygen *chip) +{ +	struct dg *data = chip->model_data; + +	data->output_sel = PLAYBACK_DST_HP_FP; +	data->input_sel = CAPTURE_SRC_MIC; + +	cs4245_init(chip); +	oxygen_write16(chip, OXYGEN_GPIO_CONTROL, +		       GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE); +	/* anti-pop delay, wait some time before enabling the output */ +	msleep(2500); +	oxygen_write16(chip, OXYGEN_GPIO_DATA, +		       GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE); +} + +void dg_cleanup(struct oxygen *chip) +{ +	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); +} + +void dg_suspend(struct oxygen *chip) +{ +	dg_cleanup(chip); +} + +void dg_resume(struct oxygen *chip) +{ +	cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); +	msleep(2500); +	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); +} + +void set_cs4245_dac_params(struct oxygen *chip, +				  struct snd_pcm_hw_params *params) +{ +	struct dg *data = chip->model_data; +	unsigned char dac_ctrl; +	unsigned char mclk_freq; + +	dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; +	mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK; +	if (params_rate(params) <= 50000) { +		dac_ctrl |= CS4245_DAC_FM_SINGLE; +		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT; +	} else if (params_rate(params) <= 100000) { +		dac_ctrl |= CS4245_DAC_FM_DOUBLE; +		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT; +	} else { +		dac_ctrl |= CS4245_DAC_FM_QUAD; +		mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT; +	} +	data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl; +	data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq; +	cs4245_write_spi(chip, CS4245_DAC_CTRL_1); +	cs4245_write_spi(chip, CS4245_MCLK_FREQ); +} + +void set_cs4245_adc_params(struct oxygen *chip, +				  struct snd_pcm_hw_params *params) +{ +	struct dg *data = chip->model_data; +	unsigned char adc_ctrl; +	unsigned char mclk_freq; + +	adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; +	mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK; +	if (params_rate(params) <= 50000) { +		adc_ctrl |= CS4245_ADC_FM_SINGLE; +		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT; +	} else if (params_rate(params) <= 100000) { +		adc_ctrl |= CS4245_ADC_FM_DOUBLE; +		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT; +	} else { +		adc_ctrl |= CS4245_ADC_FM_QUAD; +		mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT; +	} +	data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl; +	data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq; +	cs4245_write_spi(chip, CS4245_ADC_CTRL); +	cs4245_write_spi(chip, CS4245_MCLK_FREQ); +} + +static inline unsigned int shift_bits(unsigned int value, +				      unsigned int shift_from, +				      unsigned int shift_to, +				      unsigned int mask) +{ +	if (shift_from < shift_to) +		return (value << (shift_to - shift_from)) & mask; +	else +		return (value >> (shift_from - shift_to)) & mask; +} + +unsigned int adjust_dg_dac_routing(struct oxygen *chip, +					  unsigned int play_routing) +{ +	struct dg *data = chip->model_data; + +	switch (data->output_sel) { +	case PLAYBACK_DST_HP: +	case PLAYBACK_DST_HP_FP: +		oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING, +			OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 | +			OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK); +		break; +	case PLAYBACK_DST_MULTICH: +		oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING, +			OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK); +		break; +	} +	return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) | +	       shift_bits(play_routing, +			  OXYGEN_PLAY_DAC2_SOURCE_SHIFT, +			  OXYGEN_PLAY_DAC1_SOURCE_SHIFT, +			  OXYGEN_PLAY_DAC1_SOURCE_MASK) | +	       shift_bits(play_routing, +			  OXYGEN_PLAY_DAC1_SOURCE_SHIFT, +			  OXYGEN_PLAY_DAC2_SOURCE_SHIFT, +			  OXYGEN_PLAY_DAC2_SOURCE_MASK) | +	       shift_bits(play_routing, +			  OXYGEN_PLAY_DAC0_SOURCE_SHIFT, +			  OXYGEN_PLAY_DAC3_SOURCE_SHIFT, +			  OXYGEN_PLAY_DAC3_SOURCE_MASK); +} + +void dump_cs4245_registers(struct oxygen *chip, +				  struct snd_info_buffer *buffer) +{ +	struct dg *data = chip->model_data; +	unsigned int addr; + +	snd_iprintf(buffer, "\nCS4245:"); +	cs4245_read_spi(chip, CS4245_INT_STATUS); +	for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) +		snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]); +	snd_iprintf(buffer, "\n"); +} diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h new file mode 100644 index 00000000000..d461df357aa --- /dev/null +++ b/sound/pci/oxygen/xonar_dg.h @@ -0,0 +1,56 @@ +#ifndef XONAR_DG_H_INCLUDED +#define XONAR_DG_H_INCLUDED + +#include "oxygen.h" + +#define GPIO_MAGIC		0x0008 +#define GPIO_HP_DETECT		0x0010 +#define GPIO_INPUT_ROUTE	0x0060 +#define GPIO_HP_REAR		0x0080 +#define GPIO_OUTPUT_ENABLE	0x0100 + +#define CAPTURE_SRC_MIC		0 +#define CAPTURE_SRC_FP_MIC	1 +#define CAPTURE_SRC_LINE	2 +#define CAPTURE_SRC_AUX		3 + +#define PLAYBACK_DST_HP		0 +#define PLAYBACK_DST_HP_FP	1 +#define PLAYBACK_DST_MULTICH	2 + +enum cs4245_shadow_operation { +	CS4245_SAVE_TO_SHADOW, +	CS4245_LOAD_FROM_SHADOW +}; + +struct dg { +	/* shadow copy of the CS4245 register space */ +	unsigned char cs4245_shadow[17]; +	/* output select: headphone/speakers */ +	unsigned char output_sel; +	/* volumes for all capture sources */ +	char input_vol[4][2]; +	/* input select: mic/fp mic/line/aux */ +	unsigned char input_sel; +}; + +/* Xonar DG control routines */ +int cs4245_write_spi(struct oxygen *chip, u8 reg); +int cs4245_read_spi(struct oxygen *chip, u8 reg); +int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op); +void dg_init(struct oxygen *chip); +void set_cs4245_dac_params(struct oxygen *chip, +				  struct snd_pcm_hw_params *params); +void set_cs4245_adc_params(struct oxygen *chip, +				  struct snd_pcm_hw_params *params); +unsigned int adjust_dg_dac_routing(struct oxygen *chip, +					  unsigned int play_routing); +void dump_cs4245_registers(struct oxygen *chip, +				struct snd_info_buffer *buffer); +void dg_suspend(struct oxygen *chip); +void dg_resume(struct oxygen *chip); +void dg_cleanup(struct oxygen *chip); + +extern struct oxygen_model model_xonar_dg; + +#endif diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c new file mode 100644 index 00000000000..b885dac28a0 --- /dev/null +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -0,0 +1,477 @@ +/* + * Mixer controls for the Xonar DG/DGX + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Copyright (c) Roman Volkov <v1ron@mail.ru> + * + *  This driver is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License, version 2. + * + *  This driver is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this driver; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/pci.h> +#include <linux/delay.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/pcm.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "xonar_dg.h" +#include "cs4245.h" + +/* analog output select */ + +static int output_select_apply(struct oxygen *chip) +{ +	struct dg *data = chip->model_data; + +	data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK; +	if (data->output_sel == PLAYBACK_DST_HP) { +		/* mute FP (aux output) amplifier, switch rear jack to CS4245 */ +		oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); +	} else if (data->output_sel == PLAYBACK_DST_HP_FP) { +		/* +		 * Unmute FP amplifier, switch rear jack to CS4361; +		 * I2S channels 2,3,4 should be inactive. +		 */ +		oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); +		data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC; +	} else { +		/* +		 * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp., +		 * and change playback routing. +		 */ +		oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); +	} +	return cs4245_write_spi(chip, CS4245_SIGNAL_SEL); +} + +static int output_select_info(struct snd_kcontrol *ctl, +			      struct snd_ctl_elem_info *info) +{ +	static const char *const names[3] = { +		"Stereo Headphones", +		"Stereo Headphones FP", +		"Multichannel", +	}; + +	return snd_ctl_enum_info(info, 1, 3, names); +} + +static int output_select_get(struct snd_kcontrol *ctl, +			     struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; + +	mutex_lock(&chip->mutex); +	value->value.enumerated.item[0] = data->output_sel; +	mutex_unlock(&chip->mutex); +	return 0; +} + +static int output_select_put(struct snd_kcontrol *ctl, +			     struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; +	unsigned int new = value->value.enumerated.item[0]; +	int changed = 0; +	int ret; + +	mutex_lock(&chip->mutex); +	if (data->output_sel != new) { +		data->output_sel = new; +		ret = output_select_apply(chip); +		changed = ret >= 0 ? 1 : ret; +		oxygen_update_dac_routing(chip); +	} +	mutex_unlock(&chip->mutex); + +	return changed; +} + +/* CS4245 Headphone Channels A&B Volume Control */ + +static int hp_stereo_volume_info(struct snd_kcontrol *ctl, +				struct snd_ctl_elem_info *info) +{ +	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; +	info->count = 2; +	info->value.integer.min = 0; +	info->value.integer.max = 255; +	return 0; +} + +static int hp_stereo_volume_get(struct snd_kcontrol *ctl, +				struct snd_ctl_elem_value *val) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; +	unsigned int tmp; + +	mutex_lock(&chip->mutex); +	tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255; +	val->value.integer.value[0] = tmp; +	tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255; +	val->value.integer.value[1] = tmp; +	mutex_unlock(&chip->mutex); +	return 0; +} + +static int hp_stereo_volume_put(struct snd_kcontrol *ctl, +				struct snd_ctl_elem_value *val) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; +	int ret; +	int changed = 0; +	long new1 = val->value.integer.value[0]; +	long new2 = val->value.integer.value[1]; + +	if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0)) +		return -EINVAL; + +	mutex_lock(&chip->mutex); +	if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) || +	    (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) { +		data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1; +		data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2; +		ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL); +		if (ret >= 0) +			ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL); +		changed = ret >= 0 ? 1 : ret; +	} +	mutex_unlock(&chip->mutex); + +	return changed; +} + +/* Headphone Mute */ + +static int hp_mute_get(struct snd_kcontrol *ctl, +			struct snd_ctl_elem_value *val) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; + +	mutex_lock(&chip->mutex); +	val->value.integer.value[0] = +		!(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC); +	mutex_unlock(&chip->mutex); +	return 0; +} + +static int hp_mute_put(struct snd_kcontrol *ctl, +			struct snd_ctl_elem_value *val) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; +	int ret; +	int changed; + +	if (val->value.integer.value[0] > 1) +		return -EINVAL; +	mutex_lock(&chip->mutex); +	data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC; +	data->cs4245_shadow[CS4245_DAC_CTRL_1] |= +		(~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC; +	ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1); +	changed = ret >= 0 ? 1 : ret; +	mutex_unlock(&chip->mutex); +	return changed; +} + +/* capture volume for all sources */ + +static int input_volume_apply(struct oxygen *chip, char left, char right) +{ +	struct dg *data = chip->model_data; +	int ret; + +	data->cs4245_shadow[CS4245_PGA_A_CTRL] = left; +	data->cs4245_shadow[CS4245_PGA_B_CTRL] = right; +	ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL); +	if (ret < 0) +		return ret; +	return cs4245_write_spi(chip, CS4245_PGA_B_CTRL); +} + +static int input_vol_info(struct snd_kcontrol *ctl, +			  struct snd_ctl_elem_info *info) +{ +	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; +	info->count = 2; +	info->value.integer.min = 2 * -12; +	info->value.integer.max = 2 * 12; +	return 0; +} + +static int input_vol_get(struct snd_kcontrol *ctl, +			 struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; +	unsigned int idx = ctl->private_value; + +	mutex_lock(&chip->mutex); +	value->value.integer.value[0] = data->input_vol[idx][0]; +	value->value.integer.value[1] = data->input_vol[idx][1]; +	mutex_unlock(&chip->mutex); +	return 0; +} + +static int input_vol_put(struct snd_kcontrol *ctl, +			 struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; +	unsigned int idx = ctl->private_value; +	int changed = 0; +	int ret = 0; + +	if (value->value.integer.value[0] < 2 * -12 || +	    value->value.integer.value[0] > 2 * 12 || +	    value->value.integer.value[1] < 2 * -12 || +	    value->value.integer.value[1] > 2 * 12) +		return -EINVAL; +	mutex_lock(&chip->mutex); +	changed = data->input_vol[idx][0] != value->value.integer.value[0] || +		  data->input_vol[idx][1] != value->value.integer.value[1]; +	if (changed) { +		data->input_vol[idx][0] = value->value.integer.value[0]; +		data->input_vol[idx][1] = value->value.integer.value[1]; +		if (idx == data->input_sel) { +			ret = input_volume_apply(chip, +				data->input_vol[idx][0], +				data->input_vol[idx][1]); +		} +		changed = ret >= 0 ? 1 : ret; +	} +	mutex_unlock(&chip->mutex); +	return changed; +} + +/* Capture Source */ + +static int input_source_apply(struct oxygen *chip) +{ +	struct dg *data = chip->model_data; + +	data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK; +	if (data->input_sel == CAPTURE_SRC_FP_MIC) +		data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2; +	else if (data->input_sel == CAPTURE_SRC_LINE) +		data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4; +	else if (data->input_sel != CAPTURE_SRC_MIC) +		data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1; +	return cs4245_write_spi(chip, CS4245_ANALOG_IN); +} + +static int input_sel_info(struct snd_kcontrol *ctl, +			  struct snd_ctl_elem_info *info) +{ +	static const char *const names[4] = { +		"Mic", "Front Mic", "Line", "Aux" +	}; + +	return snd_ctl_enum_info(info, 1, 4, names); +} + +static int input_sel_get(struct snd_kcontrol *ctl, +			 struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; + +	mutex_lock(&chip->mutex); +	value->value.enumerated.item[0] = data->input_sel; +	mutex_unlock(&chip->mutex); +	return 0; +} + +static int input_sel_put(struct snd_kcontrol *ctl, +			 struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; +	int changed; +	int ret; + +	if (value->value.enumerated.item[0] > 3) +		return -EINVAL; + +	mutex_lock(&chip->mutex); +	changed = value->value.enumerated.item[0] != data->input_sel; +	if (changed) { +		data->input_sel = value->value.enumerated.item[0]; + +		ret = input_source_apply(chip); +		if (ret >= 0) +			ret = input_volume_apply(chip, +				data->input_vol[data->input_sel][0], +				data->input_vol[data->input_sel][1]); +		changed = ret >= 0 ? 1 : ret; +	} +	mutex_unlock(&chip->mutex); +	return changed; +} + +/* ADC high-pass filter */ + +static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ +	static const char *const names[2] = { "Active", "Frozen" }; + +	return snd_ctl_enum_info(info, 1, 2, names); +} + +static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; + +	value->value.enumerated.item[0] = +		!!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); +	return 0; +} + +static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ +	struct oxygen *chip = ctl->private_data; +	struct dg *data = chip->model_data; +	u8 reg; +	int changed; + +	mutex_lock(&chip->mutex); +	reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; +	if (value->value.enumerated.item[0]) +		reg |= CS4245_HPF_FREEZE; +	changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; +	if (changed) { +		data->cs4245_shadow[CS4245_ADC_CTRL] = reg; +		cs4245_write_spi(chip, CS4245_ADC_CTRL); +	} +	mutex_unlock(&chip->mutex); +	return changed; +} + +#define INPUT_VOLUME(xname, index) { \ +	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ +	.name = xname, \ +	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ +		  SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ +	.info = input_vol_info, \ +	.get = input_vol_get, \ +	.put = input_vol_put, \ +	.tlv = { .p = pga_db_scale }, \ +	.private_value = index, \ +} +static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0); +static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200); +static const struct snd_kcontrol_new dg_controls[] = { +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Analog Output Playback Enum", +		.info = output_select_info, +		.get = output_select_get, +		.put = output_select_put, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Headphone Playback Volume", +		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | +			  SNDRV_CTL_ELEM_ACCESS_TLV_READ, +		.info = hp_stereo_volume_info, +		.get = hp_stereo_volume_get, +		.put = hp_stereo_volume_put, +		.tlv = { .p = hp_db_scale, }, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Headphone Playback Switch", +		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, +		.info = snd_ctl_boolean_mono_info, +		.get = hp_mute_get, +		.put = hp_mute_put, +	}, +	INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC), +	INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC), +	INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE), +	INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Capture Source", +		.info = input_sel_info, +		.get = input_sel_get, +		.put = input_sel_put, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "ADC High-pass Filter Capture Enum", +		.info = hpf_info, +		.get = hpf_get, +		.put = hpf_put, +	}, +}; + +static int dg_control_filter(struct snd_kcontrol_new *template) +{ +	if (!strncmp(template->name, "Master Playback ", 16)) +		return 1; +	return 0; +} + +static int dg_mixer_init(struct oxygen *chip) +{ +	unsigned int i; +	int err; + +	output_select_apply(chip); +	input_source_apply(chip); +	oxygen_update_dac_routing(chip); + +	for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { +		err = snd_ctl_add(chip->card, +				  snd_ctl_new1(&dg_controls[i], chip)); +		if (err < 0) +			return err; +	} + +	return 0; +} + +struct oxygen_model model_xonar_dg = { +	.longname = "C-Media Oxygen HD Audio", +	.chip = "CMI8786", +	.init = dg_init, +	.control_filter = dg_control_filter, +	.mixer_init = dg_mixer_init, +	.cleanup = dg_cleanup, +	.suspend = dg_suspend, +	.resume = dg_resume, +	.set_dac_params = set_cs4245_dac_params, +	.set_adc_params = set_cs4245_adc_params, +	.adjust_dac_routing = adjust_dg_dac_routing, +	.dump_registers = dump_cs4245_registers, +	.model_data_size = sizeof(struct dg), +	.device_config = PLAYBACK_0_TO_I2S | +			 PLAYBACK_1_TO_SPDIF | +			 CAPTURE_0_FROM_I2S_1 | +			 CAPTURE_1_FROM_SPDIF, +	.dac_channels_pcm = 6, +	.dac_channels_mixer = 0, +	.function_flags = OXYGEN_FUNCTION_SPI, +	.dac_mclks = OXYGEN_MCLKS(256, 128, 128), +	.adc_mclks = OXYGEN_MCLKS(256, 128, 128), +	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; diff --git a/sound/pci/oxygen/xonar_hdmi.c b/sound/pci/oxygen/xonar_hdmi.c index b12db1f1cea..91d92bc32b7 100644 --- a/sound/pci/oxygen/xonar_hdmi.c +++ b/sound/pci/oxygen/xonar_hdmi.c @@ -1,5 +1,5 @@  /* - * helper functions for HDMI models (Xonar HDAV1.3) + * helper functions for HDMI models (Xonar HDAV1.3/HDAV1.3 Slim)   *   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>   * @@ -120,7 +120,7 @@ void xonar_hdmi_uart_input(struct oxygen *chip)  	if (chip->uart_input_count >= 2 &&  	    chip->uart_input[chip->uart_input_count - 2] == 'O' &&  	    chip->uart_input[chip->uart_input_count - 1] == 'K') { -		printk(KERN_DEBUG "message from HDMI chip received:\n"); +		dev_dbg(chip->card->dev, "message from HDMI chip received:\n");  		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,  				     chip->uart_input, chip->uart_input_count);  		chip->uart_input_count = 0; diff --git a/sound/pci/oxygen/xonar_lib.c b/sound/pci/oxygen/xonar_lib.c index b3ff7131665..706b1a42163 100644 --- a/sound/pci/oxygen/xonar_lib.c +++ b/sound/pci/oxygen/xonar_lib.c @@ -56,9 +56,9 @@ static void xonar_ext_power_gpio_changed(struct oxygen *chip)  	if (has_power != data->has_power) {  		data->has_power = has_power;  		if (has_power) { -			snd_printk(KERN_NOTICE "power restored\n"); +			dev_notice(chip->card->dev, "power restored\n");  		} else { -			snd_printk(KERN_CRIT +			dev_crit(chip->card->dev,  				   "Hey! Don't unplug the power cable!\n");  			/* TODO: stop PCMs */  		} @@ -104,9 +104,10 @@ int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,  {  	struct oxygen *chip = ctl->private_data;  	u16 bit = ctl->private_value; +	bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;  	value->value.integer.value[0] = -		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit); +		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert;  	return 0;  } @@ -115,12 +116,13 @@ int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,  {  	struct oxygen *chip = ctl->private_data;  	u16 bit = ctl->private_value; +	bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;  	u16 old_bits, new_bits;  	int changed;  	spin_lock_irq(&chip->reg_lock);  	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); -	if (value->value.integer.value[0]) +	if (!!value->value.integer.value[0] ^ invert)  		new_bits = old_bits | bit;  	else  		new_bits = old_bits & ~bit; diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index d491fd6c0be..c8c7f2c9b35 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -22,20 +22,26 @@   *   * CMI8788:   * - * SPI 0 -> 1st PCM1796 (front) - * SPI 1 -> 2nd PCM1796 (surround) - * SPI 2 -> 3rd PCM1796 (center/LFE) - * SPI 4 -> 4th PCM1796 (back) + *   SPI 0 -> 1st PCM1796 (front) + *   SPI 1 -> 2nd PCM1796 (surround) + *   SPI 2 -> 3rd PCM1796 (center/LFE) + *   SPI 4 -> 4th PCM1796 (back)   * - * GPIO 2 -> M0 of CS5381 - * GPIO 3 -> M1 of CS5381 - * GPIO 5 <- external power present (D2X only) - * GPIO 7 -> ALT - * GPIO 8 -> enable output to speakers + *   GPIO 2 -> M0 of CS5381 + *   GPIO 3 -> M1 of CS5381 + *   GPIO 5 <- external power present (D2X only) + *   GPIO 7 -> ALT + *   GPIO 8 -> enable output to speakers   *   * CM9780:   * - * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input + *   LINE_OUT -> input of ADC + * + *   AUX_IN   <- aux + *   VIDEO_IN <- CD + *   FMIC_IN  <- mic + * + *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input   */  /* @@ -44,52 +50,53 @@   *   * CMI8788:   * - * I²C <-> PCM1796 (front) + *   I²C <-> PCM1796 (addr 1001100) (front)   * - * GPI 0 <- external power present + *   GPI 0 <- external power present   * - * GPIO 0 -> enable output to speakers - * GPIO 2 -> M0 of CS5381 - * GPIO 3 -> M1 of CS5381 - * GPIO 8 -> route input jack to line-in (0) or mic-in (1) + *   GPIO 0 -> enable HDMI (0) or speaker (1) output + *   GPIO 2 -> M0 of CS5381 + *   GPIO 3 -> M1 of CS5381 + *   GPIO 4 <- daughterboard detection + *   GPIO 5 <- daughterboard detection + *   GPIO 6 -> ? + *   GPIO 7 -> ? + *   GPIO 8 -> route input jack to line-in (0) or mic-in (1)   * - * TXD -> HDMI controller - * RXD <- HDMI controller - * - * PCM1796 front: AD1,0 <- 0,0 + *   UART <-> HDMI controller   *   * CM9780:   * - * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input + *   LINE_OUT -> input of ADC + * + *   AUX_IN <- aux + *   CD_IN  <- CD + *   MIC_IN <- mic + * + *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input   *   * no daughterboard   * ----------------   * - * GPIO 4 <- 1 + *   GPIO 4 <- 1   *   * H6 daughterboard   * ----------------   * - * GPIO 4 <- 0 - * GPIO 5 <- 0 - * - * I²C <-> PCM1796 (surround) - *     <-> PCM1796 (center/LFE) - *     <-> PCM1796 (back) + *   GPIO 4 <- 0 + *   GPIO 5 <- 0   * - * PCM1796 surround:   AD1,0 <- 0,1 - * PCM1796 center/LFE: AD1,0 <- 1,0 - * PCM1796 back:       AD1,0 <- 1,1 + *   I²C <-> PCM1796 (addr 1001101) (surround) + *       <-> PCM1796 (addr 1001110) (center/LFE) + *       <-> PCM1796 (addr 1001111) (back)   *   * unknown daughterboard   * ---------------------   * - * GPIO 4 <- 0 - * GPIO 5 <- 1 - * - * I²C <-> CS4362A (surround, center/LFE, back) + *   GPIO 4 <- 0 + *   GPIO 5 <- 1   * - * CS4362A: AD0 <- 0 + *   I²C <-> CS4362A (addr 0011000) (surround, center/LFE, back)   */  /* @@ -98,32 +105,35 @@   *   * CMI8788:   * - * I²C <-> PCM1792A - *     <-> CS2000 (ST only) + *   I²C <-> PCM1792A (addr 1001100) + *       <-> CS2000 (addr 1001110) (ST only)   * - * ADC1 MCLK -> REF_CLK of CS2000 (ST only) + *   ADC1 MCLK -> REF_CLK of CS2000 (ST only)   * - * GPI 0 <- external power present (STX only) + *   GPI 0 <- external power present (STX only)   * - * 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) + *   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 4 <- daughterboard detection + *   GPIO 5 <- daughterboard detection + *   GPIO 6 -> ? + *   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:   * - * AD1,0 <- 0,0 - * SCK <- CLK_OUT of CS2000 (ST only) + *   SCK <- CLK_OUT of CS2000 (ST only)   * - * CS2000: + * CM9780:   * - * AD0 <- 0 + *   LINE_OUT -> input of ADC   * - * CM9780: + *   AUX_IN <- aux + *   MIC_IN <- mic   * - * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input + *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input   *   * H6 daughterboard   * ---------------- @@ -133,15 +143,39 @@   */  /* - * Xonar HDAV1.3 Slim - * ------------------ + * Xonar Xense + * -----------   *   * CMI8788:   * - * GPIO 1 -> enable output + *   I²C <-> PCM1796 (addr 1001100) (front) + *       <-> CS4362A (addr 0011000) (surround, center/LFE, back) + *       <-> CS2000 (addr 1001110) + * + *   ADC1 MCLK -> REF_CLK of CS2000 + * + *   GPI 0 <- external power present + * + *   GPIO 0 -> enable output + *   GPIO 1 -> route HP to front panel (0) or rear jack (1) + *   GPIO 2 -> M0 of CS5381 + *   GPIO 3 -> M1 of CS5381 + *   GPIO 4 -> enable output + *   GPIO 5 -> enable output + *   GPIO 6 -> ? + *   GPIO 7 -> route output to HP (0) or speaker (1) + *   GPIO 8 -> route input jack to mic-in (0) or line-in (1)   * - * TXD -> HDMI controller - * RXD <- HDMI controller + * CM9780: + * + *   LINE_OUT -> input of ADC + * + *   AUX_IN   <- aux + *   VIDEO_IN <- ? + *   FMIC_IN  <- mic + * + *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input + *   GPO 1 -> route mic-in from input jack (0) or front panel header (1)   */  #include <linux/pci.h> @@ -150,6 +184,7 @@  #include <sound/ac97_codec.h>  #include <sound/control.h>  #include <sound/core.h> +#include <sound/info.h>  #include <sound/pcm.h>  #include <sound/pcm_params.h>  #include <sound/tlv.h> @@ -167,12 +202,14 @@  #define GPIO_INPUT_ROUTE	0x0100  #define GPIO_HDAV_OUTPUT_ENABLE	0x0001 +#define GPIO_HDAV_MAGIC		0x00c0  #define GPIO_DB_MASK		0x0030  #define GPIO_DB_H6		0x0000  #define GPIO_ST_OUTPUT_ENABLE	0x0001  #define GPIO_ST_HP_REAR		0x0002 +#define GPIO_ST_MAGIC		0x0040  #define GPIO_ST_HP		0x0080  #define I2C_DEVICE_PCM1796(i)	(0x98 + ((i) << 1))	/* 10011, ii, /W=0 */ @@ -186,11 +223,12 @@ struct xonar_pcm179x {  	unsigned int dacs;  	u8 pcm1796_regs[4][5];  	unsigned int current_rate; -	bool os_128; +	bool h6;  	bool hp_active;  	s8 hp_gain_offset;  	bool has_cs2000; -	u8 cs2000_fun_cfg_1; +	u8 cs2000_regs[0x1f]; +	bool broken_i2c;  };  struct xonar_hdav { @@ -249,16 +287,14 @@ static void cs2000_write(struct oxygen *chip, u8 reg, u8 value)  	struct xonar_pcm179x *data = chip->model_data;  	oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value); -	if (reg == CS2000_FUN_CFG_1) -		data->cs2000_fun_cfg_1 = value; +	data->cs2000_regs[reg] = value;  }  static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value)  {  	struct xonar_pcm179x *data = chip->model_data; -	if (reg != CS2000_FUN_CFG_1 || -	    value != data->cs2000_fun_cfg_1) +	if (value != data->cs2000_regs[reg])  		cs2000_write(chip, reg, value);  } @@ -268,6 +304,7 @@ static void pcm1796_registers_init(struct oxygen *chip)  	unsigned int i;  	s8 gain_offset; +	msleep(1);  	gain_offset = data->hp_active ? data->hp_gain_offset : 0;  	for (i = 0; i < data->dacs; ++i) {  		/* set ATLD before ATL/ATR */ @@ -282,6 +319,7 @@ static void pcm1796_registers_init(struct oxygen *chip)  		pcm1796_write(chip, i, 20,  			      data->pcm1796_regs[0][20 - PCM1796_REG_BASE]);  		pcm1796_write(chip, i, 21, 0); +		gain_offset = 0;  	}  } @@ -289,11 +327,14 @@ static void pcm1796_init(struct oxygen *chip)  {  	struct xonar_pcm179x *data = chip->model_data; -	data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE | -		PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; +	data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = +		PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD; +	if (!data->broken_i2c) +		data->pcm1796_regs[0][18 - PCM1796_REG_BASE] |= PCM1796_MUTE;  	data->pcm1796_regs[0][19 - PCM1796_REG_BASE] =  		PCM1796_FLT_SHARP | PCM1796_ATS_1; -	data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64; +	data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = +		data->h6 ? PCM1796_OS_64 : PCM1796_OS_128;  	pcm1796_registers_init(chip);  	data->current_rate = 48000;  } @@ -339,18 +380,20 @@ static void xonar_hdav_init(struct oxygen *chip)  	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,  		       OXYGEN_2WIRE_LENGTH_8 |  		       OXYGEN_2WIRE_INTERRUPT_MASK | -		       OXYGEN_2WIRE_SPEED_FAST); +		       OXYGEN_2WIRE_SPEED_STANDARD);  	data->pcm179x.generic.anti_pop_delay = 100;  	data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE;  	data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA;  	data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;  	data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER; -	data->pcm179x.dacs = chip->model.private_data ? 4 : 1; +	data->pcm179x.dacs = chip->model.dac_channels_mixer / 2; +	data->pcm179x.h6 = chip->model.dac_channels_mixer > 2;  	pcm1796_init(chip); -	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_INPUT_ROUTE); +	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, +			  GPIO_HDAV_MAGIC | GPIO_INPUT_ROUTE);  	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE);  	xonar_init_cs53x1(chip); @@ -367,7 +410,7 @@ static void xonar_st_init_i2c(struct oxygen *chip)  	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,  		       OXYGEN_2WIRE_LENGTH_8 |  		       OXYGEN_2WIRE_INTERRUPT_MASK | -		       OXYGEN_2WIRE_SPEED_FAST); +		       OXYGEN_2WIRE_SPEED_STANDARD);  }  static void xonar_st_init_common(struct oxygen *chip) @@ -375,13 +418,14 @@ static void xonar_st_init_common(struct oxygen *chip)  	struct xonar_pcm179x *data = chip->model_data;  	data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; -	data->dacs = chip->model.private_data ? 4 : 1; +	data->dacs = chip->model.dac_channels_mixer / 2;  	data->hp_gain_offset = 2*-18;  	pcm1796_init(chip);  	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, -			  GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); +			  GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | +			  GPIO_ST_MAGIC | GPIO_ST_HP);  	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,  			    GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); @@ -410,9 +454,11 @@ static void cs2000_registers_init(struct oxygen *chip)  	cs2000_write(chip, CS2000_RATIO_0 + 1, 0x10);  	cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00);  	cs2000_write(chip, CS2000_RATIO_0 + 3, 0x00); -	cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1); +	cs2000_write(chip, CS2000_FUN_CFG_1, +		     data->cs2000_regs[CS2000_FUN_CFG_1]);  	cs2000_write(chip, CS2000_FUN_CFG_2, 0);  	cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2); +	msleep(3); /* PLL lock delay */  }  static void xonar_st_init(struct oxygen *chip) @@ -420,13 +466,18 @@ static void xonar_st_init(struct oxygen *chip)  	struct xonar_pcm179x *data = chip->model_data;  	data->generic.anti_pop_delay = 100; +	data->h6 = chip->model.dac_channels_mixer > 2;  	data->has_cs2000 = 1; -	data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; +	data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1; +	data->broken_i2c = true;  	oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, -		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_I2S | -		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | -		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); +		       OXYGEN_RATE_48000 | +		       OXYGEN_I2S_FORMAT_I2S | +		       OXYGEN_I2S_MCLK(data->h6 ? MCLK_256 : MCLK_512) | +		       OXYGEN_I2S_BITS_16 | +		       OXYGEN_I2S_MASTER | +		       OXYGEN_I2S_BCLK_64);  	xonar_st_init_i2c(chip);  	cs2000_registers_init(chip); @@ -507,44 +558,16 @@ static void xonar_st_resume(struct oxygen *chip)  	xonar_stx_resume(chip);  } -static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate) -{ -	struct xonar_pcm179x *data = chip->model_data; - -	if (rate <= 32000) -		return OXYGEN_I2S_MCLK_512; -	else if (rate <= 48000 && data->os_128) -		return OXYGEN_I2S_MCLK_512; -	else if (rate <= 96000) -		return OXYGEN_I2S_MCLK_256; -	else -		return OXYGEN_I2S_MCLK_128; -} - -static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip, -					 unsigned int channel, -					 struct snd_pcm_hw_params *params) -{ -	if (channel == PCM_MULTICH) -		return mclk_from_rate(chip, params_rate(params)); -	else -		return oxygen_default_i2s_mclk(chip, channel, params); -} -  static void update_pcm1796_oversampling(struct oxygen *chip)  {  	struct xonar_pcm179x *data = chip->model_data;  	unsigned int i;  	u8 reg; -	if (data->current_rate <= 32000) +	if (data->current_rate <= 48000 && !data->h6)  		reg = PCM1796_OS_128; -	else if (data->current_rate <= 48000 && data->os_128) -		reg = PCM1796_OS_128; -	else if (data->current_rate <= 96000 || data->os_128) -		reg = PCM1796_OS_64;  	else -		reg = PCM1796_OS_32; +		reg = PCM1796_OS_64;  	for (i = 0; i < data->dacs; ++i)  		pcm1796_write_cached(chip, i, 20, reg);  } @@ -554,6 +577,7 @@ static void set_pcm1796_params(struct oxygen *chip,  {  	struct xonar_pcm179x *data = chip->model_data; +	msleep(1);  	data->current_rate = params_rate(params);  	update_pcm1796_oversampling(chip);  } @@ -570,6 +594,7 @@ static void update_pcm1796_volume(struct oxygen *chip)  				     + gain_offset);  		pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]  				     + gain_offset); +		gain_offset = 0;  	}  } @@ -579,7 +604,7 @@ static void update_pcm1796_mute(struct oxygen *chip)  	unsigned int i;  	u8 value; -	value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; +	value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD;  	if (chip->dac_mute)  		value |= PCM1796_MUTE;  	for (i = 0; i < data->dacs; ++i) @@ -592,45 +617,35 @@ static void update_cs2000_rate(struct oxygen *chip, unsigned int rate)  	u8 rate_mclk, reg;  	switch (rate) { -		/* XXX Why is the I2S A MCLK half the actual I2S MCLK? */  	case 32000: -		rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256; -		break; -	case 44100: -		if (data->os_128) -			rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; -		else -			rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128; -		break; -	default: /* 48000 */ -		if (data->os_128) -			rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; -		else -			rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128; -		break;  	case 64000: -		rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256; +		rate_mclk = OXYGEN_RATE_32000;  		break; +	case 44100:  	case 88200: -		rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; -		break; -	case 96000: -		rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; -		break;  	case 176400: -		rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; +		rate_mclk = OXYGEN_RATE_44100;  		break; +	default: +	case 48000: +	case 96000:  	case 192000: -		rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; +		rate_mclk = OXYGEN_RATE_48000;  		break;  	} -	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk, -			      OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK); -	if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128) + +	if (rate <= 96000 && (rate > 48000 || data->h6)) { +		rate_mclk |= OXYGEN_I2S_MCLK(MCLK_256);  		reg = CS2000_REF_CLK_DIV_1; -	else +	} else { +		rate_mclk |= OXYGEN_I2S_MCLK(MCLK_512);  		reg = CS2000_REF_CLK_DIV_2; +	} + +	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk, +			      OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK);  	cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg); +	msleep(3); /* PLL lock delay */  }  static void set_st_params(struct oxygen *chip, @@ -665,13 +680,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,  		"Sharp Roll-off", "Slow Roll-off"  	}; -	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	info->count = 1; -	info->value.enumerated.items = 2; -	if (info->value.enumerated.item >= 2) -		info->value.enumerated.item = 1; -	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); -	return 0; +	return snd_ctl_enum_info(info, 1, 2, names);  }  static int rolloff_get(struct snd_kcontrol *ctl, @@ -719,57 +728,13 @@ static const struct snd_kcontrol_new rolloff_control = {  	.put = rolloff_put,  }; -static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) -{ -	static const char *const names[2] = { "64x", "128x" }; - -	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	info->count = 1; -	info->value.enumerated.items = 2; -	if (info->value.enumerated.item >= 2) -		info->value.enumerated.item = 1; -	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); -	return 0; -} - -static int os_128_get(struct snd_kcontrol *ctl, -		      struct snd_ctl_elem_value *value) -{ -	struct oxygen *chip = ctl->private_data; -	struct xonar_pcm179x *data = chip->model_data; - -	value->value.enumerated.item[0] = data->os_128; -	return 0; -} - -static int os_128_put(struct snd_kcontrol *ctl, -		      struct snd_ctl_elem_value *value) -{ -	struct oxygen *chip = ctl->private_data; -	struct xonar_pcm179x *data = chip->model_data; -	int changed; - -	mutex_lock(&chip->mutex); -	changed = value->value.enumerated.item[0] != data->os_128; -	if (changed) { -		data->os_128 = value->value.enumerated.item[0]; -		if (data->has_cs2000) -			update_cs2000_rate(chip, data->current_rate); -		oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT, -				      mclk_from_rate(chip, data->current_rate), -				      OXYGEN_I2S_MCLK_MASK); -		update_pcm1796_oversampling(chip); -	} -	mutex_unlock(&chip->mutex); -	return changed; -} - -static const struct snd_kcontrol_new os_128_control = { +static const struct snd_kcontrol_new hdav_hdmi_control = {  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -	.name = "DAC Oversampling Playback Enum", -	.info = os_128_info, -	.get = os_128_get, -	.put = os_128_put, +	.name = "HDMI Playback Switch", +	.info = snd_ctl_boolean_mono_info, +	.get = xonar_gpio_bit_switch_get, +	.put = xonar_gpio_bit_switch_put, +	.private_value = GPIO_HDAV_OUTPUT_ENABLE | XONAR_GPIO_BIT_INVERT,  };  static int st_output_switch_info(struct snd_kcontrol *ctl, @@ -779,13 +744,7 @@ static int st_output_switch_info(struct snd_kcontrol *ctl,  		"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; +	return snd_ctl_enum_info(info, 1, 3, names);  }  static int st_output_switch_get(struct snd_kcontrol *ctl, @@ -840,13 +799,7 @@ static int st_hp_volume_offset_info(struct snd_kcontrol *ctl,  		"< 64 ohms", "64-300 ohms", "300-600 ohms"  	}; -	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	info->count = 1; -	info->value.enumerated.items = 3; -	if (info->value.enumerated.item > 2) -		info->value.enumerated.item = 2; -	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); -	return 0; +	return snd_ctl_enum_info(info, 1, 3, names);  }  static int st_hp_volume_offset_get(struct snd_kcontrol *ctl, @@ -928,16 +881,25 @@ static int xonar_d2_control_filter(struct snd_kcontrol_new *template)  	return 0;  } +static int xonar_st_h6_control_filter(struct snd_kcontrol_new *template) +{ +	if (!strncmp(template->name, "Master Playback ", 16)) +		/* no volume/mute, as I²C to the third DAC does not work */ +		return 1; +	return 0; +} +  static int add_pcm1796_controls(struct oxygen *chip)  { +	struct xonar_pcm179x *data = chip->model_data;  	int err; -	err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); -	if (err < 0) -		return err; -	err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); -	if (err < 0) -		return err; +	if (!data->broken_i2c) { +		err = snd_ctl_add(chip->card, +				  snd_ctl_new1(&rolloff_control, chip)); +		if (err < 0) +			return err; +	}  	return 0;  } @@ -956,7 +918,15 @@ static int xonar_d2_mixer_init(struct oxygen *chip)  static int xonar_hdav_mixer_init(struct oxygen *chip)  { -	return add_pcm1796_controls(chip); +	int err; + +	err = snd_ctl_add(chip->card, snd_ctl_new1(&hdav_hdmi_control, chip)); +	if (err < 0) +		return err; +	err = add_pcm1796_controls(chip); +	if (err < 0) +		return err; +	return 0;  }  static int xonar_st_mixer_init(struct oxygen *chip) @@ -976,6 +946,45 @@ static int xonar_st_mixer_init(struct oxygen *chip)  	return 0;  } +static void dump_pcm1796_registers(struct oxygen *chip, +				   struct snd_info_buffer *buffer) +{ +	struct xonar_pcm179x *data = chip->model_data; +	unsigned int dac, i; + +	for (dac = 0; dac < data->dacs; ++dac) { +		snd_iprintf(buffer, "\nPCM1796 %u:", dac + 1); +		for (i = 0; i < 5; ++i) +			snd_iprintf(buffer, " %02x", +				    data->pcm1796_regs[dac][i]); +	} +	snd_iprintf(buffer, "\n"); +} + +static void dump_cs2000_registers(struct oxygen *chip, +				  struct snd_info_buffer *buffer) +{ +	struct xonar_pcm179x *data = chip->model_data; +	unsigned int i; + +	if (data->has_cs2000) { +		snd_iprintf(buffer, "\nCS2000:\n00:   "); +		for (i = 1; i < 0x10; ++i) +			snd_iprintf(buffer, " %02x", data->cs2000_regs[i]); +		snd_iprintf(buffer, "\n10:"); +		for (i = 0x10; i < 0x1f; ++i) +			snd_iprintf(buffer, " %02x", data->cs2000_regs[i]); +		snd_iprintf(buffer, "\n"); +	} +} + +static void dump_st_registers(struct oxygen *chip, +			      struct snd_info_buffer *buffer) +{ +	dump_pcm1796_registers(chip, buffer); +	dump_cs2000_registers(chip, buffer); +} +  static const struct oxygen_model model_xonar_d2 = {  	.longname = "Asus Virtuoso 200",  	.chip = "AV200", @@ -985,11 +994,11 @@ static const struct oxygen_model model_xonar_d2 = {  	.cleanup = xonar_d2_cleanup,  	.suspend = xonar_d2_suspend,  	.resume = xonar_d2_resume, -	.get_i2s_mclk = get_pcm1796_i2s_mclk,  	.set_dac_params = set_pcm1796_params,  	.set_adc_params = xonar_set_cs53x1_params,  	.update_dac_volume = update_pcm1796_volume,  	.update_dac_mute = update_pcm1796_mute, +	.dump_registers = dump_pcm1796_registers,  	.dac_tlv = pcm1796_db_scale,  	.model_data_size = sizeof(struct xonar_pcm179x),  	.device_config = PLAYBACK_0_TO_I2S | @@ -999,13 +1008,16 @@ static const struct oxygen_model model_xonar_d2 = {  			 MIDI_OUTPUT |  			 MIDI_INPUT |  			 AC97_CD_INPUT, -	.dac_channels = 8, +	.dac_channels_pcm = 8, +	.dac_channels_mixer = 8,  	.dac_volume_min = 255 - 2*60,  	.dac_volume_max = 255,  	.misc_flags = OXYGEN_MISC_MIDI,  	.function_flags = OXYGEN_FUNCTION_SPI |  			  OXYGEN_FUNCTION_ENABLE_SPI_4_5, -	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +	.dac_mclks = OXYGEN_MCLKS(512, 128, 128), +	.adc_mclks = OXYGEN_MCLKS(256, 128, 128), +	.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,  	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,  }; @@ -1018,25 +1030,28 @@ static const struct oxygen_model model_xonar_hdav = {  	.suspend = xonar_hdav_suspend,  	.resume = xonar_hdav_resume,  	.pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter, -	.get_i2s_mclk = get_pcm1796_i2s_mclk,  	.set_dac_params = set_hdav_params,  	.set_adc_params = xonar_set_cs53x1_params,  	.update_dac_volume = update_pcm1796_volume,  	.update_dac_mute = update_pcm1796_mute,  	.uart_input = xonar_hdmi_uart_input,  	.ac97_switch = xonar_line_mic_ac97_switch, +	.dump_registers = dump_pcm1796_registers,  	.dac_tlv = pcm1796_db_scale,  	.model_data_size = sizeof(struct xonar_hdav),  	.device_config = PLAYBACK_0_TO_I2S |  			 PLAYBACK_1_TO_SPDIF |  			 CAPTURE_0_FROM_I2S_2 |  			 CAPTURE_1_FROM_SPDIF, -	.dac_channels = 8, +	.dac_channels_pcm = 8, +	.dac_channels_mixer = 2,  	.dac_volume_min = 255 - 2*60,  	.dac_volume_max = 255,  	.misc_flags = OXYGEN_MISC_MIDI,  	.function_flags = OXYGEN_FUNCTION_2WIRE, -	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +	.dac_mclks = OXYGEN_MCLKS(512, 128, 128), +	.adc_mclks = OXYGEN_MCLKS(256, 128, 128), +	.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,  	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,  }; @@ -1048,27 +1063,32 @@ static const struct oxygen_model model_xonar_st = {  	.cleanup = xonar_st_cleanup,  	.suspend = xonar_st_suspend,  	.resume = xonar_st_resume, -	.get_i2s_mclk = get_pcm1796_i2s_mclk,  	.set_dac_params = set_st_params,  	.set_adc_params = xonar_set_cs53x1_params,  	.update_dac_volume = update_pcm1796_volume,  	.update_dac_mute = update_pcm1796_mute,  	.ac97_switch = xonar_line_mic_ac97_switch, +	.dump_registers = dump_st_registers,  	.dac_tlv = pcm1796_db_scale,  	.model_data_size = sizeof(struct xonar_pcm179x),  	.device_config = PLAYBACK_0_TO_I2S |  			 PLAYBACK_1_TO_SPDIF | -			 CAPTURE_0_FROM_I2S_2, -	.dac_channels = 2, +			 CAPTURE_0_FROM_I2S_2 | +			 CAPTURE_1_FROM_SPDIF | +			 AC97_FMIC_SWITCH, +	.dac_channels_pcm = 2, +	.dac_channels_mixer = 2,  	.dac_volume_min = 255 - 2*60,  	.dac_volume_max = 255,  	.function_flags = OXYGEN_FUNCTION_2WIRE, -	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +	.dac_mclks = OXYGEN_MCLKS(512, 128, 128), +	.adc_mclks = OXYGEN_MCLKS(256, 128, 128), +	.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,  	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,  }; -int __devinit get_xonar_pcm179x_model(struct oxygen *chip, -				      const struct pci_device_id *id) +int get_xonar_pcm179x_model(struct oxygen *chip, +			    const struct pci_device_id *id)  {  	switch (id->subdevice) {  	case 0x8269: @@ -1089,7 +1109,8 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,  			break;  		case GPIO_DB_H6:  			chip->model.shortname = "Xonar HDAV1.3+H6"; -			chip->model.private_data = 1; +			chip->model.dac_channels_mixer = 8; +			chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);  			break;  		}  		break; @@ -1102,8 +1123,11 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,  			break;  		case GPIO_DB_H6:  			chip->model.shortname = "Xonar ST+H6"; -			chip->model.dac_channels = 8; -			chip->model.private_data = 1; +			chip->model.control_filter = xonar_st_h6_control_filter; +			chip->model.dac_channels_pcm = 8; +			chip->model.dac_channels_mixer = 8; +			chip->model.dac_volume_min = 255; +			chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);  			break;  		}  		break; @@ -1114,9 +1138,6 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,  		chip->model.resume = xonar_stx_resume;  		chip->model.set_dac_params = set_pcm1796_params;  		break; -	case 0x835e: -		snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n"); -		return -ENODEV;  	default:  		return -EINVAL;  	} diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index 200f7601276..6ce68604c25 100644 --- a/sound/pci/oxygen/xonar_wm87x6.c +++ b/sound/pci/oxygen/xonar_wm87x6.c @@ -1,5 +1,5 @@  /* - * card driver for models with WM8776/WM8766 DACs (Xonar DS) + * card driver for models with WM8776/WM8766 DACs (Xonar DS/HDAV1.3 Slim)   *   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>   * @@ -22,26 +22,48 @@   *   * CMI8788:   * - * SPI 0 -> WM8766 (surround, center/LFE, back) - * SPI 1 -> WM8776 (front, input) + *   SPI 0 -> WM8766 (surround, center/LFE, back) + *   SPI 1 -> WM8776 (front, input)   * - * GPIO 4 <- headphone detect, 0 = plugged - * GPIO 6 -> route input jack to mic-in (0) or line-in (1) - * GPIO 7 -> enable output to front L/R speaker channels - * GPIO 8 -> enable output to other speaker channels and front panel headphone + *   GPIO 4 <- headphone detect, 0 = plugged + *   GPIO 6 -> route input jack to mic-in (0) or line-in (1) + *   GPIO 7 -> enable output to front L/R speaker channels + *   GPIO 8 -> enable output to other speaker channels and front panel headphone   * - * WM8766: + * WM8776:   * - * input 1 <- line - * input 2 <- mic - * input 3 <- front mic - * input 4 <- aux + *   input 1 <- line + *   input 2 <- mic + *   input 3 <- front mic + *   input 4 <- aux + */ + +/* + * Xonar HDAV1.3 Slim + * ------------------ + * + * CMI8788: + * + *   I²C <-> WM8776 (addr 0011010) + * + *   GPIO 0  -> disable HDMI output + *   GPIO 1  -> enable HP output + *   GPIO 6  -> firmware EEPROM I²C clock + *   GPIO 7 <-> firmware EEPROM I²C data + * + *   UART <-> HDMI controller + * + * WM8776: + * + *   input 1 <- mic + *   input 2 <- aux   */  #include <linux/pci.h>  #include <linux/delay.h>  #include <sound/control.h>  #include <sound/core.h> +#include <sound/info.h>  #include <sound/jack.h>  #include <sound/pcm.h>  #include <sound/pcm_params.h> @@ -55,6 +77,13 @@  #define GPIO_DS_OUTPUT_FRONTLR	0x0080  #define GPIO_DS_OUTPUT_ENABLE	0x0100 +#define GPIO_SLIM_HDMI_DISABLE	0x0001 +#define GPIO_SLIM_OUTPUT_ENABLE	0x0002 +#define GPIO_SLIM_FIRMWARE_CLK	0x0040 +#define GPIO_SLIM_FIRMWARE_DATA	0x0080 + +#define I2C_DEVICE_WM8776	0x34	/* 001101, 0, /W=0 */ +  #define LC_CONTROL_LIMITER	0x40000000  #define LC_CONTROL_ALC		0x20000000 @@ -66,19 +95,37 @@ struct xonar_wm87x6 {  	struct snd_kcontrol *mic_adcmux_control;  	struct snd_kcontrol *lc_controls[13];  	struct snd_jack *hp_jack; +	struct xonar_hdmi hdmi;  }; -static void wm8776_write(struct oxygen *chip, -			 unsigned int reg, unsigned int value) +static void wm8776_write_spi(struct oxygen *chip, +			     unsigned int reg, unsigned int value)  { -	struct xonar_wm87x6 *data = chip->model_data; -  	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |  			 OXYGEN_SPI_DATA_LENGTH_2 |  			 OXYGEN_SPI_CLOCK_160 |  			 (1 << OXYGEN_SPI_CODEC_SHIFT) |  			 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,  			 (reg << 9) | value); +} + +static void wm8776_write_i2c(struct oxygen *chip, +			     unsigned int reg, unsigned int value) +{ +	oxygen_write_i2c(chip, I2C_DEVICE_WM8776, +			 (reg << 1) | (value >> 8), value); +} + +static void wm8776_write(struct oxygen *chip, +			 unsigned int reg, unsigned int value) +{ +	struct xonar_wm87x6 *data = chip->model_data; + +	if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) == +	    OXYGEN_FUNCTION_SPI) +		wm8776_write_spi(chip, reg, value); +	else +		wm8776_write_i2c(chip, reg, value);  	if (reg < ARRAY_SIZE(data->wm8776_regs)) {  		if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)  			value &= ~WM8776_UPDATE; @@ -130,6 +177,7 @@ static void wm8776_registers_init(struct oxygen *chip)  	struct xonar_wm87x6 *data = chip->model_data;  	wm8776_write(chip, WM8776_RESET, 0); +	wm8776_write(chip, WM8776_PHASESWAP, WM8776_PH_MASK);  	wm8776_write(chip, WM8776_DACCTRL1, WM8776_DZCEN |  		     WM8776_PL_LEFT_LEFT | WM8776_PL_RIGHT_RIGHT);  	wm8776_write(chip, WM8776_DACMUTE, chip->dac_mute ? WM8776_DMUTE : 0); @@ -245,17 +293,50 @@ static void xonar_ds_init(struct oxygen *chip)  	snd_component_add(chip->card, "WM8766");  } +static void xonar_hdav_slim_init(struct oxygen *chip) +{ +	struct xonar_wm87x6 *data = chip->model_data; + +	data->generic.anti_pop_delay = 300; +	data->generic.output_enable_bit = GPIO_SLIM_OUTPUT_ENABLE; + +	wm8776_init(chip); + +	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, +			  GPIO_SLIM_HDMI_DISABLE | +			  GPIO_SLIM_FIRMWARE_CLK | +			  GPIO_SLIM_FIRMWARE_DATA); + +	xonar_hdmi_init(chip, &data->hdmi); +	xonar_enable_output(chip); + +	snd_component_add(chip->card, "WM8776"); +} +  static void xonar_ds_cleanup(struct oxygen *chip)  {  	xonar_disable_output(chip);  	wm8776_write(chip, WM8776_RESET, 0);  } +static void xonar_hdav_slim_cleanup(struct oxygen *chip) +{ +	xonar_hdmi_cleanup(chip); +	xonar_disable_output(chip); +	wm8776_write(chip, WM8776_RESET, 0); +	msleep(2); +} +  static void xonar_ds_suspend(struct oxygen *chip)  {  	xonar_ds_cleanup(chip);  } +static void xonar_hdav_slim_suspend(struct oxygen *chip) +{ +	xonar_hdav_slim_cleanup(chip); +} +  static void xonar_ds_resume(struct oxygen *chip)  {  	wm8776_registers_init(chip); @@ -264,6 +345,15 @@ static void xonar_ds_resume(struct oxygen *chip)  	xonar_ds_handle_hp_jack(chip);  } +static void xonar_hdav_slim_resume(struct oxygen *chip) +{ +	struct xonar_wm87x6 *data = chip->model_data; + +	wm8776_registers_init(chip); +	xonar_hdmi_resume(chip, &data->hdmi); +	xonar_enable_output(chip); +} +  static void wm8776_adc_hardware_filter(unsigned int channel,  				       struct snd_pcm_hardware *hardware)  { @@ -278,6 +368,13 @@ static void wm8776_adc_hardware_filter(unsigned int channel,  	}  } +static void xonar_hdav_slim_hardware_filter(unsigned int channel, +					    struct snd_pcm_hardware *hardware) +{ +	wm8776_adc_hardware_filter(channel, hardware); +	xonar_hdmi_pcm_hardware_filter(channel, hardware); +} +  static void set_wm87x6_dac_params(struct oxygen *chip,  				  struct snd_pcm_hw_params *params)  { @@ -294,6 +391,14 @@ static void set_wm8776_adc_params(struct oxygen *chip,  	wm8776_write_cached(chip, WM8776_MSTRCTRL, reg);  } +static void set_hdav_slim_dac_params(struct oxygen *chip, +				     struct snd_pcm_hw_params *params) +{ +	struct xonar_wm87x6 *data = chip->model_data; + +	xonar_set_hdmi_params(chip, &data->hdmi, params); +} +  static void update_wm8776_volume(struct oxygen *chip)  {  	struct xonar_wm87x6 *data = chip->model_data; @@ -473,11 +578,6 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,  	const char *const *names;  	max = (ctl->private_value >> 12) & 0xf; -	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	info->count = 1; -	info->value.enumerated.items = max + 1; -	if (info->value.enumerated.item > max) -		info->value.enumerated.item = max;  	switch ((ctl->private_value >> 24) & 0x1f) {  	case WM8776_ALCCTRL2:  		names = hld; @@ -501,8 +601,7 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,  	default:  		return -ENXIO;  	} -	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); -	return 0; +	return snd_ctl_enum_info(info, 1, max + 1, names);  }  static int wm8776_field_volume_info(struct snd_kcontrol *ctl, @@ -759,13 +858,8 @@ static int wm8776_level_control_info(struct snd_kcontrol *ctl,  	static const char *const names[3] = {  		"None", "Peak Limiter", "Automatic Level Control"  	}; -	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; + +	return snd_ctl_enum_info(info, 1, 3, names);  }  static int wm8776_level_control_get(struct snd_kcontrol *ctl, @@ -851,13 +945,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)  		"None", "High-pass Filter"  	}; -	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	info->count = 1; -	info->value.enumerated.items = 2; -	if (info->value.enumerated.item >= 2) -		info->value.enumerated.item = 1; -	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); -	return 0; +	return snd_ctl_enum_info(info, 1, 2, names);  }  static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) @@ -985,6 +1073,53 @@ static const struct snd_kcontrol_new ds_controls[] = {  		.private_value = 0,  	},  }; +static const struct snd_kcontrol_new hdav_slim_controls[] = { +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "HDMI Playback Switch", +		.info = snd_ctl_boolean_mono_info, +		.get = xonar_gpio_bit_switch_get, +		.put = xonar_gpio_bit_switch_put, +		.private_value = GPIO_SLIM_HDMI_DISABLE | XONAR_GPIO_BIT_INVERT, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Headphone Playback Volume", +		.info = wm8776_hp_vol_info, +		.get = wm8776_hp_vol_get, +		.put = wm8776_hp_vol_put, +		.tlv = { .p = wm8776_hp_db_scale }, +	}, +	WM8776_BIT_SWITCH("Headphone Playback Switch", +			  WM8776_PWRDOWN, WM8776_HPPD, 1, 0), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Input Capture Volume", +		.info = wm8776_input_vol_info, +		.get = wm8776_input_vol_get, +		.put = wm8776_input_vol_put, +		.tlv = { .p = wm8776_adc_db_scale }, +	}, +	WM8776_BIT_SWITCH("Mic Capture Switch", +			  WM8776_ADCMUX, 1 << 0, 0, 0), +	WM8776_BIT_SWITCH("Aux Capture Switch", +			  WM8776_ADCMUX, 1 << 1, 0, 0), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "ADC Filter Capture Enum", +		.info = hpf_info, +		.get = hpf_get, +		.put = hpf_put, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Level Control Capture Enum", +		.info = wm8776_level_control_info, +		.get = wm8776_level_control_get, +		.put = wm8776_level_control_put, +		.private_value = 0, +	}, +};  static const struct snd_kcontrol_new lc_controls[] = {  	WM8776_FIELD_CTL_VOLUME("Limiter Threshold",  				WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf, @@ -1028,6 +1163,26 @@ static const struct snd_kcontrol_new lc_controls[] = {  				LC_CONTROL_ALC, wm8776_ngth_db_scale),  }; +static int add_lc_controls(struct oxygen *chip) +{ +	struct xonar_wm87x6 *data = chip->model_data; +	unsigned int i; +	struct snd_kcontrol *ctl; +	int err; + +	BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls)); +	for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) { +		ctl = snd_ctl_new1(&lc_controls[i], chip); +		if (!ctl) +			return -ENOMEM; +		err = snd_ctl_add(chip->card, ctl); +		if (err < 0) +			return err; +		data->lc_controls[i] = ctl; +	} +	return 0; +} +  static int xonar_ds_mixer_init(struct oxygen *chip)  {  	struct xonar_wm87x6 *data = chip->model_data; @@ -1049,21 +1204,57 @@ static int xonar_ds_mixer_init(struct oxygen *chip)  	}  	if (!data->line_adcmux_control || !data->mic_adcmux_control)  		return -ENXIO; -	BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls)); -	for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) { -		ctl = snd_ctl_new1(&lc_controls[i], chip); + +	return add_lc_controls(chip); +} + +static int xonar_hdav_slim_mixer_init(struct oxygen *chip) +{ +	unsigned int i; +	struct snd_kcontrol *ctl; +	int err; + +	for (i = 0; i < ARRAY_SIZE(hdav_slim_controls); ++i) { +		ctl = snd_ctl_new1(&hdav_slim_controls[i], chip);  		if (!ctl)  			return -ENOMEM;  		err = snd_ctl_add(chip->card, ctl);  		if (err < 0)  			return err; -		data->lc_controls[i] = ctl;  	} -	return 0; + +	return add_lc_controls(chip); +} + +static void dump_wm8776_registers(struct oxygen *chip, +				  struct snd_info_buffer *buffer) +{ +	struct xonar_wm87x6 *data = chip->model_data; +	unsigned int i; + +	snd_iprintf(buffer, "\nWM8776:\n00:"); +	for (i = 0; i < 0x10; ++i) +		snd_iprintf(buffer, " %03x", data->wm8776_regs[i]); +	snd_iprintf(buffer, "\n10:"); +	for (i = 0x10; i < 0x17; ++i) +		snd_iprintf(buffer, " %03x", data->wm8776_regs[i]); +	snd_iprintf(buffer, "\n"); +} + +static void dump_wm87x6_registers(struct oxygen *chip, +				  struct snd_info_buffer *buffer) +{ +	struct xonar_wm87x6 *data = chip->model_data; +	unsigned int i; + +	dump_wm8776_registers(chip, buffer); +	snd_iprintf(buffer, "\nWM8766:\n00:"); +	for (i = 0; i < 0x10; ++i) +		snd_iprintf(buffer, " %03x", data->wm8766_regs[i]); +	snd_iprintf(buffer, "\n");  }  static const struct oxygen_model model_xonar_ds = { -	.shortname = "Xonar DS",  	.longname = "Asus Virtuoso 66",  	.chip = "AV200",  	.init = xonar_ds_init, @@ -1072,32 +1263,77 @@ static const struct oxygen_model model_xonar_ds = {  	.suspend = xonar_ds_suspend,  	.resume = xonar_ds_resume,  	.pcm_hardware_filter = wm8776_adc_hardware_filter, -	.get_i2s_mclk = oxygen_default_i2s_mclk,  	.set_dac_params = set_wm87x6_dac_params,  	.set_adc_params = set_wm8776_adc_params,  	.update_dac_volume = update_wm87x6_volume,  	.update_dac_mute = update_wm87x6_mute,  	.update_center_lfe_mix = update_wm8766_center_lfe_mix,  	.gpio_changed = xonar_ds_gpio_changed, +	.dump_registers = dump_wm87x6_registers,  	.dac_tlv = wm87x6_dac_db_scale,  	.model_data_size = sizeof(struct xonar_wm87x6),  	.device_config = PLAYBACK_0_TO_I2S |  			 PLAYBACK_1_TO_SPDIF | -			 CAPTURE_0_FROM_I2S_1, -	.dac_channels = 8, +			 CAPTURE_0_FROM_I2S_1 | +			 CAPTURE_1_FROM_SPDIF, +	.dac_channels_pcm = 8, +	.dac_channels_mixer = 8,  	.dac_volume_min = 255 - 2*60,  	.dac_volume_max = 255,  	.function_flags = OXYGEN_FUNCTION_SPI, +	.dac_mclks = OXYGEN_MCLKS(256, 256, 128), +	.adc_mclks = OXYGEN_MCLKS(256, 256, 128),  	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,  	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,  }; -int __devinit get_xonar_wm87x6_model(struct oxygen *chip, -				     const struct pci_device_id *id) +static const struct oxygen_model model_xonar_hdav_slim = { +	.shortname = "Xonar HDAV1.3 Slim", +	.longname = "Asus Virtuoso 200", +	.chip = "AV200", +	.init = xonar_hdav_slim_init, +	.mixer_init = xonar_hdav_slim_mixer_init, +	.cleanup = xonar_hdav_slim_cleanup, +	.suspend = xonar_hdav_slim_suspend, +	.resume = xonar_hdav_slim_resume, +	.pcm_hardware_filter = xonar_hdav_slim_hardware_filter, +	.set_dac_params = set_hdav_slim_dac_params, +	.set_adc_params = set_wm8776_adc_params, +	.update_dac_volume = update_wm8776_volume, +	.update_dac_mute = update_wm8776_mute, +	.uart_input = xonar_hdmi_uart_input, +	.dump_registers = dump_wm8776_registers, +	.dac_tlv = wm87x6_dac_db_scale, +	.model_data_size = sizeof(struct xonar_wm87x6), +	.device_config = PLAYBACK_0_TO_I2S | +			 PLAYBACK_1_TO_SPDIF | +			 CAPTURE_0_FROM_I2S_1 | +			 CAPTURE_1_FROM_SPDIF, +	.dac_channels_pcm = 8, +	.dac_channels_mixer = 2, +	.dac_volume_min = 255 - 2*60, +	.dac_volume_max = 255, +	.function_flags = OXYGEN_FUNCTION_2WIRE, +	.dac_mclks = OXYGEN_MCLKS(256, 256, 128), +	.adc_mclks = OXYGEN_MCLKS(256, 256, 128), +	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +int get_xonar_wm87x6_model(struct oxygen *chip, +			   const struct pci_device_id *id)  {  	switch (id->subdevice) {  	case 0x838e:  		chip->model = model_xonar_ds; +		chip->model.shortname = "Xonar DS"; +		break; +	case 0x8522: +		chip->model = model_xonar_ds; +		chip->model.shortname = "Xonar DSX"; +		break; +	case 0x835e: +		chip->model = model_xonar_hdav_slim;  		break;  	default:  		return -EINVAL;  | 
