diff options
Diffstat (limited to 'sound/pci/oxygen')
| -rw-r--r-- | sound/pci/oxygen/Makefile | 2 | ||||
| -rw-r--r-- | sound/pci/oxygen/cs4245.h | 7 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen.h | 2 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen_io.c | 31 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen_lib.c | 14 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen_mixer.c | 1 | ||||
| -rw-r--r-- | sound/pci/oxygen/oxygen_regs.h | 1 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_dg.c | 625 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_dg.h | 48 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_dg_mixer.c | 477 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_hdmi.c | 2 | ||||
| -rw-r--r-- | sound/pci/oxygen/xonar_lib.c | 4 | 
12 files changed, 722 insertions, 492 deletions
diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile index 0f8726551fd..8f4c409f7e4 100644 --- a/sound/pci/oxygen/Makefile +++ b/sound/pci/oxygen/Makefile @@ -1,5 +1,5 @@  snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o -snd-oxygen-objs := oxygen.o xonar_dg.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 diff --git a/sound/pci/oxygen/cs4245.h b/sound/pci/oxygen/cs4245.h index 5e0197e07dd..99098657695 100644 --- a/sound/pci/oxygen/cs4245.h +++ b/sound/pci/oxygen/cs4245.h @@ -102,6 +102,9 @@  #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 << 16) -#define CS4245_SPI_WRITE	(0 << 16) +#define CS4245_SPI_ADDRESS	0x9e +#define CS4245_SPI_WRITE	0 +#define CS4245_SPI_READ		1 diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index 09a24b24958..c10ab077afd 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -198,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 521eae45834..4b8a32c37e3 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -147,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); @@ -179,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); @@ -194,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 30.72 us (24 * 1.28 us) */ -	count = 10; -	while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) -	       && count > 0) { +	/* +	 * Higher timeout to be sure: 200 us; +	 * actual transaction should not need more than 40 us. +	 */ +	for (count = 50; count > 0; count--) {  		udelay(4); -		--count; +		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); @@ -275,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 b0cb48adddc..b67e3060247 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -313,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");  	}  } @@ -595,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; @@ -616,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;  	} @@ -648,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); @@ -658,7 +658,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  	err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,  			  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; @@ -796,7 +796,7 @@ static int oxygen_pci_resume(struct device *dev)  	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;  	} diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index c0dbb52d45b..5988e044c51 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -190,6 +190,7 @@ 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)  { diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h index 63dc7a0ab55..8c191badaae 100644 --- a/sound/pci/oxygen/oxygen_regs.h +++ b/sound/pci/oxygen/oxygen_regs.h @@ -318,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/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c index 77acd790ea4..4cf3200e988 100644 --- a/sound/pci/oxygen/xonar_dg.c +++ b/sound/pci/oxygen/xonar_dg.c @@ -2,7 +2,7 @@   * 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. @@ -20,27 +20,35 @@   * 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 -> route input jack to line-in (0) or mic-in (1) - *   GPIO 6 -> route input jack to line-in (0) or mic-in (1) - *   GPIO 7 -> enable rear headphone amp + *   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/mic + *   input 4 <- line   *   DAC out -> headphones   *   aux out -> front panel headphones   */ @@ -56,161 +64,178 @@  #include "xonar_dg.h"  #include "cs4245.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 - -struct dg { -	unsigned int output_sel; -	s8 input_vol[4][2]; -	unsigned int input_sel; -	u8 hp_vol_att; -	u8 cs4245_regs[0x11]; -}; - -static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value) +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]; -	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, -			 CS4245_SPI_ADDRESS | -			 CS4245_SPI_WRITE | -			 (reg << 8) | value); -	data->cs4245_regs[reg] = value; +	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);  } -static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value) +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); -	if (value != data->cs4245_regs[reg]) -		cs4245_write(chip, reg, value); +	return 0;  } -static void cs4245_registers_init(struct oxygen *chip) +int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)  {  	struct dg *data = chip->model_data; - -	cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN); -	cs4245_write(chip, CS4245_DAC_CTRL_1, -		     data->cs4245_regs[CS4245_DAC_CTRL_1]); -	cs4245_write(chip, CS4245_ADC_CTRL, -		     data->cs4245_regs[CS4245_ADC_CTRL]); -	cs4245_write(chip, CS4245_SIGNAL_SEL, -		     data->cs4245_regs[CS4245_SIGNAL_SEL]); -	cs4245_write(chip, CS4245_PGA_B_CTRL, -		     data->cs4245_regs[CS4245_PGA_B_CTRL]); -	cs4245_write(chip, CS4245_PGA_A_CTRL, -		     data->cs4245_regs[CS4245_PGA_A_CTRL]); -	cs4245_write(chip, CS4245_ANALOG_IN, -		     data->cs4245_regs[CS4245_ANALOG_IN]); -	cs4245_write(chip, CS4245_DAC_A_CTRL, -		     data->cs4245_regs[CS4245_DAC_A_CTRL]); -	cs4245_write(chip, CS4245_DAC_B_CTRL, -		     data->cs4245_regs[CS4245_DAC_B_CTRL]); -	cs4245_write(chip, CS4245_DAC_CTRL_2, -		     CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC); -	cs4245_write(chip, CS4245_INT_MASK, 0); -	cs4245_write(chip, CS4245_POWER_CTRL, 0); +	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; -	data->cs4245_regs[CS4245_DAC_CTRL_1] = +	/* 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_regs[CS4245_ADC_CTRL] = +	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_regs[CS4245_SIGNAL_SEL] = -		CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH; -	data->cs4245_regs[CS4245_PGA_B_CTRL] = 0; -	data->cs4245_regs[CS4245_PGA_A_CTRL] = 0; -	data->cs4245_regs[CS4245_ANALOG_IN] = -		CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4; -	data->cs4245_regs[CS4245_DAC_A_CTRL] = 0; -	data->cs4245_regs[CS4245_DAC_B_CTRL] = 0; -	cs4245_registers_init(chip); +	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");  } -static void dg_output_enable(struct oxygen *chip) -{ -	msleep(2500); -	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); -} - -static void dg_init(struct oxygen *chip) +void dg_init(struct oxygen *chip)  {  	struct dg *data = chip->model_data; -	data->output_sel = 0; -	data->input_sel = 3; -	data->hp_vol_att = 2 * 16; +	data->output_sel = PLAYBACK_DST_HP_FP; +	data->input_sel = CAPTURE_SRC_MIC;  	cs4245_init(chip); - -	oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, -			    GPIO_MAGIC | GPIO_HP_DETECT); -	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, -			  GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE); -	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, -			    GPIO_INPUT_ROUTE | GPIO_HP_REAR); -	dg_output_enable(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);  } -static void dg_cleanup(struct oxygen *chip) +void dg_cleanup(struct oxygen *chip)  {  	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);  } -static void dg_suspend(struct oxygen *chip) +void dg_suspend(struct oxygen *chip)  {  	dg_cleanup(chip);  } -static void dg_resume(struct oxygen *chip) +void dg_resume(struct oxygen *chip)  { -	cs4245_registers_init(chip); -	dg_output_enable(chip); +	cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW); +	msleep(2500); +	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);  } -static void set_cs4245_dac_params(struct oxygen *chip, +void set_cs4245_dac_params(struct oxygen *chip,  				  struct snd_pcm_hw_params *params)  {  	struct dg *data = chip->model_data; -	u8 value; - -	value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK; -	if (params_rate(params) <= 50000) -		value |= CS4245_DAC_FM_SINGLE; -	else if (params_rate(params) <= 100000) -		value |= CS4245_DAC_FM_DOUBLE; -	else -		value |= CS4245_DAC_FM_QUAD; -	cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value); +	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);  } -static void set_cs4245_adc_params(struct oxygen *chip, +void set_cs4245_adc_params(struct oxygen *chip,  				  struct snd_pcm_hw_params *params)  {  	struct dg *data = chip->model_data; -	u8 value; - -	value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK; -	if (params_rate(params) <= 50000) -		value |= CS4245_ADC_FM_SINGLE; -	else if (params_rate(params) <= 100000) -		value |= CS4245_ADC_FM_DOUBLE; -	else -		value |= CS4245_ADC_FM_QUAD; -	cs4245_write_cached(chip, CS4245_ADC_CTRL, value); +	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, @@ -224,9 +249,23 @@ static inline unsigned int shift_bits(unsigned int value,  		return (value >> (shift_from - shift_to)) & mask;  } -static unsigned int adjust_dg_dac_routing(struct oxygen *chip, +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, @@ -242,367 +281,15 @@ static unsigned int adjust_dg_dac_routing(struct oxygen *chip,  			  OXYGEN_PLAY_DAC3_SOURCE_MASK);  } -static int output_switch_info(struct snd_kcontrol *ctl, -			      struct snd_ctl_elem_info *info) -{ -	static const char *const names[3] = { -		"Speakers", "Headphones", "FP Headphones" -	}; - -	return snd_ctl_enum_info(info, 1, 3, names); -} - -static int output_switch_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_switch_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; - -	if (value->value.enumerated.item[0] > 2) -		return -EINVAL; - -	mutex_lock(&chip->mutex); -	changed = value->value.enumerated.item[0] != data->output_sel; -	if (changed) { -		data->output_sel = value->value.enumerated.item[0]; - -		reg = data->cs4245_regs[CS4245_SIGNAL_SEL] & -						~CS4245_A_OUT_SEL_MASK; -		reg |= data->output_sel == 2 ? -				CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; -		cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg); - -		cs4245_write_cached(chip, CS4245_DAC_A_CTRL, -				    data->output_sel ? data->hp_vol_att : 0); -		cs4245_write_cached(chip, CS4245_DAC_B_CTRL, -				    data->output_sel ? data->hp_vol_att : 0); - -		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, -				      data->output_sel == 1 ? GPIO_HP_REAR : 0, -				      GPIO_HP_REAR); -	} -	mutex_unlock(&chip->mutex); -	return changed; -} - -static int hp_volume_offset_info(struct snd_kcontrol *ctl, -				 struct snd_ctl_elem_info *info) -{ -	static const char *const names[3] = { -		"< 64 ohms", "64-150 ohms", "150-300 ohms" -	}; - -	return snd_ctl_enum_info(info, 1, 3, names); -} - -static int hp_volume_offset_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); -	if (data->hp_vol_att > 2 * 7) -		value->value.enumerated.item[0] = 0; -	else if (data->hp_vol_att > 0) -		value->value.enumerated.item[0] = 1; -	else -		value->value.enumerated.item[0] = 2; -	mutex_unlock(&chip->mutex); -	return 0; -} - -static int hp_volume_offset_put(struct snd_kcontrol *ctl, -				struct snd_ctl_elem_value *value) -{ -	static const s8 atts[3] = { 2 * 16, 2 * 7, 0 }; -	struct oxygen *chip = ctl->private_data; -	struct dg *data = chip->model_data; -	s8 att; -	int changed; - -	if (value->value.enumerated.item[0] > 2) -		return -EINVAL; -	att = atts[value->value.enumerated.item[0]]; -	mutex_lock(&chip->mutex); -	changed = att != data->hp_vol_att; -	if (changed) { -		data->hp_vol_att = att; -		if (data->output_sel) { -			cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); -			cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att); -		} -	} -	mutex_unlock(&chip->mutex); -	return changed; -} - -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; - -	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) { -			cs4245_write_cached(chip, CS4245_PGA_A_CTRL, -					    data->input_vol[idx][0]); -			cs4245_write_cached(chip, CS4245_PGA_B_CTRL, -					    data->input_vol[idx][1]); -		} -	} -	mutex_unlock(&chip->mutex); -	return changed; -} - -static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0); - -static int input_sel_info(struct snd_kcontrol *ctl, -			  struct snd_ctl_elem_info *info) -{ -	static const char *const names[4] = { -		"Mic", "Aux", "Front Mic", "Line" -	}; - -	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) -{ -	static const u8 sel_values[4] = { -		CS4245_SEL_MIC, -		CS4245_SEL_INPUT_1, -		CS4245_SEL_INPUT_2, -		CS4245_SEL_INPUT_4 -	}; -	struct oxygen *chip = ctl->private_data; -	struct dg *data = chip->model_data; -	int changed; - -	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]; - -		cs4245_write(chip, CS4245_ANALOG_IN, -			     (data->cs4245_regs[CS4245_ANALOG_IN] & -							~CS4245_SEL_MASK) | -			     sel_values[data->input_sel]); - -		cs4245_write_cached(chip, CS4245_PGA_A_CTRL, -				    data->input_vol[data->input_sel][0]); -		cs4245_write_cached(chip, CS4245_PGA_B_CTRL, -				    data->input_vol[data->input_sel][1]); - -		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, -				      data->input_sel ? 0 : GPIO_INPUT_ROUTE, -				      GPIO_INPUT_ROUTE); -	} -	mutex_unlock(&chip->mutex); -	return changed; -} - -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_regs[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_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; -	if (value->value.enumerated.item[0]) -		reg |= CS4245_HPF_FREEZE; -	changed = reg != data->cs4245_regs[CS4245_ADC_CTRL]; -	if (changed) -		cs4245_write(chip, CS4245_ADC_CTRL, reg); -	mutex_unlock(&chip->mutex); -	return changed; -} - -#define INPUT_VOLUME(xname, index) { \ -	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ -	.name = xname, \ -	.info = input_vol_info, \ -	.get = input_vol_get, \ -	.put = input_vol_put, \ -	.tlv = { .p = cs4245_pga_db_scale }, \ -	.private_value = index, \ -} -static const struct snd_kcontrol_new dg_controls[] = { -	{ -		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -		.name = "Analog Output Playback Enum", -		.info = output_switch_info, -		.get = output_switch_get, -		.put = output_switch_put, -	}, -	{ -		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -		.name = "Headphones Impedance Playback Enum", -		.info = hp_volume_offset_info, -		.get = hp_volume_offset_get, -		.put = hp_volume_offset_put, -	}, -	INPUT_VOLUME("Mic Capture Volume", 0), -	INPUT_VOLUME("Aux Capture Volume", 1), -	INPUT_VOLUME("Front Mic Capture Volume", 2), -	INPUT_VOLUME("Line Capture Volume", 3), -	{ -		.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; - -	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; -} - -static void dump_cs4245_registers(struct oxygen *chip, +void dump_cs4245_registers(struct oxygen *chip,  				  struct snd_info_buffer *buffer)  {  	struct dg *data = chip->model_data; -	unsigned int i; +	unsigned int addr;  	snd_iprintf(buffer, "\nCS4245:"); -	for (i = 1; i <= 0x10; ++i) -		snd_iprintf(buffer, " %02x", data->cs4245_regs[i]); +	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");  } - -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_2 | -			 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_dg.h b/sound/pci/oxygen/xonar_dg.h index 5688d78609a..d461df357aa 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -3,6 +3,54 @@  #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 136dac6a396..91d92bc32b7 100644 --- a/sound/pci/oxygen/xonar_hdmi.c +++ b/sound/pci/oxygen/xonar_hdmi.c @@ -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 0ebe7f5916f..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 */  		}  | 
