xref: /openbmc/linux/sound/pci/oxygen/xonar_dg.c (revision f8fe80e4)
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  *
2766410bfdSClemens Ladisch  *   GPIO 3 <- ?
2866410bfdSClemens Ladisch  *   GPIO 4 <- headphone detect
2966410bfdSClemens Ladisch  *   GPIO 5 -> route input jack to line-in (0) or mic-in (1)
3066410bfdSClemens Ladisch  *   GPIO 6 -> route input jack to line-in (0) or mic-in (1)
3166410bfdSClemens Ladisch  *   GPIO 7 -> enable rear headphone amp
3266410bfdSClemens Ladisch  *   GPIO 8 -> enable output to speakers
3366410bfdSClemens Ladisch  *
3466410bfdSClemens Ladisch  * CS4245:
3566410bfdSClemens Ladisch  *
3666410bfdSClemens Ladisch  *   input 1 <- aux
3766410bfdSClemens Ladisch  *   input 2 <- front mic
3866410bfdSClemens Ladisch  *   input 4 <- line/mic
3966410bfdSClemens Ladisch  *   aux out -> front panel headphones
4066410bfdSClemens Ladisch  */
4166410bfdSClemens Ladisch 
4266410bfdSClemens Ladisch #include <linux/pci.h>
43e92d4575SStephen Rothwell #include <linux/delay.h>
4466410bfdSClemens Ladisch #include <sound/control.h>
4566410bfdSClemens Ladisch #include <sound/core.h>
4666410bfdSClemens Ladisch #include <sound/info.h>
4766410bfdSClemens Ladisch #include <sound/pcm.h>
4866410bfdSClemens Ladisch #include <sound/tlv.h>
4966410bfdSClemens Ladisch #include "oxygen.h"
5066410bfdSClemens Ladisch #include "xonar_dg.h"
5166410bfdSClemens Ladisch #include "cs4245.h"
5266410bfdSClemens Ladisch 
5366410bfdSClemens Ladisch #define GPIO_MAGIC		0x0008
5466410bfdSClemens Ladisch #define GPIO_HP_DETECT		0x0010
5566410bfdSClemens Ladisch #define GPIO_INPUT_ROUTE	0x0060
5666410bfdSClemens Ladisch #define GPIO_HP_REAR		0x0080
5766410bfdSClemens Ladisch #define GPIO_OUTPUT_ENABLE	0x0100
5866410bfdSClemens Ladisch 
5966410bfdSClemens Ladisch struct dg {
6066410bfdSClemens Ladisch 	unsigned int output_sel;
6166410bfdSClemens Ladisch 	s8 input_vol[4][2];
6266410bfdSClemens Ladisch 	unsigned int input_sel;
6366410bfdSClemens Ladisch 	u8 hp_vol_att;
6466410bfdSClemens Ladisch 	u8 cs4245_regs[0x11];
6566410bfdSClemens Ladisch };
6666410bfdSClemens Ladisch 
6766410bfdSClemens Ladisch static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value)
6866410bfdSClemens Ladisch {
6966410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
7066410bfdSClemens Ladisch 
7166410bfdSClemens Ladisch 	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
7266410bfdSClemens Ladisch 			 OXYGEN_SPI_DATA_LENGTH_3 |
7366410bfdSClemens Ladisch 			 OXYGEN_SPI_CLOCK_1280 |
7466410bfdSClemens Ladisch 			 (0 << OXYGEN_SPI_CODEC_SHIFT) |
7566410bfdSClemens Ladisch 			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
7666410bfdSClemens Ladisch 			 CS4245_SPI_ADDRESS |
7766410bfdSClemens Ladisch 			 CS4245_SPI_WRITE |
78f8fe80e4SClemens Ladisch 			 (reg << 8) | value);
7966410bfdSClemens Ladisch 	data->cs4245_regs[reg] = value;
8066410bfdSClemens Ladisch }
8166410bfdSClemens Ladisch 
8266410bfdSClemens Ladisch static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value)
8366410bfdSClemens Ladisch {
8466410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
8566410bfdSClemens Ladisch 
8666410bfdSClemens Ladisch 	if (value != data->cs4245_regs[reg])
8766410bfdSClemens Ladisch 		cs4245_write(chip, reg, value);
8866410bfdSClemens Ladisch }
8966410bfdSClemens Ladisch 
9066410bfdSClemens Ladisch static void cs4245_registers_init(struct oxygen *chip)
9166410bfdSClemens Ladisch {
9266410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
9366410bfdSClemens Ladisch 
9466410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN);
9566410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_DAC_CTRL_1,
9666410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_DAC_CTRL_1]);
9766410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_ADC_CTRL,
9866410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_ADC_CTRL]);
9966410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_SIGNAL_SEL,
10066410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_SIGNAL_SEL]);
10166410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_PGA_B_CTRL,
10266410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_PGA_B_CTRL]);
10366410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_PGA_A_CTRL,
10466410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_PGA_A_CTRL]);
10566410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_ANALOG_IN,
10666410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_ANALOG_IN]);
10766410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_DAC_A_CTRL,
10866410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_DAC_A_CTRL]);
10966410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_DAC_B_CTRL,
11066410bfdSClemens Ladisch 		     data->cs4245_regs[CS4245_DAC_B_CTRL]);
11166410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_DAC_CTRL_2,
11266410bfdSClemens Ladisch 		     CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC);
11366410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_INT_MASK, 0);
11466410bfdSClemens Ladisch 	cs4245_write(chip, CS4245_POWER_CTRL, 0);
11566410bfdSClemens Ladisch }
11666410bfdSClemens Ladisch 
11766410bfdSClemens Ladisch static void cs4245_init(struct oxygen *chip)
11866410bfdSClemens Ladisch {
11966410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
12066410bfdSClemens Ladisch 
12166410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_DAC_CTRL_1] =
12266410bfdSClemens Ladisch 		CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
12366410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_ADC_CTRL] =
12466410bfdSClemens Ladisch 		CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
12566410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_SIGNAL_SEL] =
12666410bfdSClemens Ladisch 		CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH;
12766410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_PGA_B_CTRL] = 0;
12866410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_PGA_A_CTRL] = 0;
12966410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_ANALOG_IN] =
13066410bfdSClemens Ladisch 		CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4;
13166410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_DAC_A_CTRL] = 0;
13266410bfdSClemens Ladisch 	data->cs4245_regs[CS4245_DAC_B_CTRL] = 0;
13366410bfdSClemens Ladisch 	cs4245_registers_init(chip);
13466410bfdSClemens Ladisch 	snd_component_add(chip->card, "CS4245");
13566410bfdSClemens Ladisch }
13666410bfdSClemens Ladisch 
13766410bfdSClemens Ladisch static void dg_output_enable(struct oxygen *chip)
13866410bfdSClemens Ladisch {
13966410bfdSClemens Ladisch 	msleep(2500);
14066410bfdSClemens Ladisch 	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
14166410bfdSClemens Ladisch }
14266410bfdSClemens Ladisch 
14366410bfdSClemens Ladisch static void dg_init(struct oxygen *chip)
14466410bfdSClemens Ladisch {
14566410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
14666410bfdSClemens Ladisch 
14766410bfdSClemens Ladisch 	data->output_sel = 0;
14866410bfdSClemens Ladisch 	data->input_sel = 3;
14966410bfdSClemens Ladisch 	data->hp_vol_att = 2 * 16;
15066410bfdSClemens Ladisch 
15166410bfdSClemens Ladisch 	cs4245_init(chip);
15266410bfdSClemens Ladisch 
15366410bfdSClemens Ladisch 	oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
15466410bfdSClemens Ladisch 			    GPIO_MAGIC | GPIO_HP_DETECT);
15566410bfdSClemens Ladisch 	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
15666410bfdSClemens Ladisch 			  GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE);
15766410bfdSClemens Ladisch 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
15866410bfdSClemens Ladisch 			    GPIO_INPUT_ROUTE | GPIO_HP_REAR);
15966410bfdSClemens Ladisch 	dg_output_enable(chip);
16066410bfdSClemens Ladisch }
16166410bfdSClemens Ladisch 
16266410bfdSClemens Ladisch static void dg_cleanup(struct oxygen *chip)
16366410bfdSClemens Ladisch {
16466410bfdSClemens Ladisch 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
16566410bfdSClemens Ladisch }
16666410bfdSClemens Ladisch 
16766410bfdSClemens Ladisch static void dg_suspend(struct oxygen *chip)
16866410bfdSClemens Ladisch {
16966410bfdSClemens Ladisch 	dg_cleanup(chip);
17066410bfdSClemens Ladisch }
17166410bfdSClemens Ladisch 
17266410bfdSClemens Ladisch static void dg_resume(struct oxygen *chip)
17366410bfdSClemens Ladisch {
17466410bfdSClemens Ladisch 	cs4245_registers_init(chip);
17566410bfdSClemens Ladisch 	dg_output_enable(chip);
17666410bfdSClemens Ladisch }
17766410bfdSClemens Ladisch 
17866410bfdSClemens Ladisch static void set_cs4245_dac_params(struct oxygen *chip,
17966410bfdSClemens Ladisch 				  struct snd_pcm_hw_params *params)
18066410bfdSClemens Ladisch {
18166410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
18266410bfdSClemens Ladisch 	u8 value;
18366410bfdSClemens Ladisch 
18466410bfdSClemens Ladisch 	value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
18566410bfdSClemens Ladisch 	if (params_rate(params) <= 50000)
18666410bfdSClemens Ladisch 		value |= CS4245_DAC_FM_SINGLE;
18766410bfdSClemens Ladisch 	else if (params_rate(params) <= 100000)
18866410bfdSClemens Ladisch 		value |= CS4245_DAC_FM_DOUBLE;
18966410bfdSClemens Ladisch 	else
19066410bfdSClemens Ladisch 		value |= CS4245_DAC_FM_QUAD;
19166410bfdSClemens Ladisch 	cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value);
19266410bfdSClemens Ladisch }
19366410bfdSClemens Ladisch 
19466410bfdSClemens Ladisch static void set_cs4245_adc_params(struct oxygen *chip,
19566410bfdSClemens Ladisch 				  struct snd_pcm_hw_params *params)
19666410bfdSClemens Ladisch {
19766410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
19866410bfdSClemens Ladisch 	u8 value;
19966410bfdSClemens Ladisch 
20066410bfdSClemens Ladisch 	value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
20166410bfdSClemens Ladisch 	if (params_rate(params) <= 50000)
20266410bfdSClemens Ladisch 		value |= CS4245_ADC_FM_SINGLE;
20366410bfdSClemens Ladisch 	else if (params_rate(params) <= 100000)
20466410bfdSClemens Ladisch 		value |= CS4245_ADC_FM_DOUBLE;
20566410bfdSClemens Ladisch 	else
20666410bfdSClemens Ladisch 		value |= CS4245_ADC_FM_QUAD;
20766410bfdSClemens Ladisch 	cs4245_write_cached(chip, CS4245_ADC_CTRL, value);
20866410bfdSClemens Ladisch }
20966410bfdSClemens Ladisch 
21066410bfdSClemens Ladisch static int output_switch_info(struct snd_kcontrol *ctl,
21166410bfdSClemens Ladisch 			      struct snd_ctl_elem_info *info)
21266410bfdSClemens Ladisch {
21366410bfdSClemens Ladisch 	static const char *const names[3] = {
21466410bfdSClemens Ladisch 		"Speakers", "Headphones", "FP Headphones"
21566410bfdSClemens Ladisch 	};
21666410bfdSClemens Ladisch 
2179600732bSClemens Ladisch 	return snd_ctl_enum_info(info, 1, 3, names);
21866410bfdSClemens Ladisch }
21966410bfdSClemens Ladisch 
22066410bfdSClemens Ladisch static int output_switch_get(struct snd_kcontrol *ctl,
22166410bfdSClemens Ladisch 			     struct snd_ctl_elem_value *value)
22266410bfdSClemens Ladisch {
22366410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
22466410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
22566410bfdSClemens Ladisch 
22666410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
22766410bfdSClemens Ladisch 	value->value.enumerated.item[0] = data->output_sel;
22866410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
22966410bfdSClemens Ladisch 	return 0;
23066410bfdSClemens Ladisch }
23166410bfdSClemens Ladisch 
23266410bfdSClemens Ladisch static int output_switch_put(struct snd_kcontrol *ctl,
23366410bfdSClemens Ladisch 			     struct snd_ctl_elem_value *value)
23466410bfdSClemens Ladisch {
23566410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
23666410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
23766410bfdSClemens Ladisch 	u8 reg;
23866410bfdSClemens Ladisch 	int changed;
23966410bfdSClemens Ladisch 
24066410bfdSClemens Ladisch 	if (value->value.enumerated.item[0] > 2)
24166410bfdSClemens Ladisch 		return -EINVAL;
24266410bfdSClemens Ladisch 
24366410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
24466410bfdSClemens Ladisch 	changed = value->value.enumerated.item[0] != data->output_sel;
24566410bfdSClemens Ladisch 	if (changed) {
24666410bfdSClemens Ladisch 		data->output_sel = value->value.enumerated.item[0];
24766410bfdSClemens Ladisch 
24866410bfdSClemens Ladisch 		reg = data->cs4245_regs[CS4245_SIGNAL_SEL] &
24966410bfdSClemens Ladisch 						~CS4245_A_OUT_SEL_MASK;
25066410bfdSClemens Ladisch 		reg |= data->output_sel == 2 ?
25166410bfdSClemens Ladisch 				CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ;
25266410bfdSClemens Ladisch 		cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg);
25366410bfdSClemens Ladisch 
25466410bfdSClemens Ladisch 		cs4245_write_cached(chip, CS4245_DAC_A_CTRL,
25566410bfdSClemens Ladisch 				    data->output_sel ? data->hp_vol_att : 0);
25666410bfdSClemens Ladisch 		cs4245_write_cached(chip, CS4245_DAC_B_CTRL,
25766410bfdSClemens Ladisch 				    data->output_sel ? data->hp_vol_att : 0);
25866410bfdSClemens Ladisch 
25966410bfdSClemens Ladisch 		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
26066410bfdSClemens Ladisch 				      data->output_sel == 1 ? GPIO_HP_REAR : 0,
26166410bfdSClemens Ladisch 				      GPIO_HP_REAR);
26266410bfdSClemens Ladisch 	}
26366410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
26466410bfdSClemens Ladisch 	return changed;
26566410bfdSClemens Ladisch }
26666410bfdSClemens Ladisch 
26766410bfdSClemens Ladisch static int hp_volume_offset_info(struct snd_kcontrol *ctl,
26866410bfdSClemens Ladisch 				 struct snd_ctl_elem_info *info)
26966410bfdSClemens Ladisch {
27066410bfdSClemens Ladisch 	static const char *const names[3] = {
27166410bfdSClemens Ladisch 		"< 64 ohms", "64-150 ohms", "150-300 ohms"
27266410bfdSClemens Ladisch 	};
27366410bfdSClemens Ladisch 
2749600732bSClemens Ladisch 	return snd_ctl_enum_info(info, 1, 3, names);
27566410bfdSClemens Ladisch }
27666410bfdSClemens Ladisch 
27766410bfdSClemens Ladisch static int hp_volume_offset_get(struct snd_kcontrol *ctl,
27866410bfdSClemens Ladisch 				struct snd_ctl_elem_value *value)
27966410bfdSClemens Ladisch {
28066410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
28166410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
28266410bfdSClemens Ladisch 
28366410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
28466410bfdSClemens Ladisch 	if (data->hp_vol_att > 2 * 7)
28566410bfdSClemens Ladisch 		value->value.enumerated.item[0] = 0;
28666410bfdSClemens Ladisch 	else if (data->hp_vol_att > 0)
28766410bfdSClemens Ladisch 		value->value.enumerated.item[0] = 1;
28866410bfdSClemens Ladisch 	else
28966410bfdSClemens Ladisch 		value->value.enumerated.item[0] = 2;
29066410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
29166410bfdSClemens Ladisch 	return 0;
29266410bfdSClemens Ladisch }
29366410bfdSClemens Ladisch 
29466410bfdSClemens Ladisch static int hp_volume_offset_put(struct snd_kcontrol *ctl,
29566410bfdSClemens Ladisch 				struct snd_ctl_elem_value *value)
29666410bfdSClemens Ladisch {
29766410bfdSClemens Ladisch 	static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
29866410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
29966410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
30066410bfdSClemens Ladisch 	s8 att;
30166410bfdSClemens Ladisch 	int changed;
30266410bfdSClemens Ladisch 
30366410bfdSClemens Ladisch 	if (value->value.enumerated.item[0] > 2)
30466410bfdSClemens Ladisch 		return -EINVAL;
30566410bfdSClemens Ladisch 	att = atts[value->value.enumerated.item[0]];
30666410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
30766410bfdSClemens Ladisch 	changed = att != data->hp_vol_att;
30866410bfdSClemens Ladisch 	if (changed) {
30966410bfdSClemens Ladisch 		data->hp_vol_att = att;
31066410bfdSClemens Ladisch 		if (data->output_sel) {
31166410bfdSClemens Ladisch 			cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
31266410bfdSClemens Ladisch 			cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
31366410bfdSClemens Ladisch 		}
31466410bfdSClemens Ladisch 	}
31566410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
31666410bfdSClemens Ladisch 	return changed;
31766410bfdSClemens Ladisch }
31866410bfdSClemens Ladisch 
31966410bfdSClemens Ladisch static int input_vol_info(struct snd_kcontrol *ctl,
32066410bfdSClemens Ladisch 			  struct snd_ctl_elem_info *info)
32166410bfdSClemens Ladisch {
32266410bfdSClemens Ladisch 	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
32366410bfdSClemens Ladisch 	info->count = 2;
32466410bfdSClemens Ladisch 	info->value.integer.min = 2 * -12;
32566410bfdSClemens Ladisch 	info->value.integer.max = 2 * 12;
32666410bfdSClemens Ladisch 	return 0;
32766410bfdSClemens Ladisch }
32866410bfdSClemens Ladisch 
32966410bfdSClemens Ladisch static int input_vol_get(struct snd_kcontrol *ctl,
33066410bfdSClemens Ladisch 			 struct snd_ctl_elem_value *value)
33166410bfdSClemens Ladisch {
33266410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
33366410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
33466410bfdSClemens Ladisch 	unsigned int idx = ctl->private_value;
33566410bfdSClemens Ladisch 
33666410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
33766410bfdSClemens Ladisch 	value->value.integer.value[0] = data->input_vol[idx][0];
33866410bfdSClemens Ladisch 	value->value.integer.value[1] = data->input_vol[idx][1];
33966410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
34066410bfdSClemens Ladisch 	return 0;
34166410bfdSClemens Ladisch }
34266410bfdSClemens Ladisch 
34366410bfdSClemens Ladisch static int input_vol_put(struct snd_kcontrol *ctl,
34466410bfdSClemens Ladisch 			 struct snd_ctl_elem_value *value)
34566410bfdSClemens Ladisch {
34666410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
34766410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
34866410bfdSClemens Ladisch 	unsigned int idx = ctl->private_value;
34966410bfdSClemens Ladisch 	int changed = 0;
35066410bfdSClemens Ladisch 
35166410bfdSClemens Ladisch 	if (value->value.integer.value[0] < 2 * -12 ||
35266410bfdSClemens Ladisch 	    value->value.integer.value[0] > 2 * 12 ||
35366410bfdSClemens Ladisch 	    value->value.integer.value[1] < 2 * -12 ||
35466410bfdSClemens Ladisch 	    value->value.integer.value[1] > 2 * 12)
35566410bfdSClemens Ladisch 		return -EINVAL;
35666410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
35766410bfdSClemens Ladisch 	changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
35866410bfdSClemens Ladisch 		  data->input_vol[idx][1] != value->value.integer.value[1];
35966410bfdSClemens Ladisch 	if (changed) {
36066410bfdSClemens Ladisch 		data->input_vol[idx][0] = value->value.integer.value[0];
36166410bfdSClemens Ladisch 		data->input_vol[idx][1] = value->value.integer.value[1];
36266410bfdSClemens Ladisch 		if (idx == data->input_sel) {
36366410bfdSClemens Ladisch 			cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
36466410bfdSClemens Ladisch 					    data->input_vol[idx][0]);
36566410bfdSClemens Ladisch 			cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
36666410bfdSClemens Ladisch 					    data->input_vol[idx][1]);
36766410bfdSClemens Ladisch 		}
36866410bfdSClemens Ladisch 	}
36966410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
37066410bfdSClemens Ladisch 	return changed;
37166410bfdSClemens Ladisch }
37266410bfdSClemens Ladisch 
37366410bfdSClemens Ladisch static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0);
37466410bfdSClemens Ladisch 
37566410bfdSClemens Ladisch static int input_sel_info(struct snd_kcontrol *ctl,
37666410bfdSClemens Ladisch 			  struct snd_ctl_elem_info *info)
37766410bfdSClemens Ladisch {
37866410bfdSClemens Ladisch 	static const char *const names[4] = {
37966410bfdSClemens Ladisch 		"Mic", "Aux", "Front Mic", "Line"
38066410bfdSClemens Ladisch 	};
38166410bfdSClemens Ladisch 
3829600732bSClemens Ladisch 	return snd_ctl_enum_info(info, 1, 4, names);
38366410bfdSClemens Ladisch }
38466410bfdSClemens Ladisch 
38566410bfdSClemens Ladisch static int input_sel_get(struct snd_kcontrol *ctl,
38666410bfdSClemens Ladisch 			 struct snd_ctl_elem_value *value)
38766410bfdSClemens Ladisch {
38866410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
38966410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
39066410bfdSClemens Ladisch 
39166410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
39266410bfdSClemens Ladisch 	value->value.enumerated.item[0] = data->input_sel;
39366410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
39466410bfdSClemens Ladisch 	return 0;
39566410bfdSClemens Ladisch }
39666410bfdSClemens Ladisch 
39766410bfdSClemens Ladisch static int input_sel_put(struct snd_kcontrol *ctl,
39866410bfdSClemens Ladisch 			 struct snd_ctl_elem_value *value)
39966410bfdSClemens Ladisch {
40066410bfdSClemens Ladisch 	static const u8 sel_values[4] = {
40166410bfdSClemens Ladisch 		CS4245_SEL_MIC,
40266410bfdSClemens Ladisch 		CS4245_SEL_INPUT_1,
40366410bfdSClemens Ladisch 		CS4245_SEL_INPUT_2,
40466410bfdSClemens Ladisch 		CS4245_SEL_INPUT_4
40566410bfdSClemens Ladisch 	};
40666410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
40766410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
40866410bfdSClemens Ladisch 	int changed;
40966410bfdSClemens Ladisch 
41066410bfdSClemens Ladisch 	if (value->value.enumerated.item[0] > 3)
41166410bfdSClemens Ladisch 		return -EINVAL;
41266410bfdSClemens Ladisch 
41366410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
41466410bfdSClemens Ladisch 	changed = value->value.enumerated.item[0] != data->input_sel;
41566410bfdSClemens Ladisch 	if (changed) {
41666410bfdSClemens Ladisch 		data->input_sel = value->value.enumerated.item[0];
41766410bfdSClemens Ladisch 
41866410bfdSClemens Ladisch 		cs4245_write(chip, CS4245_ANALOG_IN,
41966410bfdSClemens Ladisch 			     (data->cs4245_regs[CS4245_ANALOG_IN] &
42066410bfdSClemens Ladisch 							~CS4245_SEL_MASK) |
42166410bfdSClemens Ladisch 			     sel_values[data->input_sel]);
42266410bfdSClemens Ladisch 
42366410bfdSClemens Ladisch 		cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
42466410bfdSClemens Ladisch 				    data->input_vol[data->input_sel][0]);
42566410bfdSClemens Ladisch 		cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
42666410bfdSClemens Ladisch 				    data->input_vol[data->input_sel][1]);
42766410bfdSClemens Ladisch 
42866410bfdSClemens Ladisch 		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
42966410bfdSClemens Ladisch 				      data->input_sel ? 0 : GPIO_INPUT_ROUTE,
43066410bfdSClemens Ladisch 				      GPIO_INPUT_ROUTE);
43166410bfdSClemens Ladisch 	}
43266410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
43366410bfdSClemens Ladisch 	return changed;
43466410bfdSClemens Ladisch }
43566410bfdSClemens Ladisch 
43666410bfdSClemens Ladisch static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
43766410bfdSClemens Ladisch {
43866410bfdSClemens Ladisch 	static const char *const names[2] = { "Active", "Frozen" };
43966410bfdSClemens Ladisch 
4409600732bSClemens Ladisch 	return snd_ctl_enum_info(info, 1, 2, names);
44166410bfdSClemens Ladisch }
44266410bfdSClemens Ladisch 
44366410bfdSClemens Ladisch static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
44466410bfdSClemens Ladisch {
44566410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
44666410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
44766410bfdSClemens Ladisch 
44866410bfdSClemens Ladisch 	value->value.enumerated.item[0] =
44966410bfdSClemens Ladisch 		!!(data->cs4245_regs[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
45066410bfdSClemens Ladisch 	return 0;
45166410bfdSClemens Ladisch }
45266410bfdSClemens Ladisch 
45366410bfdSClemens Ladisch static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
45466410bfdSClemens Ladisch {
45566410bfdSClemens Ladisch 	struct oxygen *chip = ctl->private_data;
45666410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
45766410bfdSClemens Ladisch 	u8 reg;
45866410bfdSClemens Ladisch 	int changed;
45966410bfdSClemens Ladisch 
46066410bfdSClemens Ladisch 	mutex_lock(&chip->mutex);
46166410bfdSClemens Ladisch 	reg = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
46266410bfdSClemens Ladisch 	if (value->value.enumerated.item[0])
46366410bfdSClemens Ladisch 		reg |= CS4245_HPF_FREEZE;
46466410bfdSClemens Ladisch 	changed = reg != data->cs4245_regs[CS4245_ADC_CTRL];
46566410bfdSClemens Ladisch 	if (changed)
46666410bfdSClemens Ladisch 		cs4245_write(chip, CS4245_ADC_CTRL, reg);
46766410bfdSClemens Ladisch 	mutex_unlock(&chip->mutex);
46866410bfdSClemens Ladisch 	return changed;
46966410bfdSClemens Ladisch }
47066410bfdSClemens Ladisch 
47166410bfdSClemens Ladisch #define INPUT_VOLUME(xname, index) { \
47266410bfdSClemens Ladisch 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
47366410bfdSClemens Ladisch 	.name = xname, \
47466410bfdSClemens Ladisch 	.info = input_vol_info, \
47566410bfdSClemens Ladisch 	.get = input_vol_get, \
47666410bfdSClemens Ladisch 	.put = input_vol_put, \
47766410bfdSClemens Ladisch 	.tlv = { .p = cs4245_pga_db_scale }, \
47866410bfdSClemens Ladisch 	.private_value = index, \
47966410bfdSClemens Ladisch }
48066410bfdSClemens Ladisch static const struct snd_kcontrol_new dg_controls[] = {
48166410bfdSClemens Ladisch 	{
48266410bfdSClemens Ladisch 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
48366410bfdSClemens Ladisch 		.name = "Analog Output Playback Enum",
48466410bfdSClemens Ladisch 		.info = output_switch_info,
48566410bfdSClemens Ladisch 		.get = output_switch_get,
48666410bfdSClemens Ladisch 		.put = output_switch_put,
48766410bfdSClemens Ladisch 	},
48866410bfdSClemens Ladisch 	{
48966410bfdSClemens Ladisch 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
49066410bfdSClemens Ladisch 		.name = "Headphones Impedance Playback Enum",
49166410bfdSClemens Ladisch 		.info = hp_volume_offset_info,
49266410bfdSClemens Ladisch 		.get = hp_volume_offset_get,
49366410bfdSClemens Ladisch 		.put = hp_volume_offset_put,
49466410bfdSClemens Ladisch 	},
49566410bfdSClemens Ladisch 	INPUT_VOLUME("Mic Capture Volume", 0),
49666410bfdSClemens Ladisch 	INPUT_VOLUME("Aux Capture Volume", 1),
49766410bfdSClemens Ladisch 	INPUT_VOLUME("Front Mic Capture Volume", 2),
49866410bfdSClemens Ladisch 	INPUT_VOLUME("Line Capture Volume", 3),
49966410bfdSClemens Ladisch 	{
50066410bfdSClemens Ladisch 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
50166410bfdSClemens Ladisch 		.name = "Capture Source",
50266410bfdSClemens Ladisch 		.info = input_sel_info,
50366410bfdSClemens Ladisch 		.get = input_sel_get,
50466410bfdSClemens Ladisch 		.put = input_sel_put,
50566410bfdSClemens Ladisch 	},
50666410bfdSClemens Ladisch 	{
50766410bfdSClemens Ladisch 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
50866410bfdSClemens Ladisch 		.name = "ADC High-pass Filter Capture Enum",
50966410bfdSClemens Ladisch 		.info = hpf_info,
51066410bfdSClemens Ladisch 		.get = hpf_get,
51166410bfdSClemens Ladisch 		.put = hpf_put,
51266410bfdSClemens Ladisch 	},
51366410bfdSClemens Ladisch };
51466410bfdSClemens Ladisch 
51566410bfdSClemens Ladisch static int dg_control_filter(struct snd_kcontrol_new *template)
51666410bfdSClemens Ladisch {
51766410bfdSClemens Ladisch 	if (!strncmp(template->name, "Master Playback ", 16))
51866410bfdSClemens Ladisch 		return 1;
51966410bfdSClemens Ladisch 	return 0;
52066410bfdSClemens Ladisch }
52166410bfdSClemens Ladisch 
52266410bfdSClemens Ladisch static int dg_mixer_init(struct oxygen *chip)
52366410bfdSClemens Ladisch {
52466410bfdSClemens Ladisch 	unsigned int i;
52566410bfdSClemens Ladisch 	int err;
52666410bfdSClemens Ladisch 
52766410bfdSClemens Ladisch 	for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
52866410bfdSClemens Ladisch 		err = snd_ctl_add(chip->card,
52966410bfdSClemens Ladisch 				  snd_ctl_new1(&dg_controls[i], chip));
53066410bfdSClemens Ladisch 		if (err < 0)
53166410bfdSClemens Ladisch 			return err;
53266410bfdSClemens Ladisch 	}
53366410bfdSClemens Ladisch 	return 0;
53466410bfdSClemens Ladisch }
53566410bfdSClemens Ladisch 
53666410bfdSClemens Ladisch static void dump_cs4245_registers(struct oxygen *chip,
53766410bfdSClemens Ladisch 				  struct snd_info_buffer *buffer)
53866410bfdSClemens Ladisch {
53966410bfdSClemens Ladisch 	struct dg *data = chip->model_data;
54066410bfdSClemens Ladisch 	unsigned int i;
54166410bfdSClemens Ladisch 
54266410bfdSClemens Ladisch 	snd_iprintf(buffer, "\nCS4245:");
54366410bfdSClemens Ladisch 	for (i = 1; i <= 0x10; ++i)
54466410bfdSClemens Ladisch 		snd_iprintf(buffer, " %02x", data->cs4245_regs[i]);
54566410bfdSClemens Ladisch 	snd_iprintf(buffer, "\n");
54666410bfdSClemens Ladisch }
54766410bfdSClemens Ladisch 
54866410bfdSClemens Ladisch struct oxygen_model model_xonar_dg = {
54966410bfdSClemens Ladisch 	.shortname = "Xonar DG",
55066410bfdSClemens Ladisch 	.longname = "C-Media Oxygen HD Audio",
55166410bfdSClemens Ladisch 	.chip = "CMI8786",
55266410bfdSClemens Ladisch 	.init = dg_init,
55366410bfdSClemens Ladisch 	.control_filter = dg_control_filter,
55466410bfdSClemens Ladisch 	.mixer_init = dg_mixer_init,
55566410bfdSClemens Ladisch 	.cleanup = dg_cleanup,
55666410bfdSClemens Ladisch 	.suspend = dg_suspend,
55766410bfdSClemens Ladisch 	.resume = dg_resume,
55866410bfdSClemens Ladisch 	.set_dac_params = set_cs4245_dac_params,
55966410bfdSClemens Ladisch 	.set_adc_params = set_cs4245_adc_params,
56066410bfdSClemens Ladisch 	.dump_registers = dump_cs4245_registers,
56166410bfdSClemens Ladisch 	.model_data_size = sizeof(struct dg),
56266410bfdSClemens Ladisch 	.device_config = PLAYBACK_0_TO_I2S |
56366410bfdSClemens Ladisch 			 PLAYBACK_1_TO_SPDIF |
564c3867352SClemens Ladisch 			 CAPTURE_0_FROM_I2S_2,
56566410bfdSClemens Ladisch 	.dac_channels_pcm = 6,
56666410bfdSClemens Ladisch 	.dac_channels_mixer = 0,
56766410bfdSClemens Ladisch 	.function_flags = OXYGEN_FUNCTION_SPI,
56866410bfdSClemens Ladisch 	.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
56966410bfdSClemens Ladisch 	.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
57066410bfdSClemens Ladisch 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
57166410bfdSClemens Ladisch 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
57266410bfdSClemens Ladisch };
573