xref: /openbmc/linux/sound/pci/oxygen/xonar_dg.c (revision efbeb071)
166410bfdSClemens Ladisch /*
266410bfdSClemens Ladisch  * card driver for the Xonar DG
366410bfdSClemens Ladisch  *
466410bfdSClemens Ladisch  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
566410bfdSClemens Ladisch  *
666410bfdSClemens Ladisch  *
766410bfdSClemens Ladisch  *  This driver is free software; you can redistribute it and/or modify
866410bfdSClemens Ladisch  *  it under the terms of the GNU General Public License, version 2.
966410bfdSClemens Ladisch  *
1066410bfdSClemens Ladisch  *  This driver is distributed in the hope that it will be useful,
1166410bfdSClemens Ladisch  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1266410bfdSClemens Ladisch  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1366410bfdSClemens Ladisch  *  GNU General Public License for more details.
1466410bfdSClemens Ladisch  *
1566410bfdSClemens Ladisch  *  You should have received a copy of the GNU General Public License
1666410bfdSClemens Ladisch  *  along with this driver; if not, see <http://www.gnu.org/licenses/>.
1766410bfdSClemens Ladisch  */
1866410bfdSClemens Ladisch 
1966410bfdSClemens Ladisch /*
2066410bfdSClemens Ladisch  * Xonar DG
2166410bfdSClemens Ladisch  * --------
2266410bfdSClemens Ladisch  *
2366410bfdSClemens Ladisch  * CMI8788:
2466410bfdSClemens Ladisch  *
2566410bfdSClemens Ladisch  *   SPI 0 -> CS4245
2666410bfdSClemens Ladisch  *
27efbeb071SClemens Ladisch  *   I²S 1 -> CS4245
28efbeb071SClemens Ladisch  *   I²S 2 -> CS4361 (center/LFE)
29efbeb071SClemens Ladisch  *   I²S 3 -> CS4361 (surround)
30efbeb071SClemens Ladisch  *   I²S 4 -> CS4361 (front)
31efbeb071SClemens Ladisch  *
3266410bfdSClemens Ladisch  *   GPIO 3 <- ?
3366410bfdSClemens Ladisch  *   GPIO 4 <- headphone detect
3466410bfdSClemens Ladisch  *   GPIO 5 -> route input jack to line-in (0) or mic-in (1)
3566410bfdSClemens Ladisch  *   GPIO 6 -> route input jack to line-in (0) or mic-in (1)
3666410bfdSClemens Ladisch  *   GPIO 7 -> enable rear headphone amp
3766410bfdSClemens Ladisch  *   GPIO 8 -> enable output to speakers
3866410bfdSClemens Ladisch  *
3966410bfdSClemens Ladisch  * CS4245:
4066410bfdSClemens Ladisch  *
4166410bfdSClemens Ladisch  *   input 1 <- aux
4266410bfdSClemens Ladisch  *   input 2 <- front mic
4366410bfdSClemens Ladisch  *   input 4 <- line/mic
44efbeb071SClemens Ladisch  *   DAC out -> headphones
4566410bfdSClemens Ladisch  *   aux out -> front panel headphones
4666410bfdSClemens Ladisch  */
4766410bfdSClemens Ladisch 
4866410bfdSClemens Ladisch #include <linux/pci.h>
49e92d4575SStephen Rothwell #include <linux/delay.h>
5066410bfdSClemens Ladisch #include <sound/control.h>
5166410bfdSClemens Ladisch #include <sound/core.h>
5266410bfdSClemens Ladisch #include <sound/info.h>
5366410bfdSClemens Ladisch #include <sound/pcm.h>
5466410bfdSClemens Ladisch #include <sound/tlv.h>
5566410bfdSClemens Ladisch #include "oxygen.h"
5666410bfdSClemens Ladisch #include "xonar_dg.h"
5766410bfdSClemens Ladisch #include "cs4245.h"
5866410bfdSClemens Ladisch 
5966410bfdSClemens Ladisch #define GPIO_MAGIC		0x0008
6066410bfdSClemens Ladisch #define GPIO_HP_DETECT		0x0010
6166410bfdSClemens Ladisch #define GPIO_INPUT_ROUTE	0x0060
6266410bfdSClemens Ladisch #define GPIO_HP_REAR		0x0080
6366410bfdSClemens Ladisch #define GPIO_OUTPUT_ENABLE	0x0100
6466410bfdSClemens Ladisch 
6566410bfdSClemens Ladisch struct dg {
6666410bfdSClemens Ladisch 	unsigned int output_sel;
6766410bfdSClemens Ladisch 	s8 input_vol[4][2];
6866410bfdSClemens Ladisch 	unsigned int input_sel;
6966410bfdSClemens Ladisch 	u8 hp_vol_att;
7066410bfdSClemens Ladisch 	u8 cs4245_regs[0x11];
7166410bfdSClemens Ladisch };
7266410bfdSClemens Ladisch 
7366410bfdSClemens Ladisch static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value)
7466410bfdSClemens Ladisch {
7566410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
7666410bfdSClemens Ladisch 
7766410bfdSClemens Ladisch 	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
7866410bfdSClemens Ladisch 			 OXYGEN_SPI_DATA_LENGTH_3 |
7966410bfdSClemens Ladisch 			 OXYGEN_SPI_CLOCK_1280 |
8066410bfdSClemens Ladisch 			 (0 << OXYGEN_SPI_CODEC_SHIFT) |
8166410bfdSClemens Ladisch 			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
8266410bfdSClemens Ladisch 			 CS4245_SPI_ADDRESS |
8366410bfdSClemens Ladisch 			 CS4245_SPI_WRITE |
84f8fe80e4SClemens Ladisch 			 (reg << 8) | value);
8566410bfdSClemens Ladisch 	data->cs4245_regs[reg] = value;
8666410bfdSClemens Ladisch }
8766410bfdSClemens Ladisch 
8866410bfdSClemens Ladisch static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value)
8966410bfdSClemens Ladisch {
9066410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
9166410bfdSClemens Ladisch 
9266410bfdSClemens Ladisch 	if (value != data->cs4245_regs[reg])
9366410bfdSClemens Ladisch 		cs4245_write(chip, reg, value);
9466410bfdSClemens Ladisch }
9566410bfdSClemens Ladisch 
9666410bfdSClemens Ladisch static void cs4245_registers_init(struct oxygen *chip)
9766410bfdSClemens Ladisch {
9866410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
9966410bfdSClemens Ladisch 
10066410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN);
10166410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_DAC_CTRL_1,
10266410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_DAC_CTRL_1]);
10366410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_ADC_CTRL,
10466410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_ADC_CTRL]);
10566410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_SIGNAL_SEL,
10666410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_SIGNAL_SEL]);
10766410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_PGA_B_CTRL,
10866410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_PGA_B_CTRL]);
10966410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_PGA_A_CTRL,
11066410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_PGA_A_CTRL]);
11166410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_ANALOG_IN,
11266410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_ANALOG_IN]);
11366410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_DAC_A_CTRL,
11466410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_DAC_A_CTRL]);
11566410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_DAC_B_CTRL,
11666410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_DAC_B_CTRL]);
11766410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_DAC_CTRL_2,
11866410bfdSClemens Ladisch 		     CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC);
11966410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_INT_MASK, 0);
12066410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_POWER_CTRL, 0);
12166410bfdSClemens Ladisch }
12266410bfdSClemens Ladisch 
12366410bfdSClemens Ladisch static void cs4245_init(struct oxygen *chip)
12466410bfdSClemens Ladisch {
12566410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
12666410bfdSClemens Ladisch 
12766410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_DAC_CTRL_1] =
12866410bfdSClemens Ladisch 		CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
12966410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_ADC_CTRL] =
13066410bfdSClemens Ladisch 		CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
13166410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_SIGNAL_SEL] =
13266410bfdSClemens Ladisch 		CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH;
13366410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_PGA_B_CTRL] = 0;
13466410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_PGA_A_CTRL] = 0;
13566410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_ANALOG_IN] =
13666410bfdSClemens Ladisch 		CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4;
13766410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_DAC_A_CTRL] = 0;
13866410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_DAC_B_CTRL] = 0;
13966410bfdSClemens Ladisch 	cs4245_registers_init(chip);
14066410bfdSClemens Ladisch 	snd_component_add(chip->card, "CS4245");
14166410bfdSClemens Ladisch }
14266410bfdSClemens Ladisch 
14366410bfdSClemens Ladisch static void dg_output_enable(struct oxygen *chip)
14466410bfdSClemens Ladisch {
14566410bfdSClemens Ladisch 	msleep(2500);
14666410bfdSClemens Ladisch 	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
14766410bfdSClemens Ladisch }
14866410bfdSClemens Ladisch 
14966410bfdSClemens Ladisch static void dg_init(struct oxygen *chip)
15066410bfdSClemens Ladisch {
15166410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
15266410bfdSClemens Ladisch 
15366410bfdSClemens Ladisch 	data->output_sel = 0;
15466410bfdSClemens Ladisch 	data->input_sel = 3;
15566410bfdSClemens Ladisch 	data->hp_vol_att = 2 * 16;
15666410bfdSClemens Ladisch 
15766410bfdSClemens Ladisch 	cs4245_init(chip);
15866410bfdSClemens Ladisch 
15966410bfdSClemens Ladisch 	oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
16066410bfdSClemens Ladisch 			    GPIO_MAGIC | GPIO_HP_DETECT);
16166410bfdSClemens Ladisch 	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
16266410bfdSClemens Ladisch 			  GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE);
16366410bfdSClemens Ladisch 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
16466410bfdSClemens Ladisch 			    GPIO_INPUT_ROUTE | GPIO_HP_REAR);
16566410bfdSClemens Ladisch 	dg_output_enable(chip);
16666410bfdSClemens Ladisch }
16766410bfdSClemens Ladisch 
16866410bfdSClemens Ladisch static void dg_cleanup(struct oxygen *chip)
16966410bfdSClemens Ladisch {
17066410bfdSClemens Ladisch 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
17166410bfdSClemens Ladisch }
17266410bfdSClemens Ladisch 
17366410bfdSClemens Ladisch static void dg_suspend(struct oxygen *chip)
17466410bfdSClemens Ladisch {
17566410bfdSClemens Ladisch 	dg_cleanup(chip);
17666410bfdSClemens Ladisch }
17766410bfdSClemens Ladisch 
17866410bfdSClemens Ladisch static void dg_resume(struct oxygen *chip)
17966410bfdSClemens Ladisch {
18066410bfdSClemens Ladisch 	cs4245_registers_init(chip);
18166410bfdSClemens Ladisch 	dg_output_enable(chip);
18266410bfdSClemens Ladisch }
18366410bfdSClemens Ladisch 
18466410bfdSClemens Ladisch static void set_cs4245_dac_params(struct oxygen *chip,
18566410bfdSClemens Ladisch 				  struct snd_pcm_hw_params *params)
18666410bfdSClemens Ladisch {
18766410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
18866410bfdSClemens Ladisch 	u8 value;
18966410bfdSClemens Ladisch 
19066410bfdSClemens Ladisch 	value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
19166410bfdSClemens Ladisch 	if (params_rate(params) <= 50000)
19266410bfdSClemens Ladisch 		value |= CS4245_DAC_FM_SINGLE;
19366410bfdSClemens Ladisch 	else if (params_rate(params) <= 100000)
19466410bfdSClemens Ladisch 		value |= CS4245_DAC_FM_DOUBLE;
19566410bfdSClemens Ladisch 	else
19666410bfdSClemens Ladisch 		value |= CS4245_DAC_FM_QUAD;
19766410bfdSClemens Ladisch 	cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value);
19866410bfdSClemens Ladisch }
19966410bfdSClemens Ladisch 
20066410bfdSClemens Ladisch static void set_cs4245_adc_params(struct oxygen *chip,
20166410bfdSClemens Ladisch 				  struct snd_pcm_hw_params *params)
20266410bfdSClemens Ladisch {
20366410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
20466410bfdSClemens Ladisch 	u8 value;
20566410bfdSClemens Ladisch 
20666410bfdSClemens Ladisch 	value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
20766410bfdSClemens Ladisch 	if (params_rate(params) <= 50000)
20866410bfdSClemens Ladisch 		value |= CS4245_ADC_FM_SINGLE;
20966410bfdSClemens Ladisch 	else if (params_rate(params) <= 100000)
21066410bfdSClemens Ladisch 		value |= CS4245_ADC_FM_DOUBLE;
21166410bfdSClemens Ladisch 	else
21266410bfdSClemens Ladisch 		value |= CS4245_ADC_FM_QUAD;
21366410bfdSClemens Ladisch 	cs4245_write_cached(chip, CS4245_ADC_CTRL, value);
21466410bfdSClemens Ladisch }
21566410bfdSClemens Ladisch 
216efbeb071SClemens Ladisch static inline unsigned int shift_bits(unsigned int value,
217efbeb071SClemens Ladisch 				      unsigned int shift_from,
218efbeb071SClemens Ladisch 				      unsigned int shift_to,
219efbeb071SClemens Ladisch 				      unsigned int mask)
220efbeb071SClemens Ladisch {
221efbeb071SClemens Ladisch 	if (shift_from < shift_to)
222efbeb071SClemens Ladisch 		return (value << (shift_to - shift_from)) & mask;
223efbeb071SClemens Ladisch 	else
224efbeb071SClemens Ladisch 		return (value >> (shift_from - shift_to)) & mask;
225efbeb071SClemens Ladisch }
226efbeb071SClemens Ladisch 
227efbeb071SClemens Ladisch static unsigned int adjust_dg_dac_routing(struct oxygen *chip,
228efbeb071SClemens Ladisch 					  unsigned int play_routing)
229efbeb071SClemens Ladisch {
230efbeb071SClemens Ladisch 	return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
231efbeb071SClemens Ladisch 	       shift_bits(play_routing,
232efbeb071SClemens Ladisch 			  OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
233efbeb071SClemens Ladisch 			  OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
234efbeb071SClemens Ladisch 			  OXYGEN_PLAY_DAC1_SOURCE_MASK) |
235efbeb071SClemens Ladisch 	       shift_bits(play_routing,
236efbeb071SClemens Ladisch 			  OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
237efbeb071SClemens Ladisch 			  OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
238efbeb071SClemens Ladisch 			  OXYGEN_PLAY_DAC2_SOURCE_MASK) |
239efbeb071SClemens Ladisch 	       shift_bits(play_routing,
240efbeb071SClemens Ladisch 			  OXYGEN_PLAY_DAC0_SOURCE_SHIFT,
241efbeb071SClemens Ladisch 			  OXYGEN_PLAY_DAC3_SOURCE_SHIFT,
242efbeb071SClemens Ladisch 			  OXYGEN_PLAY_DAC3_SOURCE_MASK);
243efbeb071SClemens Ladisch }
244efbeb071SClemens Ladisch 
24566410bfdSClemens Ladisch static int output_switch_info(struct snd_kcontrol *ctl,
24666410bfdSClemens Ladisch 			      struct snd_ctl_elem_info *info)
24766410bfdSClemens Ladisch {
24866410bfdSClemens Ladisch 	static const char *const names[3] = {
24966410bfdSClemens Ladisch 		"Speakers", "Headphones", "FP Headphones"
25066410bfdSClemens Ladisch 	};
25166410bfdSClemens Ladisch 
2529600732bSClemens Ladisch 	return snd_ctl_enum_info(info, 1, 3, names);
25366410bfdSClemens Ladisch }
25466410bfdSClemens Ladisch 
25566410bfdSClemens Ladisch static int output_switch_get(struct snd_kcontrol *ctl,
25666410bfdSClemens Ladisch 			     struct snd_ctl_elem_value *value)
25766410bfdSClemens Ladisch {
25866410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
25966410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
26066410bfdSClemens Ladisch 
26166410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
26266410bfdSClemens Ladisch 	value->value.enumerated.item[0] = data->output_sel;
26366410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
26466410bfdSClemens Ladisch 	return 0;
26566410bfdSClemens Ladisch }
26666410bfdSClemens Ladisch 
26766410bfdSClemens Ladisch static int output_switch_put(struct snd_kcontrol *ctl,
26866410bfdSClemens Ladisch 			     struct snd_ctl_elem_value *value)
26966410bfdSClemens Ladisch {
27066410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
27166410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
27266410bfdSClemens Ladisch 	u8 reg;
27366410bfdSClemens Ladisch 	int changed;
27466410bfdSClemens Ladisch 
27566410bfdSClemens Ladisch 	if (value->value.enumerated.item[0] > 2)
27666410bfdSClemens Ladisch 		return -EINVAL;
27766410bfdSClemens Ladisch 
27866410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
27966410bfdSClemens Ladisch 	changed = value->value.enumerated.item[0] != data->output_sel;
28066410bfdSClemens Ladisch 	if (changed) {
28166410bfdSClemens Ladisch 		data->output_sel = value->value.enumerated.item[0];
28266410bfdSClemens Ladisch 
28366410bfdSClemens Ladisch 		reg = data->cs4245_regs[CS4245_SIGNAL_SEL] &
28466410bfdSClemens Ladisch 						~CS4245_A_OUT_SEL_MASK;
28566410bfdSClemens Ladisch 		reg |= data->output_sel == 2 ?
28666410bfdSClemens Ladisch 				CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ;
28766410bfdSClemens Ladisch 		cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg);
28866410bfdSClemens Ladisch 
28966410bfdSClemens Ladisch 		cs4245_write_cached(chip, CS4245_DAC_A_CTRL,
29066410bfdSClemens Ladisch 				    data->output_sel ? data->hp_vol_att : 0);
29166410bfdSClemens Ladisch 		cs4245_write_cached(chip, CS4245_DAC_B_CTRL,
29266410bfdSClemens Ladisch 				    data->output_sel ? data->hp_vol_att : 0);
29366410bfdSClemens Ladisch 
29466410bfdSClemens Ladisch 		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
29566410bfdSClemens Ladisch 				      data->output_sel == 1 ? GPIO_HP_REAR : 0,
29666410bfdSClemens Ladisch 				      GPIO_HP_REAR);
29766410bfdSClemens Ladisch 	}
29866410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
29966410bfdSClemens Ladisch 	return changed;
30066410bfdSClemens Ladisch }
30166410bfdSClemens Ladisch 
30266410bfdSClemens Ladisch static int hp_volume_offset_info(struct snd_kcontrol *ctl,
30366410bfdSClemens Ladisch 				 struct snd_ctl_elem_info *info)
30466410bfdSClemens Ladisch {
30566410bfdSClemens Ladisch 	static const char *const names[3] = {
30666410bfdSClemens Ladisch 		"< 64 ohms", "64-150 ohms", "150-300 ohms"
30766410bfdSClemens Ladisch 	};
30866410bfdSClemens Ladisch 
3099600732bSClemens Ladisch 	return snd_ctl_enum_info(info, 1, 3, names);
31066410bfdSClemens Ladisch }
31166410bfdSClemens Ladisch 
31266410bfdSClemens Ladisch static int hp_volume_offset_get(struct snd_kcontrol *ctl,
31366410bfdSClemens Ladisch 				struct snd_ctl_elem_value *value)
31466410bfdSClemens Ladisch {
31566410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
31666410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
31766410bfdSClemens Ladisch 
31866410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
31966410bfdSClemens Ladisch 	if (data->hp_vol_att > 2 * 7)
32066410bfdSClemens Ladisch 		value->value.enumerated.item[0] = 0;
32166410bfdSClemens Ladisch 	else if (data->hp_vol_att > 0)
32266410bfdSClemens Ladisch 		value->value.enumerated.item[0] = 1;
32366410bfdSClemens Ladisch 	else
32466410bfdSClemens Ladisch 		value->value.enumerated.item[0] = 2;
32566410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
32666410bfdSClemens Ladisch 	return 0;
32766410bfdSClemens Ladisch }
32866410bfdSClemens Ladisch 
32966410bfdSClemens Ladisch static int hp_volume_offset_put(struct snd_kcontrol *ctl,
33066410bfdSClemens Ladisch 				struct snd_ctl_elem_value *value)
33166410bfdSClemens Ladisch {
33266410bfdSClemens Ladisch 	static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
33366410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
33466410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
33566410bfdSClemens Ladisch 	s8 att;
33666410bfdSClemens Ladisch 	int changed;
33766410bfdSClemens Ladisch 
33866410bfdSClemens Ladisch 	if (value->value.enumerated.item[0] > 2)
33966410bfdSClemens Ladisch 		return -EINVAL;
34066410bfdSClemens Ladisch 	att = atts[value->value.enumerated.item[0]];
34166410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
34266410bfdSClemens Ladisch 	changed = att != data->hp_vol_att;
34366410bfdSClemens Ladisch 	if (changed) {
34466410bfdSClemens Ladisch 		data->hp_vol_att = att;
34566410bfdSClemens Ladisch 		if (data->output_sel) {
34666410bfdSClemens Ladisch 			cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
34766410bfdSClemens Ladisch 			cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
34866410bfdSClemens Ladisch 		}
34966410bfdSClemens Ladisch 	}
35066410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
35166410bfdSClemens Ladisch 	return changed;
35266410bfdSClemens Ladisch }
35366410bfdSClemens Ladisch 
35466410bfdSClemens Ladisch static int input_vol_info(struct snd_kcontrol *ctl,
35566410bfdSClemens Ladisch 			  struct snd_ctl_elem_info *info)
35666410bfdSClemens Ladisch {
35766410bfdSClemens Ladisch 	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
35866410bfdSClemens Ladisch 	info->count = 2;
35966410bfdSClemens Ladisch 	info->value.integer.min = 2 * -12;
36066410bfdSClemens Ladisch 	info->value.integer.max = 2 * 12;
36166410bfdSClemens Ladisch 	return 0;
36266410bfdSClemens Ladisch }
36366410bfdSClemens Ladisch 
36466410bfdSClemens Ladisch static int input_vol_get(struct snd_kcontrol *ctl,
36566410bfdSClemens Ladisch 			 struct snd_ctl_elem_value *value)
36666410bfdSClemens Ladisch {
36766410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
36866410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
36966410bfdSClemens Ladisch 	unsigned int idx = ctl->private_value;
37066410bfdSClemens Ladisch 
37166410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
37266410bfdSClemens Ladisch 	value->value.integer.value[0] = data->input_vol[idx][0];
37366410bfdSClemens Ladisch 	value->value.integer.value[1] = data->input_vol[idx][1];
37466410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
37566410bfdSClemens Ladisch 	return 0;
37666410bfdSClemens Ladisch }
37766410bfdSClemens Ladisch 
37866410bfdSClemens Ladisch static int input_vol_put(struct snd_kcontrol *ctl,
37966410bfdSClemens Ladisch 			 struct snd_ctl_elem_value *value)
38066410bfdSClemens Ladisch {
38166410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
38266410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
38366410bfdSClemens Ladisch 	unsigned int idx = ctl->private_value;
38466410bfdSClemens Ladisch 	int changed = 0;
38566410bfdSClemens Ladisch 
38666410bfdSClemens Ladisch 	if (value->value.integer.value[0] < 2 * -12 ||
38766410bfdSClemens Ladisch 	    value->value.integer.value[0] > 2 * 12 ||
38866410bfdSClemens Ladisch 	    value->value.integer.value[1] < 2 * -12 ||
38966410bfdSClemens Ladisch 	    value->value.integer.value[1] > 2 * 12)
39066410bfdSClemens Ladisch 		return -EINVAL;
39166410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
39266410bfdSClemens Ladisch 	changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
39366410bfdSClemens Ladisch 		  data->input_vol[idx][1] != value->value.integer.value[1];
39466410bfdSClemens Ladisch 	if (changed) {
39566410bfdSClemens Ladisch 		data->input_vol[idx][0] = value->value.integer.value[0];
39666410bfdSClemens Ladisch 		data->input_vol[idx][1] = value->value.integer.value[1];
39766410bfdSClemens Ladisch 		if (idx == data->input_sel) {
39866410bfdSClemens Ladisch 			cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
39966410bfdSClemens Ladisch 					    data->input_vol[idx][0]);
40066410bfdSClemens Ladisch 			cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
40166410bfdSClemens Ladisch 					    data->input_vol[idx][1]);
40266410bfdSClemens Ladisch 		}
40366410bfdSClemens Ladisch 	}
40466410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
40566410bfdSClemens Ladisch 	return changed;
40666410bfdSClemens Ladisch }
40766410bfdSClemens Ladisch 
40866410bfdSClemens Ladisch static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0);
40966410bfdSClemens Ladisch 
41066410bfdSClemens Ladisch static int input_sel_info(struct snd_kcontrol *ctl,
41166410bfdSClemens Ladisch 			  struct snd_ctl_elem_info *info)
41266410bfdSClemens Ladisch {
41366410bfdSClemens Ladisch 	static const char *const names[4] = {
41466410bfdSClemens Ladisch 		"Mic", "Aux", "Front Mic", "Line"
41566410bfdSClemens Ladisch 	};
41666410bfdSClemens Ladisch 
4179600732bSClemens Ladisch 	return snd_ctl_enum_info(info, 1, 4, names);
41866410bfdSClemens Ladisch }
41966410bfdSClemens Ladisch 
42066410bfdSClemens Ladisch static int input_sel_get(struct snd_kcontrol *ctl,
42166410bfdSClemens Ladisch 			 struct snd_ctl_elem_value *value)
42266410bfdSClemens Ladisch {
42366410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
42466410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
42566410bfdSClemens Ladisch 
42666410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
42766410bfdSClemens Ladisch 	value->value.enumerated.item[0] = data->input_sel;
42866410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
42966410bfdSClemens Ladisch 	return 0;
43066410bfdSClemens Ladisch }
43166410bfdSClemens Ladisch 
43266410bfdSClemens Ladisch static int input_sel_put(struct snd_kcontrol *ctl,
43366410bfdSClemens Ladisch 			 struct snd_ctl_elem_value *value)
43466410bfdSClemens Ladisch {
43566410bfdSClemens Ladisch 	static const u8 sel_values[4] = {
43666410bfdSClemens Ladisch 		CS4245_SEL_MIC,
43766410bfdSClemens Ladisch 		CS4245_SEL_INPUT_1,
43866410bfdSClemens Ladisch 		CS4245_SEL_INPUT_2,
43966410bfdSClemens Ladisch 		CS4245_SEL_INPUT_4
44066410bfdSClemens Ladisch 	};
44166410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
44266410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
44366410bfdSClemens Ladisch 	int changed;
44466410bfdSClemens Ladisch 
44566410bfdSClemens Ladisch 	if (value->value.enumerated.item[0] > 3)
44666410bfdSClemens Ladisch 		return -EINVAL;
44766410bfdSClemens Ladisch 
44866410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
44966410bfdSClemens Ladisch 	changed = value->value.enumerated.item[0] != data->input_sel;
45066410bfdSClemens Ladisch 	if (changed) {
45166410bfdSClemens Ladisch 		data->input_sel = value->value.enumerated.item[0];
45266410bfdSClemens Ladisch 
45366410bfdSClemens Ladisch 		cs4245_write(chip, CS4245_ANALOG_IN,
45466410bfdSClemens Ladisch 			     (data->cs4245_regs[CS4245_ANALOG_IN] &
45566410bfdSClemens Ladisch 							~CS4245_SEL_MASK) |
45666410bfdSClemens Ladisch 			     sel_values[data->input_sel]);
45766410bfdSClemens Ladisch 
45866410bfdSClemens Ladisch 		cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
45966410bfdSClemens Ladisch 				    data->input_vol[data->input_sel][0]);
46066410bfdSClemens Ladisch 		cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
46166410bfdSClemens Ladisch 				    data->input_vol[data->input_sel][1]);
46266410bfdSClemens Ladisch 
46366410bfdSClemens Ladisch 		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
46466410bfdSClemens Ladisch 				      data->input_sel ? 0 : GPIO_INPUT_ROUTE,
46566410bfdSClemens Ladisch 				      GPIO_INPUT_ROUTE);
46666410bfdSClemens Ladisch 	}
46766410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
46866410bfdSClemens Ladisch 	return changed;
46966410bfdSClemens Ladisch }
47066410bfdSClemens Ladisch 
47166410bfdSClemens Ladisch static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
47266410bfdSClemens Ladisch {
47366410bfdSClemens Ladisch 	static const char *const names[2] = { "Active", "Frozen" };
47466410bfdSClemens Ladisch 
4759600732bSClemens Ladisch 	return snd_ctl_enum_info(info, 1, 2, names);
47666410bfdSClemens Ladisch }
47766410bfdSClemens Ladisch 
47866410bfdSClemens Ladisch static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
47966410bfdSClemens Ladisch {
48066410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
48166410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
48266410bfdSClemens Ladisch 
48366410bfdSClemens Ladisch 	value->value.enumerated.item[0] =
48466410bfdSClemens Ladisch 		!!(data->cs4245_regs[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
48566410bfdSClemens Ladisch 	return 0;
48666410bfdSClemens Ladisch }
48766410bfdSClemens Ladisch 
48866410bfdSClemens Ladisch static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
48966410bfdSClemens Ladisch {
49066410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
49166410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
49266410bfdSClemens Ladisch 	u8 reg;
49366410bfdSClemens Ladisch 	int changed;
49466410bfdSClemens Ladisch 
49566410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
49666410bfdSClemens Ladisch 	reg = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
49766410bfdSClemens Ladisch 	if (value->value.enumerated.item[0])
49866410bfdSClemens Ladisch 		reg |= CS4245_HPF_FREEZE;
49966410bfdSClemens Ladisch 	changed = reg != data->cs4245_regs[CS4245_ADC_CTRL];
50066410bfdSClemens Ladisch 	if (changed)
50166410bfdSClemens Ladisch 		cs4245_write(chip, CS4245_ADC_CTRL, reg);
50266410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
50366410bfdSClemens Ladisch 	return changed;
50466410bfdSClemens Ladisch }
50566410bfdSClemens Ladisch 
50666410bfdSClemens Ladisch #define INPUT_VOLUME(xname, index) { \
50766410bfdSClemens Ladisch 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
50866410bfdSClemens Ladisch 	.name = xname, \
50966410bfdSClemens Ladisch 	.info = input_vol_info, \
51066410bfdSClemens Ladisch 	.get = input_vol_get, \
51166410bfdSClemens Ladisch 	.put = input_vol_put, \
51266410bfdSClemens Ladisch 	.tlv = { .p = cs4245_pga_db_scale }, \
51366410bfdSClemens Ladisch 	.private_value = index, \
51466410bfdSClemens Ladisch }
51566410bfdSClemens Ladisch static const struct snd_kcontrol_new dg_controls[] = {
51666410bfdSClemens Ladisch 	{
51766410bfdSClemens Ladisch 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
51866410bfdSClemens Ladisch 		.name = "Analog Output Playback Enum",
51966410bfdSClemens Ladisch 		.info = output_switch_info,
52066410bfdSClemens Ladisch 		.get = output_switch_get,
52166410bfdSClemens Ladisch 		.put = output_switch_put,
52266410bfdSClemens Ladisch 	},
52366410bfdSClemens Ladisch 	{
52466410bfdSClemens Ladisch 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
52566410bfdSClemens Ladisch 		.name = "Headphones Impedance Playback Enum",
52666410bfdSClemens Ladisch 		.info = hp_volume_offset_info,
52766410bfdSClemens Ladisch 		.get = hp_volume_offset_get,
52866410bfdSClemens Ladisch 		.put = hp_volume_offset_put,
52966410bfdSClemens Ladisch 	},
53066410bfdSClemens Ladisch 	INPUT_VOLUME("Mic Capture Volume", 0),
53166410bfdSClemens Ladisch 	INPUT_VOLUME("Aux Capture Volume", 1),
53266410bfdSClemens Ladisch 	INPUT_VOLUME("Front Mic Capture Volume", 2),
53366410bfdSClemens Ladisch 	INPUT_VOLUME("Line Capture Volume", 3),
53466410bfdSClemens Ladisch 	{
53566410bfdSClemens Ladisch 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
53666410bfdSClemens Ladisch 		.name = "Capture Source",
53766410bfdSClemens Ladisch 		.info = input_sel_info,
53866410bfdSClemens Ladisch 		.get = input_sel_get,
53966410bfdSClemens Ladisch 		.put = input_sel_put,
54066410bfdSClemens Ladisch 	},
54166410bfdSClemens Ladisch 	{
54266410bfdSClemens Ladisch 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
54366410bfdSClemens Ladisch 		.name = "ADC High-pass Filter Capture Enum",
54466410bfdSClemens Ladisch 		.info = hpf_info,
54566410bfdSClemens Ladisch 		.get = hpf_get,
54666410bfdSClemens Ladisch 		.put = hpf_put,
54766410bfdSClemens Ladisch 	},
54866410bfdSClemens Ladisch };
54966410bfdSClemens Ladisch 
55066410bfdSClemens Ladisch static int dg_control_filter(struct snd_kcontrol_new *template)
55166410bfdSClemens Ladisch {
55266410bfdSClemens Ladisch 	if (!strncmp(template->name, "Master Playback ", 16))
55366410bfdSClemens Ladisch 		return 1;
55466410bfdSClemens Ladisch 	return 0;
55566410bfdSClemens Ladisch }
55666410bfdSClemens Ladisch 
55766410bfdSClemens Ladisch static int dg_mixer_init(struct oxygen *chip)
55866410bfdSClemens Ladisch {
55966410bfdSClemens Ladisch 	unsigned int i;
56066410bfdSClemens Ladisch 	int err;
56166410bfdSClemens Ladisch 
56266410bfdSClemens Ladisch 	for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
56366410bfdSClemens Ladisch 		err = snd_ctl_add(chip->card,
56466410bfdSClemens Ladisch 				  snd_ctl_new1(&dg_controls[i], chip));
56566410bfdSClemens Ladisch 		if (err < 0)
56666410bfdSClemens Ladisch 			return err;
56766410bfdSClemens Ladisch 	}
56866410bfdSClemens Ladisch 	return 0;
56966410bfdSClemens Ladisch }
57066410bfdSClemens Ladisch 
57166410bfdSClemens Ladisch static void dump_cs4245_registers(struct oxygen *chip,
57266410bfdSClemens Ladisch 				  struct snd_info_buffer *buffer)
57366410bfdSClemens Ladisch {
57466410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
57566410bfdSClemens Ladisch 	unsigned int i;
57666410bfdSClemens Ladisch 
57766410bfdSClemens Ladisch 	snd_iprintf(buffer, "\nCS4245:");
57866410bfdSClemens Ladisch 	for (i = 1; i <= 0x10; ++i)
57966410bfdSClemens Ladisch 		snd_iprintf(buffer, " %02x", data->cs4245_regs[i]);
58066410bfdSClemens Ladisch 	snd_iprintf(buffer, "\n");
58166410bfdSClemens Ladisch }
58266410bfdSClemens Ladisch 
58366410bfdSClemens Ladisch struct oxygen_model model_xonar_dg = {
58466410bfdSClemens Ladisch 	.shortname = "Xonar DG",
58566410bfdSClemens Ladisch 	.longname = "C-Media Oxygen HD Audio",
58666410bfdSClemens Ladisch 	.chip = "CMI8786",
58766410bfdSClemens Ladisch 	.init = dg_init,
58866410bfdSClemens Ladisch 	.control_filter = dg_control_filter,
58966410bfdSClemens Ladisch 	.mixer_init = dg_mixer_init,
59066410bfdSClemens Ladisch 	.cleanup = dg_cleanup,
59166410bfdSClemens Ladisch 	.suspend = dg_suspend,
59266410bfdSClemens Ladisch 	.resume = dg_resume,
59366410bfdSClemens Ladisch 	.set_dac_params = set_cs4245_dac_params,
59466410bfdSClemens Ladisch 	.set_adc_params = set_cs4245_adc_params,
595efbeb071SClemens Ladisch 	.adjust_dac_routing = adjust_dg_dac_routing,
59666410bfdSClemens Ladisch 	.dump_registers = dump_cs4245_registers,
59766410bfdSClemens Ladisch 	.model_data_size = sizeof(struct dg),
59866410bfdSClemens Ladisch 	.device_config = PLAYBACK_0_TO_I2S |
59966410bfdSClemens Ladisch 			 PLAYBACK_1_TO_SPDIF |
600c3867352SClemens Ladisch 			 CAPTURE_0_FROM_I2S_2,
60166410bfdSClemens Ladisch 	.dac_channels_pcm = 6,
60266410bfdSClemens Ladisch 	.dac_channels_mixer = 0,
60366410bfdSClemens Ladisch 	.function_flags = OXYGEN_FUNCTION_SPI,
60466410bfdSClemens Ladisch 	.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
60566410bfdSClemens Ladisch 	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
60666410bfdSClemens Ladisch 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
60766410bfdSClemens Ladisch 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
60866410bfdSClemens Ladisch };
609