diff options
Diffstat (limited to 'sound/soc/codecs/tlv320aic3x.c')
| -rw-r--r-- | sound/soc/codecs/tlv320aic3x.c | 844 | 
1 files changed, 452 insertions, 392 deletions
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index fc687790188..e12fafbb1e0 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -40,13 +40,13 @@  #include <linux/i2c.h>  #include <linux/gpio.h>  #include <linux/regulator/consumer.h> -#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_gpio.h>  #include <linux/slab.h>  #include <sound/core.h>  #include <sound/pcm.h>  #include <sound/pcm_params.h>  #include <sound/soc.h> -#include <sound/soc-dapm.h>  #include <sound/initval.h>  #include <sound/tlv.h>  #include <sound/tlv320aic3x.h> @@ -61,6 +61,8 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = {  	"DRVDD",	/* ADC Analog and Output Driver Voltage */  }; +static LIST_HEAD(reset_list); +  struct aic3x_priv;  struct aic3x_disable_nb { @@ -71,12 +73,12 @@ struct aic3x_disable_nb {  /* codec private data */  struct aic3x_priv {  	struct snd_soc_codec *codec; +	struct regmap *regmap;  	struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES];  	struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES]; -	enum snd_soc_control_type control_type;  	struct aic3x_setup_data *setup; -	void *control_data;  	unsigned int sysclk; +	struct list_head list;  	int master;  	int gpio_reset;  	int power; @@ -84,69 +86,55 @@ struct aic3x_priv {  #define AIC3X_MODEL_33 1  #define AIC3X_MODEL_3007 2  	u16 model; -}; -/* - * AIC3X register cache - * We can't read the AIC3X register space when we are - * using 2 wire for device control, so we cache them instead. - * There is no point in caching the reset register - */ -static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { -	0x00, 0x00, 0x00, 0x10,	/* 0 */ -	0x04, 0x00, 0x00, 0x00,	/* 4 */ -	0x00, 0x00, 0x00, 0x01,	/* 8 */ -	0x00, 0x00, 0x00, 0x80,	/* 12 */ -	0x80, 0xff, 0xff, 0x78,	/* 16 */ -	0x78, 0x78, 0x78, 0x78,	/* 20 */ -	0x78, 0x00, 0x00, 0xfe,	/* 24 */ -	0x00, 0x00, 0xfe, 0x00,	/* 28 */ -	0x18, 0x18, 0x00, 0x00,	/* 32 */ -	0x00, 0x00, 0x00, 0x00,	/* 36 */ -	0x00, 0x00, 0x00, 0x80,	/* 40 */ -	0x80, 0x00, 0x00, 0x00,	/* 44 */ -	0x00, 0x00, 0x00, 0x04,	/* 48 */ -	0x00, 0x00, 0x00, 0x00,	/* 52 */ -	0x00, 0x00, 0x04, 0x00,	/* 56 */ -	0x00, 0x00, 0x00, 0x00,	/* 60 */ -	0x00, 0x04, 0x00, 0x00,	/* 64 */ -	0x00, 0x00, 0x00, 0x00,	/* 68 */ -	0x04, 0x00, 0x00, 0x00,	/* 72 */ -	0x00, 0x00, 0x00, 0x00,	/* 76 */ -	0x00, 0x00, 0x00, 0x00,	/* 80 */ -	0x00, 0x00, 0x00, 0x00,	/* 84 */ -	0x00, 0x00, 0x00, 0x00,	/* 88 */ -	0x00, 0x00, 0x00, 0x00,	/* 92 */ -	0x00, 0x00, 0x00, 0x00,	/* 96 */ -	0x00, 0x00, 0x02,	/* 100 */ +	/* Selects the micbias voltage */ +	enum aic3x_micbias_voltage micbias_vg;  }; -/* - * read from the aic3x register space. Only use for this function is if - * wanting to read volatile bits from those registers that has both read-only - * and read/write bits. All other cases should use snd_soc_read. - */ -static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg, -		      u8 *value) -{ -	u8 *cache = codec->reg_cache; - -	if (codec->cache_only) -		return -EINVAL; -	if (reg >= AIC3X_CACHEREGNUM) -		return -1; +static const struct reg_default aic3x_reg[] = { +	{   0, 0x00 }, {   1, 0x00 }, {   2, 0x00 }, {   3, 0x10 }, +	{   4, 0x04 }, {   5, 0x00 }, {   6, 0x00 }, {   7, 0x00 }, +	{   8, 0x00 }, {   9, 0x00 }, {  10, 0x00 }, {  11, 0x01 }, +	{  12, 0x00 }, {  13, 0x00 }, {  14, 0x00 }, {  15, 0x80 }, +	{  16, 0x80 }, {  17, 0xff }, {  18, 0xff }, {  19, 0x78 }, +	{  20, 0x78 }, {  21, 0x78 }, {  22, 0x78 }, {  23, 0x78 }, +	{  24, 0x78 }, {  25, 0x00 }, {  26, 0x00 }, {  27, 0xfe }, +	{  28, 0x00 }, {  29, 0x00 }, {  30, 0xfe }, {  31, 0x00 }, +	{  32, 0x18 }, {  33, 0x18 }, {  34, 0x00 }, {  35, 0x00 }, +	{  36, 0x00 }, {  37, 0x00 }, {  38, 0x00 }, {  39, 0x00 }, +	{  40, 0x00 }, {  41, 0x00 }, {  42, 0x00 }, {  43, 0x80 }, +	{  44, 0x80 }, {  45, 0x00 }, {  46, 0x00 }, {  47, 0x00 }, +	{  48, 0x00 }, {  49, 0x00 }, {  50, 0x00 }, {  51, 0x04 }, +	{  52, 0x00 }, {  53, 0x00 }, {  54, 0x00 }, {  55, 0x00 }, +	{  56, 0x00 }, {  57, 0x00 }, {  58, 0x04 }, {  59, 0x00 }, +	{  60, 0x00 }, {  61, 0x00 }, {  62, 0x00 }, {  63, 0x00 }, +	{  64, 0x00 }, {  65, 0x04 }, {  66, 0x00 }, {  67, 0x00 }, +	{  68, 0x00 }, {  69, 0x00 }, {  70, 0x00 }, {  71, 0x00 }, +	{  72, 0x04 }, {  73, 0x00 }, {  74, 0x00 }, {  75, 0x00 }, +	{  76, 0x00 }, {  77, 0x00 }, {  78, 0x00 }, {  79, 0x00 }, +	{  80, 0x00 }, {  81, 0x00 }, {  82, 0x00 }, {  83, 0x00 }, +	{  84, 0x00 }, {  85, 0x00 }, {  86, 0x00 }, {  87, 0x00 }, +	{  88, 0x00 }, {  89, 0x00 }, {  90, 0x00 }, {  91, 0x00 }, +	{  92, 0x00 }, {  93, 0x00 }, {  94, 0x00 }, {  95, 0x00 }, +	{  96, 0x00 }, {  97, 0x00 }, {  98, 0x00 }, {  99, 0x00 }, +	{ 100, 0x00 }, { 101, 0x00 }, { 102, 0x02 }, { 103, 0x00 }, +	{ 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 }, +	{ 108, 0x00 }, { 109, 0x00 }, +}; -	*value = codec->hw_read(codec, reg); -	cache[reg] = *value; +static const struct regmap_config aic3x_regmap = { +	.reg_bits = 8, +	.val_bits = 8, -	return 0; -} +	.max_register = DAC_ICC_ADJ, +	.reg_defaults = aic3x_reg, +	.num_reg_defaults = ARRAY_SIZE(aic3x_reg), +	.cache_type = REGCACHE_RBTREE, +};  #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ -{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ -	.info = snd_soc_info_volsw, \ -	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw_aic3x, \ -	.private_value =  SOC_SINGLE_VALUE(reg, shift, mask, invert) } +	SOC_SINGLE_EXT(xname, reg, shift, mask, invert, \ +		snd_soc_dapm_get_volsw, snd_soc_dapm_put_volsw_aic3x)  /*   * All input lines are connected when !0xf and disconnected with 0xf bit field, @@ -155,7 +143,7 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,  static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,  					struct snd_ctl_elem_value *ucontrol)  { -	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); +	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);  	struct soc_mixer_control *mc =  		(struct soc_mixer_control *)kcontrol->private_value;  	unsigned int reg = mc->reg; @@ -163,10 +151,9 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,  	int max = mc->max;  	unsigned int mask = (1 << fls(max)) - 1;  	unsigned int invert = mc->invert; -	unsigned short val, val_mask; -	int ret; -	struct snd_soc_dapm_path *path; -	int found = 0; +	unsigned short val; +	struct snd_soc_dapm_update update; +	int connect, change;  	val = (ucontrol->value.integer.value[0] & mask); @@ -174,38 +161,57 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,  	if (val)  		val = mask; +	connect = !!val; +  	if (invert)  		val = mask - val; -	val_mask = mask << shift; -	val = val << shift; - -	mutex_lock(&widget->codec->mutex); - -	if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { -		/* find dapm widget path assoc with kcontrol */ -		list_for_each_entry(path, &widget->codec->dapm_paths, list) { -			if (path->kcontrol != kcontrol) -				continue; - -			/* found, now check type */ -			found = 1; -			if (val) -				/* new connection */ -				path->connect = invert ? 0 : 1; -			else -				/* old connection must be powered down */ -				path->connect = invert ? 1 : 0; -			break; -		} -		if (found) -			snd_soc_dapm_sync(widget->codec); +	mask <<= shift; +	val <<= shift; + +	change = snd_soc_test_bits(codec, reg, mask, val); +	if (change) { +		update.kcontrol = kcontrol; +		update.reg = reg; +		update.mask = mask; +		update.val = val; + +		snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect, +			&update);  	} -	ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); +	return change; +} -	mutex_unlock(&widget->codec->mutex); -	return ret; +/* + * mic bias power on/off share the same register bits with + * output voltage of mic bias. when power on mic bias, we + * need reclaim it to voltage value. + * 0x0 = Powered off + * 0x1 = MICBIAS output is powered to 2.0V, + * 0x2 = MICBIAS output is powered to 2.5V + * 0x3 = MICBIAS output is connected to AVDD + */ +static int mic_bias_event(struct snd_soc_dapm_widget *w, +	struct snd_kcontrol *kcontrol, int event) +{ +	struct snd_soc_codec *codec = w->codec; +	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + +	switch (event) { +	case SND_SOC_DAPM_POST_PMU: +		/* change mic bias voltage to user defined */ +		snd_soc_update_bits(codec, MICBIAS_CTRL, +				MICBIAS_LEVEL_MASK, +				aic3x->micbias_vg << MICBIAS_LEVEL_SHIFT); +		break; + +	case SND_SOC_DAPM_PRE_PMD: +		snd_soc_update_bits(codec, MICBIAS_CTRL, +				MICBIAS_LEVEL_MASK, 0); +		break; +	} +	return 0;  }  static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" }; @@ -223,11 +229,13 @@ static const char *aic3x_adc_hpf[] =  #define RDAC_ENUM	1  #define LHPCOM_ENUM	2  #define RHPCOM_ENUM	3 -#define LINE1L_ENUM	4 -#define LINE1R_ENUM	5 -#define LINE2L_ENUM	6 -#define LINE2R_ENUM	7 -#define ADC_HPF_ENUM	8 +#define LINE1L_2_L_ENUM	4 +#define LINE1L_2_R_ENUM	5 +#define LINE1R_2_L_ENUM	6 +#define LINE1R_2_R_ENUM	7 +#define LINE2L_ENUM	8 +#define LINE2R_ENUM	9 +#define ADC_HPF_ENUM	10  static const struct soc_enum aic3x_enum[] = {  	SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux), @@ -235,12 +243,33 @@ static const struct soc_enum aic3x_enum[] = {  	SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux),  	SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux),  	SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), +	SOC_ENUM_SINGLE(LINE1L_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), +	SOC_ENUM_SINGLE(LINE1R_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),  	SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),  	SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),  	SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),  	SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),  }; +static const char *aic3x_agc_level[] = +	{ "-5.5dB", "-8dB", "-10dB", "-12dB", "-14dB", "-17dB", "-20dB", "-24dB" }; +static const struct soc_enum aic3x_agc_level_enum[] = { +	SOC_ENUM_SINGLE(LAGC_CTRL_A, 4, 8, aic3x_agc_level), +	SOC_ENUM_SINGLE(RAGC_CTRL_A, 4, 8, aic3x_agc_level), +}; + +static const char *aic3x_agc_attack[] = { "8ms", "11ms", "16ms", "20ms" }; +static const struct soc_enum aic3x_agc_attack_enum[] = { +	SOC_ENUM_SINGLE(LAGC_CTRL_A, 2, 4, aic3x_agc_attack), +	SOC_ENUM_SINGLE(RAGC_CTRL_A, 2, 4, aic3x_agc_attack), +}; + +static const char *aic3x_agc_decay[] = { "100ms", "200ms", "400ms", "500ms" }; +static const struct soc_enum aic3x_agc_decay_enum[] = { +	SOC_ENUM_SINGLE(LAGC_CTRL_A, 0, 4, aic3x_agc_decay), +	SOC_ENUM_SINGLE(RAGC_CTRL_A, 0, 4, aic3x_agc_decay), +}; +  /*   * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps   */ @@ -321,16 +350,6 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {  			 DACL1_2_LLOPM_VOL, DACR1_2_RLOPM_VOL,  			 0, 118, 1, output_stage_tlv), -	SOC_DOUBLE_R_TLV("Mono Line2 Bypass Volume", -			 LINE2L_2_MONOLOPM_VOL, LINE2R_2_MONOLOPM_VOL, -			 0, 118, 1, output_stage_tlv), -	SOC_DOUBLE_R_TLV("Mono PGA Bypass Volume", -			 PGAL_2_MONOLOPM_VOL, PGAR_2_MONOLOPM_VOL, -			 0, 118, 1, output_stage_tlv), -	SOC_DOUBLE_R_TLV("Mono DAC Playback Volume", -			 DACL1_2_MONOLOPM_VOL, DACR1_2_MONOLOPM_VOL, -			 0, 118, 1, output_stage_tlv), -  	SOC_DOUBLE_R_TLV("HP Line2 Bypass Volume",  			 LINE2L_2_HPLOUT_VOL, LINE2R_2_HPROUT_VOL,  			 0, 118, 1, output_stage_tlv), @@ -354,7 +373,6 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {  	/* Output pin mute controls */  	SOC_DOUBLE_R("Line Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3,  		     0x01, 0), -	SOC_SINGLE("Mono Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0),  	SOC_DOUBLE_R("HP Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,  		     0x01, 0),  	SOC_DOUBLE_R("HPCOM Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3, @@ -365,6 +383,15 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {  	 * adjust PGA to max value when ADC is on and will never go back.  	*/  	SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0), +	SOC_ENUM("Left AGC Target level", aic3x_agc_level_enum[0]), +	SOC_ENUM("Right AGC Target level", aic3x_agc_level_enum[1]), +	SOC_ENUM("Left AGC Attack time", aic3x_agc_attack_enum[0]), +	SOC_ENUM("Right AGC Attack time", aic3x_agc_attack_enum[1]), +	SOC_ENUM("Left AGC Decay time", aic3x_agc_decay_enum[0]), +	SOC_ENUM("Right AGC Decay time", aic3x_agc_decay_enum[1]), + +	/* De-emphasis */ +	SOC_DOUBLE("De-emphasis Switch", AIC3X_CODEC_DFILT_CTRL, 2, 0, 0x01, 0),  	/* Input */  	SOC_DOUBLE_R_TLV("PGA Capture Volume", LADC_VOL, RADC_VOL, @@ -374,13 +401,27 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {  	SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),  }; +static const struct snd_kcontrol_new aic3x_mono_controls[] = { +	SOC_DOUBLE_R_TLV("Mono Line2 Bypass Volume", +			 LINE2L_2_MONOLOPM_VOL, LINE2R_2_MONOLOPM_VOL, +			 0, 118, 1, output_stage_tlv), +	SOC_DOUBLE_R_TLV("Mono PGA Bypass Volume", +			 PGAL_2_MONOLOPM_VOL, PGAR_2_MONOLOPM_VOL, +			 0, 118, 1, output_stage_tlv), +	SOC_DOUBLE_R_TLV("Mono DAC Playback Volume", +			 DACL1_2_MONOLOPM_VOL, DACR1_2_MONOLOPM_VOL, +			 0, 118, 1, output_stage_tlv), + +	SOC_SINGLE("Mono Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0), +}; +  /*   * Class-D amplifier gain. From 0 to 18 dB in 6 dB steps   */  static DECLARE_TLV_DB_SCALE(classd_amp_tlv, 0, 600, 0);  static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl = -	SOC_DOUBLE_TLV("Class-D Amplifier Gain", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv); +	SOC_DOUBLE_TLV("Class-D Playback Volume", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv);  /* Left DAC Mux */  static const struct snd_kcontrol_new aic3x_left_dac_mux_controls = @@ -487,12 +528,16 @@ static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {  };  /* Left Line1 Mux */ -static const struct snd_kcontrol_new aic3x_left_line1_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_ENUM]); +static const struct snd_kcontrol_new aic3x_left_line1l_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_L_ENUM]); +static const struct snd_kcontrol_new aic3x_right_line1l_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_R_ENUM]);  /* Right Line1 Mux */ -static const struct snd_kcontrol_new aic3x_right_line1_mux_controls = -SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_ENUM]); +static const struct snd_kcontrol_new aic3x_right_line1r_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_R_ENUM]); +static const struct snd_kcontrol_new aic3x_left_line1r_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_L_ENUM]);  /* Left Line2 Mux */  static const struct snd_kcontrol_new aic3x_left_line2_mux_controls = @@ -523,18 +568,15 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {  	SND_SOC_DAPM_PGA("Right HP Out", HPROUT_CTRL, 0, 0, NULL, 0),  	SND_SOC_DAPM_PGA("Right HP Com", HPRCOM_CTRL, 0, 0, NULL, 0), -	/* Mono Output */ -	SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0), -  	/* Inputs to Left ADC */  	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0),  	SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,  			   &aic3x_left_pga_mixer_controls[0],  			   ARRAY_SIZE(aic3x_left_pga_mixer_controls)),  	SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0, -			 &aic3x_left_line1_mux_controls), +			 &aic3x_left_line1l_mux_controls),  	SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0, -			 &aic3x_left_line1_mux_controls), +			 &aic3x_left_line1r_mux_controls),  	SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,  			 &aic3x_left_line2_mux_controls), @@ -545,9 +587,9 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {  			   &aic3x_right_pga_mixer_controls[0],  			   ARRAY_SIZE(aic3x_right_pga_mixer_controls)),  	SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0, -			 &aic3x_right_line1_mux_controls), +			 &aic3x_right_line1l_mux_controls),  	SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0, -			 &aic3x_right_line1_mux_controls), +			 &aic3x_right_line1r_mux_controls),  	SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,  			 &aic3x_right_line2_mux_controls), @@ -573,12 +615,9 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {  			 AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0),  	/* Mic Bias */ -	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2V", -			 MICBIAS_CTRL, 6, 3, 1, 0), -	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2.5V", -			 MICBIAS_CTRL, 6, 3, 2, 0), -	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias AVDD", -			 MICBIAS_CTRL, 6, 3, 3, 0), +	SND_SOC_DAPM_SUPPLY("Mic Bias", MICBIAS_CTRL, 6, 0, +			 mic_bias_event, +			 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),  	/* Output mixers */  	SND_SOC_DAPM_MIXER("Left Line Mixer", SND_SOC_NOPM, 0, 0, @@ -587,9 +626,6 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {  	SND_SOC_DAPM_MIXER("Right Line Mixer", SND_SOC_NOPM, 0, 0,  			   &aic3x_right_line_mixer_controls[0],  			   ARRAY_SIZE(aic3x_right_line_mixer_controls)), -	SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, -			   &aic3x_mono_mixer_controls[0], -			   ARRAY_SIZE(aic3x_mono_mixer_controls)),  	SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,  			   &aic3x_left_hp_mixer_controls[0],  			   ARRAY_SIZE(aic3x_left_hp_mixer_controls)), @@ -605,7 +641,6 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {  	SND_SOC_DAPM_OUTPUT("LLOUT"),  	SND_SOC_DAPM_OUTPUT("RLOUT"), -	SND_SOC_DAPM_OUTPUT("MONO_LOUT"),  	SND_SOC_DAPM_OUTPUT("HPLOUT"),  	SND_SOC_DAPM_OUTPUT("HPROUT"),  	SND_SOC_DAPM_OUTPUT("HPLCOM"), @@ -627,6 +662,17 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {  	SND_SOC_DAPM_OUTPUT("Detection"),  }; +static const struct snd_soc_dapm_widget aic3x_dapm_mono_widgets[] = { +	/* Mono Output */ +	SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0), + +	SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, +			   &aic3x_mono_mixer_controls[0], +			   ARRAY_SIZE(aic3x_mono_mixer_controls)), + +	SND_SOC_DAPM_OUTPUT("MONO_LOUT"), +}; +  static const struct snd_soc_dapm_widget aic3007_dapm_widgets[] = {  	/* Class-D outputs */  	SND_SOC_DAPM_PGA("Left Class-D Out", CLASSD_CTRL, 3, 0, NULL, 0), @@ -640,6 +686,8 @@ static const struct snd_soc_dapm_route intercon[] = {  	/* Left Input */  	{"Left Line1L Mux", "single-ended", "LINE1L"},  	{"Left Line1L Mux", "differential", "LINE1L"}, +	{"Left Line1R Mux", "single-ended", "LINE1R"}, +	{"Left Line1R Mux", "differential", "LINE1R"},  	{"Left Line2L Mux", "single-ended", "LINE2L"},  	{"Left Line2L Mux", "differential", "LINE2L"}, @@ -656,6 +704,8 @@ static const struct snd_soc_dapm_route intercon[] = {  	/* Right Input */  	{"Right Line1R Mux", "single-ended", "LINE1R"},  	{"Right Line1R Mux", "differential", "LINE1R"}, +	{"Right Line1L Mux", "single-ended", "LINE1L"}, +	{"Right Line1L Mux", "differential", "LINE1L"},  	{"Right Line2R Mux", "single-ended", "LINE2R"},  	{"Right Line2R Mux", "differential", "LINE2R"}, @@ -711,17 +761,6 @@ static const struct snd_soc_dapm_route intercon[] = {  	{"Right Line Out", NULL, "Right DAC Mux"},  	{"RLOUT", NULL, "Right Line Out"}, -	/* Mono Output */ -	{"Mono Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, -	{"Mono Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, -	{"Mono Mixer", "DACL1 Switch", "Left DAC Mux"}, -	{"Mono Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, -	{"Mono Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, -	{"Mono Mixer", "DACR1 Switch", "Right DAC Mux"}, - -	{"Mono Out", NULL, "Mono Mixer"}, -	{"MONO_LOUT", NULL, "Mono Out"}, -  	/* Left HP Output */  	{"Left HP Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},  	{"Left HP Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, @@ -777,6 +816,18 @@ static const struct snd_soc_dapm_route intercon[] = {  	{"HPRCOM", NULL, "Right HP Com"},  }; +static const struct snd_soc_dapm_route intercon_mono[] = { +	/* Mono Output */ +	{"Mono Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, +	{"Mono Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, +	{"Mono Mixer", "DACL1 Switch", "Left DAC Mux"}, +	{"Mono Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, +	{"Mono Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, +	{"Mono Mixer", "DACR1 Switch", "Right DAC Mux"}, +	{"Mono Out", NULL, "Mono Mixer"}, +	{"MONO_LOUT", NULL, "Mono Out"}, +}; +  static const struct snd_soc_dapm_route intercon_3007[] = {  	/* Class-D outputs */  	{"Left Class-D Out", NULL, "Left Line Out"}, @@ -788,17 +839,22 @@ static const struct snd_soc_dapm_route intercon_3007[] = {  static int aic3x_add_widgets(struct snd_soc_codec *codec)  {  	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); - -	snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets, -				  ARRAY_SIZE(aic3x_dapm_widgets)); - -	/* set up audio path interconnects */ -	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - -	if (aic3x->model == AIC3X_MODEL_3007) { -		snd_soc_dapm_new_controls(codec, aic3007_dapm_widgets, +	struct snd_soc_dapm_context *dapm = &codec->dapm; + +	switch (aic3x->model) { +	case AIC3X_MODEL_3X: +	case AIC3X_MODEL_33: +		snd_soc_dapm_new_controls(dapm, aic3x_dapm_mono_widgets, +			ARRAY_SIZE(aic3x_dapm_mono_widgets)); +		snd_soc_dapm_add_routes(dapm, intercon_mono, +					ARRAY_SIZE(intercon_mono)); +		break; +	case AIC3X_MODEL_3007: +		snd_soc_dapm_new_controls(dapm, aic3007_dapm_widgets,  			ARRAY_SIZE(aic3007_dapm_widgets)); -		snd_soc_dapm_add_routes(codec, intercon_3007, ARRAY_SIZE(intercon_3007)); +		snd_soc_dapm_add_routes(dapm, intercon_3007, +					ARRAY_SIZE(intercon_3007)); +		break;  	}  	return 0; @@ -808,13 +864,11 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,  			   struct snd_pcm_hw_params *params,  			   struct snd_soc_dai *dai)  { -	struct snd_soc_pcm_runtime *rtd = substream->private_data; -	struct snd_soc_codec *codec =rtd->codec; +	struct snd_soc_codec *codec = dai->codec;  	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);  	int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;  	u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;  	u16 d, pll_d = 1; -	u8 reg;  	int clk;  	/* select data word length */ @@ -850,14 +904,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,  		snd_soc_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);  		snd_soc_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);  		/* disable PLL if it is bypassed */ -		reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG); -		snd_soc_write(codec, AIC3X_PLL_PROGA_REG, reg & ~PLL_ENABLE); +		snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, PLL_ENABLE, 0);  	} else {  		snd_soc_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);  		/* enable PLL when it is used */ -		reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG); -		snd_soc_write(codec, AIC3X_PLL_PROGA_REG, reg | PLL_ENABLE); +		snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, +				    PLL_ENABLE, PLL_ENABLE);  	}  	/* Route Left DAC to left channel input and @@ -880,7 +933,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,  	if (bypass_pll)  		return 0; -	/* Use PLL, compute apropriate setup for j, d, r and p, the closest +	/* Use PLL, compute appropriate setup for j, d, r and p, the closest  	 * one wins the game. Try with d==0 first, next with d!=0.  	 * Constraints for j are according to the datasheet.  	 * The sysclk is divided by 1000 to prevent integer overflows. @@ -944,9 +997,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,  	}  found: -	data = snd_soc_read(codec, AIC3X_PLL_PROGA_REG); -	snd_soc_write(codec, AIC3X_PLL_PROGA_REG, -		      data | (pll_p << PLLP_SHIFT)); +	snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, PLLP_MASK, pll_p);  	snd_soc_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG,  		      pll_r << PLLR_SHIFT);  	snd_soc_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT); @@ -981,6 +1032,12 @@ static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai,  	struct snd_soc_codec *codec = codec_dai->codec;  	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); +	/* set clock on MCLK or GPIO2 or BCLK */ +	snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, PLLCLK_IN_MASK, +				clk_id << PLLCLK_IN_SHIFT); +	snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, CLKDIV_IN_MASK, +				clk_id << CLKDIV_IN_SHIFT); +  	aic3x->sysclk = freq;  	return 0;  } @@ -1004,6 +1061,7 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,  		break;  	case SND_SOC_DAIFMT_CBS_CFS:  		aic3x->master = 0; +		iface_areg &= ~(BIT_CLK_MASTER | WORD_CLK_MASTER);  		break;  	default:  		return -EINVAL; @@ -1040,29 +1098,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,  	return 0;  } -static int aic3x_init_3007(struct snd_soc_codec *codec) -{ -	u8 tmp1, tmp2, *cache = codec->reg_cache; - -	/* -	 * There is no need to cache writes to undocumented page 0xD but -	 * respective page 0 register cache entries must be preserved -	 */ -	tmp1 = cache[0xD]; -	tmp2 = cache[0x8]; -	/* Class-D speaker driver init; datasheet p. 46 */ -	snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x0D); -	snd_soc_write(codec, 0xD, 0x0D); -	snd_soc_write(codec, 0x8, 0x5C); -	snd_soc_write(codec, 0x8, 0x5D); -	snd_soc_write(codec, 0x8, 0x5C); -	snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x00); -	cache[0xD] = tmp1; -	cache[0x8] = tmp2; - -	return 0; -} -  static int aic3x_regulator_event(struct notifier_block *nb,  				 unsigned long event, void *data)  { @@ -1075,9 +1110,9 @@ static int aic3x_regulator_event(struct notifier_block *nb,  		 * Put codec to reset and require cache sync as at least one  		 * of the supplies was disabled  		 */ -		if (aic3x->gpio_reset >= 0) +		if (gpio_is_valid(aic3x->gpio_reset))  			gpio_set_value(aic3x->gpio_reset, 0); -		aic3x->codec->cache_sync = 1; +		regcache_mark_dirty(aic3x->regmap);  	}  	return 0; @@ -1086,8 +1121,7 @@ static int aic3x_regulator_event(struct notifier_block *nb,  static int aic3x_set_power(struct snd_soc_codec *codec, int power)  {  	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); -	int i, ret; -	u8 *cache = codec->reg_cache; +	int ret;  	if (power) {  		ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies), @@ -1095,29 +1129,26 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)  		if (ret)  			goto out;  		aic3x->power = 1; -		/* -		 * Reset release and cache sync is necessary only if some -		 * supply was off or if there were cached writes -		 */ -		if (!codec->cache_sync) -			goto out; -		if (aic3x->gpio_reset >= 0) { +		if (gpio_is_valid(aic3x->gpio_reset)) {  			udelay(1);  			gpio_set_value(aic3x->gpio_reset, 1);  		}  		/* Sync reg_cache with the hardware */ -		codec->cache_only = 0; -		for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++) -			snd_soc_write(codec, i, cache[i]); -		if (aic3x->model == AIC3X_MODEL_3007) -			aic3x_init_3007(codec); -		codec->cache_sync = 0; +		regcache_cache_only(aic3x->regmap, false); +		regcache_sync(aic3x->regmap);  	} else { +		/* +		 * Do soft reset to this codec instance in order to clear +		 * possible VDD leakage currents in case the supply regulators +		 * remain on +		 */ +		snd_soc_write(codec, AIC3X_RESET, SOFT_RESET); +		regcache_mark_dirty(aic3x->regmap);  		aic3x->power = 0;  		/* HW writes are needless when bias is off */ -		codec->cache_only = 1; +		regcache_cache_only(aic3x->regmap, true);  		ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies),  					     aic3x->supplies);  	} @@ -1129,29 +1160,26 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,  				enum snd_soc_bias_level level)  {  	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); -	u8 reg;  	switch (level) {  	case SND_SOC_BIAS_ON:  		break;  	case SND_SOC_BIAS_PREPARE: -		if (codec->bias_level == SND_SOC_BIAS_STANDBY && +		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY &&  		    aic3x->master) {  			/* enable pll */ -			reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG); -			snd_soc_write(codec, AIC3X_PLL_PROGA_REG, -				      reg | PLL_ENABLE); +			snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, +					    PLL_ENABLE, PLL_ENABLE);  		}  		break;  	case SND_SOC_BIAS_STANDBY:  		if (!aic3x->power)  			aic3x_set_power(codec, 1); -		if (codec->bias_level == SND_SOC_BIAS_PREPARE && +		if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE &&  		    aic3x->master) {  			/* disable pll */ -			reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG); -			snd_soc_write(codec, AIC3X_PLL_PROGA_REG, -				      reg & ~PLL_ENABLE); +			snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, +					    PLL_ENABLE, 0);  		}  		break;  	case SND_SOC_BIAS_OFF: @@ -1159,70 +1187,16 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,  			aic3x_set_power(codec, 0);  		break;  	} -	codec->bias_level = level; +	codec->dapm.bias_level = level;  	return 0;  } -void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state) -{ -	u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG; -	u8 bit = gpio ? 3: 0; -	u8 val = snd_soc_read(codec, reg) & ~(1 << bit); -	snd_soc_write(codec, reg, val | (!!state << bit)); -} -EXPORT_SYMBOL_GPL(aic3x_set_gpio); - -int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio) -{ -	u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG; -	u8 val, bit = gpio ? 2: 1; - -	aic3x_read(codec, reg, &val); -	return (val >> bit) & 1; -} -EXPORT_SYMBOL_GPL(aic3x_get_gpio); - -void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect, -				 int headset_debounce, int button_debounce) -{ -	u8 val; - -	val = ((detect & AIC3X_HEADSET_DETECT_MASK) -		<< AIC3X_HEADSET_DETECT_SHIFT) | -	      ((headset_debounce & AIC3X_HEADSET_DEBOUNCE_MASK) -		<< AIC3X_HEADSET_DEBOUNCE_SHIFT) | -	      ((button_debounce & AIC3X_BUTTON_DEBOUNCE_MASK) -		<< AIC3X_BUTTON_DEBOUNCE_SHIFT); - -	if (detect & AIC3X_HEADSET_DETECT_MASK) -		val |= AIC3X_HEADSET_DETECT_ENABLED; - -	snd_soc_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val); -} -EXPORT_SYMBOL_GPL(aic3x_set_headset_detection); - -int aic3x_headset_detected(struct snd_soc_codec *codec) -{ -	u8 val; -	aic3x_read(codec, AIC3X_HEADSET_DETECT_CTRL_B, &val); -	return (val >> 4) & 1; -} -EXPORT_SYMBOL_GPL(aic3x_headset_detected); - -int aic3x_button_pressed(struct snd_soc_codec *codec) -{ -	u8 val; -	aic3x_read(codec, AIC3X_HEADSET_DETECT_CTRL_B, &val); -	return (val >> 5) & 1; -} -EXPORT_SYMBOL_GPL(aic3x_button_pressed); -  #define AIC3X_RATES	SNDRV_PCM_RATE_8000_96000  #define AIC3X_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \  			 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) -static struct snd_soc_dai_ops aic3x_dai_ops = { +static const struct snd_soc_dai_ops aic3x_dai_ops = {  	.hw_params	= aic3x_hw_params,  	.digital_mute	= aic3x_mute,  	.set_sysclk	= aic3x_set_dai_sysclk, @@ -1233,13 +1207,13 @@ static struct snd_soc_dai_driver aic3x_dai = {  	.name = "tlv320aic3x-hifi",  	.playback = {  		.stream_name = "Playback", -		.channels_min = 1, +		.channels_min = 2,  		.channels_max = 2,  		.rates = AIC3X_RATES,  		.formats = AIC3X_FORMATS,},  	.capture = {  		.stream_name = "Capture", -		.channels_min = 1, +		.channels_min = 2,  		.channels_max = 2,  		.rates = AIC3X_RATES,  		.formats = AIC3X_FORMATS,}, @@ -1247,7 +1221,7 @@ static struct snd_soc_dai_driver aic3x_dai = {  	.symmetric_rates = 1,  }; -static int aic3x_suspend(struct snd_soc_codec *codec, pm_message_t state) +static int aic3x_suspend(struct snd_soc_codec *codec)  {  	aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); @@ -1261,6 +1235,24 @@ static int aic3x_resume(struct snd_soc_codec *codec)  	return 0;  } +static void aic3x_mono_init(struct snd_soc_codec *codec) +{ +	/* DAC to Mono Line Out default volume and route to Output mixer */ +	snd_soc_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); +	snd_soc_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); + +	/* unmute all outputs */ +	snd_soc_update_bits(codec, MONOLOPM_CTRL, UNMUTE, UNMUTE); + +	/* PGA to Mono Line Out default volume, disconnect from Output Mixer */ +	snd_soc_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL); +	snd_soc_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL); + +	/* Line2 to Mono Out default volume, disconnect from Output Mixer */ +	snd_soc_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL); +	snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); +} +  /*   * initialise the AIC3X driver   * register the mixer and dsp interfaces with the kernel @@ -1268,7 +1260,6 @@ static int aic3x_resume(struct snd_soc_codec *codec)  static int aic3x_init(struct snd_soc_codec *codec)  {  	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); -	int reg;  	snd_soc_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);  	snd_soc_write(codec, AIC3X_RESET, SOFT_RESET); @@ -1285,25 +1276,14 @@ static int aic3x_init(struct snd_soc_codec *codec)  	/* DAC to Line Out default volume and route to Output mixer */  	snd_soc_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);  	snd_soc_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON); -	/* DAC to Mono Line Out default volume and route to Output mixer */ -	snd_soc_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); -	snd_soc_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);  	/* unmute all outputs */ -	reg = snd_soc_read(codec, LLOPM_CTRL); -	snd_soc_write(codec, LLOPM_CTRL, reg | UNMUTE); -	reg = snd_soc_read(codec, RLOPM_CTRL); -	snd_soc_write(codec, RLOPM_CTRL, reg | UNMUTE); -	reg = snd_soc_read(codec, MONOLOPM_CTRL); -	snd_soc_write(codec, MONOLOPM_CTRL, reg | UNMUTE); -	reg = snd_soc_read(codec, HPLOUT_CTRL); -	snd_soc_write(codec, HPLOUT_CTRL, reg | UNMUTE); -	reg = snd_soc_read(codec, HPROUT_CTRL); -	snd_soc_write(codec, HPROUT_CTRL, reg | UNMUTE); -	reg = snd_soc_read(codec, HPLCOM_CTRL); -	snd_soc_write(codec, HPLCOM_CTRL, reg | UNMUTE); -	reg = snd_soc_read(codec, HPRCOM_CTRL); -	snd_soc_write(codec, HPRCOM_CTRL, reg | UNMUTE); +	snd_soc_update_bits(codec, LLOPM_CTRL, UNMUTE, UNMUTE); +	snd_soc_update_bits(codec, RLOPM_CTRL, UNMUTE, UNMUTE); +	snd_soc_update_bits(codec, HPLOUT_CTRL, UNMUTE, UNMUTE); +	snd_soc_update_bits(codec, HPROUT_CTRL, UNMUTE, UNMUTE); +	snd_soc_update_bits(codec, HPLCOM_CTRL, UNMUTE, UNMUTE); +	snd_soc_update_bits(codec, HPRCOM_CTRL, UNMUTE, UNMUTE);  	/* ADC default volume and unmute */  	snd_soc_write(codec, LADC_VOL, DEFAULT_GAIN); @@ -1320,9 +1300,6 @@ static int aic3x_init(struct snd_soc_codec *codec)  	/* PGA to Line Out default volume, disconnect from Output Mixer */  	snd_soc_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);  	snd_soc_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL); -	/* PGA to Mono Line Out default volume, disconnect from Output Mixer */ -	snd_soc_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL); -	snd_soc_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);  	/* Line2 to HP Bypass default volume, disconnect from Output Mixer */  	snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL); @@ -1332,49 +1309,41 @@ static int aic3x_init(struct snd_soc_codec *codec)  	/* Line2 Line Out default volume, disconnect from Output Mixer */  	snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);  	snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL); -	/* Line2 to Mono Out default volume, disconnect from Output Mixer */ -	snd_soc_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL); -	snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); -	if (aic3x->model == AIC3X_MODEL_3007) { -		aic3x_init_3007(codec); +	switch (aic3x->model) { +	case AIC3X_MODEL_3X: +	case AIC3X_MODEL_33: +		aic3x_mono_init(codec); +		break; +	case AIC3X_MODEL_3007:  		snd_soc_write(codec, CLASSD_CTRL, 0); +		break;  	}  	return 0;  } +static bool aic3x_is_shared_reset(struct aic3x_priv *aic3x) +{ +	struct aic3x_priv *a; + +	list_for_each_entry(a, &reset_list, list) { +		if (gpio_is_valid(aic3x->gpio_reset) && +		    aic3x->gpio_reset == a->gpio_reset) +			return true; +	} + +	return false; +} +  static int aic3x_probe(struct snd_soc_codec *codec)  {  	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);  	int ret, i; -	codec->control_data = aic3x->control_data; +	INIT_LIST_HEAD(&aic3x->list);  	aic3x->codec = codec; -	codec->idle_bias_off = 1; - -	ret = snd_soc_codec_set_cache_io(codec, 8, 8, aic3x->control_type); -	if (ret != 0) { -		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); -		return ret; -	} - -	if (aic3x->gpio_reset >= 0) { -		ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset"); -		if (ret != 0) -			goto err_gpio; -		gpio_direction_output(aic3x->gpio_reset, 0); -	} -	for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) -		aic3x->supplies[i].supply = aic3x_supply_names[i]; - -	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(aic3x->supplies), -				 aic3x->supplies); -	if (ret != 0) { -		dev_err(codec->dev, "Failed to request supplies: %d\n", ret); -		goto err_get; -	}  	for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) {  		aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event;  		aic3x->disable_nb[i].aic3x = aic3x; @@ -1388,7 +1357,7 @@ static int aic3x_probe(struct snd_soc_codec *codec)  		}  	} -	codec->cache_only = 1; +	regcache_mark_dirty(aic3x->regmap);  	aic3x_init(codec);  	if (aic3x->setup) { @@ -1399,10 +1368,35 @@ static int aic3x_probe(struct snd_soc_codec *codec)  			      (aic3x->setup->gpio_func[1] & 0xf) << 4);  	} -	snd_soc_add_controls(codec, aic3x_snd_controls, -			     ARRAY_SIZE(aic3x_snd_controls)); -	if (aic3x->model == AIC3X_MODEL_3007) -		snd_soc_add_controls(codec, &aic3x_classd_amp_gain_ctrl, 1); +	switch (aic3x->model) { +	case AIC3X_MODEL_3X: +	case AIC3X_MODEL_33: +		snd_soc_add_codec_controls(codec, aic3x_mono_controls, +				ARRAY_SIZE(aic3x_mono_controls)); +		break; +	case AIC3X_MODEL_3007: +		snd_soc_add_codec_controls(codec, +				&aic3x_classd_amp_gain_ctrl, 1); +		break; +	} + +	/* set mic bias voltage */ +	switch (aic3x->micbias_vg) { +	case AIC3X_MICBIAS_2_0V: +	case AIC3X_MICBIAS_2_5V: +	case AIC3X_MICBIAS_AVDDV: +		snd_soc_update_bits(codec, MICBIAS_CTRL, +				    MICBIAS_LEVEL_MASK, +				    (aic3x->micbias_vg) << MICBIAS_LEVEL_SHIFT); +		break; +	case AIC3X_MICBIAS_OFF: +		/* +		 * noting to do. target won't enter here. This is just to avoid +		 * compile time warning "warning: enumeration value +		 * 'AIC3X_MICBIAS_OFF' not handled in switch" +		 */ +		break; +	}  	aic3x_add_widgets(codec); @@ -1412,12 +1406,6 @@ err_notif:  	while (i--)  		regulator_unregister_notifier(aic3x->supplies[i].consumer,  					      &aic3x->disable_nb[i].nb); -	regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); -err_get: -	if (aic3x->gpio_reset >= 0) -		gpio_free(aic3x->gpio_reset); -err_gpio: -	kfree(aic3x);  	return ret;  } @@ -1427,43 +1415,53 @@ static int aic3x_remove(struct snd_soc_codec *codec)  	int i;  	aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); -	if (aic3x->gpio_reset >= 0) { -		gpio_set_value(aic3x->gpio_reset, 0); -		gpio_free(aic3x->gpio_reset); -	} +	list_del(&aic3x->list);  	for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)  		regulator_unregister_notifier(aic3x->supplies[i].consumer,  					      &aic3x->disable_nb[i].nb); -	regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);  	return 0;  }  static struct snd_soc_codec_driver soc_codec_dev_aic3x = {  	.set_bias_level = aic3x_set_bias_level, -	.reg_cache_size = ARRAY_SIZE(aic3x_reg), -	.reg_word_size = sizeof(u8), -	.reg_cache_default = aic3x_reg, +	.idle_bias_off = true,  	.probe = aic3x_probe,  	.remove = aic3x_remove,  	.suspend = aic3x_suspend,  	.resume = aic3x_resume, +	.controls = aic3x_snd_controls, +	.num_controls = ARRAY_SIZE(aic3x_snd_controls), +	.dapm_widgets = aic3x_dapm_widgets, +	.num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets), +	.dapm_routes = intercon, +	.num_dapm_routes = ARRAY_SIZE(intercon),  }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)  /*   * AIC3X 2 wire address can be up to 4 devices with device addresses   * 0x18, 0x19, 0x1A, 0x1B   */  static const struct i2c_device_id aic3x_i2c_id[] = { -	[AIC3X_MODEL_3X] = { "tlv320aic3x", 0 }, -	[AIC3X_MODEL_33] = { "tlv320aic33", 0 }, -	[AIC3X_MODEL_3007] = { "tlv320aic3007", 0 }, +	{ "tlv320aic3x", AIC3X_MODEL_3X }, +	{ "tlv320aic33", AIC3X_MODEL_33 }, +	{ "tlv320aic3007", AIC3X_MODEL_3007 }, +	{ "tlv320aic3106", AIC3X_MODEL_3X },  	{ }  };  MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); +static const struct reg_default aic3007_class_d[] = { +	/* Class-D speaker driver init; datasheet p. 46 */ +	{ AIC3X_PAGE_SELECT, 0x0D }, +	{ 0xD, 0x0D }, +	{ 0x8, 0x5C }, +	{ 0x8, 0x5D }, +	{ 0x8, 0x5C }, +	{ AIC3X_PAGE_SELECT, 0x00 }, +}; +  /*   * If the i2c layer weren't so broken, we could pass this kind of data   * around @@ -1473,94 +1471,156 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,  {  	struct aic3x_pdata *pdata = i2c->dev.platform_data;  	struct aic3x_priv *aic3x; -	int ret; -	const struct i2c_device_id *tbl; +	struct aic3x_setup_data *ai3x_setup; +	struct device_node *np = i2c->dev.of_node; +	int ret, i; +	u32 value; -	aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); +	aic3x = devm_kzalloc(&i2c->dev, sizeof(struct aic3x_priv), GFP_KERNEL);  	if (aic3x == NULL) {  		dev_err(&i2c->dev, "failed to create private data\n");  		return -ENOMEM;  	} -	aic3x->control_data = i2c; -	aic3x->control_type = SND_SOC_I2C; +	aic3x->regmap = devm_regmap_init_i2c(i2c, &aic3x_regmap); +	if (IS_ERR(aic3x->regmap)) { +		ret = PTR_ERR(aic3x->regmap); +		return ret; +	} + +	regcache_cache_only(aic3x->regmap, true);  	i2c_set_clientdata(i2c, aic3x);  	if (pdata) {  		aic3x->gpio_reset = pdata->gpio_reset;  		aic3x->setup = pdata->setup; +		aic3x->micbias_vg = pdata->micbias_vg; +	} else if (np) { +		ai3x_setup = devm_kzalloc(&i2c->dev, sizeof(*ai3x_setup), +								GFP_KERNEL); +		if (ai3x_setup == NULL) { +			dev_err(&i2c->dev, "failed to create private data\n"); +			return -ENOMEM; +		} + +		ret = of_get_named_gpio(np, "gpio-reset", 0); +		if (ret >= 0) +			aic3x->gpio_reset = ret; +		else +			aic3x->gpio_reset = -1; + +		if (of_property_read_u32_array(np, "ai3x-gpio-func", +					ai3x_setup->gpio_func, 2) >= 0) { +			aic3x->setup = ai3x_setup; +		} + +		if (!of_property_read_u32(np, "ai3x-micbias-vg", &value)) { +			switch (value) { +			case 1 : +				aic3x->micbias_vg = AIC3X_MICBIAS_2_0V; +				break; +			case 2 : +				aic3x->micbias_vg = AIC3X_MICBIAS_2_5V; +				break; +			case 3 : +				aic3x->micbias_vg = AIC3X_MICBIAS_AVDDV; +				break; +			default : +				aic3x->micbias_vg = AIC3X_MICBIAS_OFF; +				dev_err(&i2c->dev, "Unsuitable MicBias voltage " +							"found in DT\n"); +			} +		} else { +			aic3x->micbias_vg = AIC3X_MICBIAS_OFF; +		} +  	} else {  		aic3x->gpio_reset = -1;  	} -	for (tbl = aic3x_i2c_id; tbl->name[0]; tbl++) { -		if (!strcmp(tbl->name, id->name)) -			break; +	aic3x->model = id->driver_data; + +	if (gpio_is_valid(aic3x->gpio_reset) && +	    !aic3x_is_shared_reset(aic3x)) { +		ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset"); +		if (ret != 0) +			goto err; +		gpio_direction_output(aic3x->gpio_reset, 0); +	} + +	for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) +		aic3x->supplies[i].supply = aic3x_supply_names[i]; + +	ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(aic3x->supplies), +				      aic3x->supplies); +	if (ret != 0) { +		dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); +		goto err_gpio; +	} + +	if (aic3x->model == AIC3X_MODEL_3007) { +		ret = regmap_register_patch(aic3x->regmap, aic3007_class_d, +					    ARRAY_SIZE(aic3007_class_d)); +		if (ret != 0) +			dev_err(&i2c->dev, "Failed to init class D: %d\n", +				ret);  	} -	aic3x->model = tbl - aic3x_i2c_id;  	ret = snd_soc_register_codec(&i2c->dev,  			&soc_codec_dev_aic3x, &aic3x_dai, 1); -	if (ret < 0) -		kfree(aic3x); + +	if (ret != 0) +		goto err_gpio; + +	list_add(&aic3x->list, &reset_list); + +	return 0; + +err_gpio: +	if (gpio_is_valid(aic3x->gpio_reset) && +	    !aic3x_is_shared_reset(aic3x)) +		gpio_free(aic3x->gpio_reset); +err:  	return ret;  }  static int aic3x_i2c_remove(struct i2c_client *client)  { +	struct aic3x_priv *aic3x = i2c_get_clientdata(client); +  	snd_soc_unregister_codec(&client->dev); -	kfree(i2c_get_clientdata(client)); +	if (gpio_is_valid(aic3x->gpio_reset) && +	    !aic3x_is_shared_reset(aic3x)) { +		gpio_set_value(aic3x->gpio_reset, 0); +		gpio_free(aic3x->gpio_reset); +	}  	return 0;  } +#if defined(CONFIG_OF) +static const struct of_device_id tlv320aic3x_of_match[] = { +	{ .compatible = "ti,tlv320aic3x", }, +	{ .compatible = "ti,tlv320aic33" }, +	{ .compatible = "ti,tlv320aic3007" }, +	{ .compatible = "ti,tlv320aic3106" }, +	{}, +}; +MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match); +#endif +  /* machine i2c codec control layer */  static struct i2c_driver aic3x_i2c_driver = {  	.driver = {  		.name = "tlv320aic3x-codec",  		.owner = THIS_MODULE, +		.of_match_table = of_match_ptr(tlv320aic3x_of_match),  	},  	.probe	= aic3x_i2c_probe,  	.remove = aic3x_i2c_remove,  	.id_table = aic3x_i2c_id,  }; -static inline void aic3x_i2c_init(void) -{ -	int ret; - -	ret = i2c_add_driver(&aic3x_i2c_driver); -	if (ret) -		printk(KERN_ERR "%s: error regsitering i2c driver, %d\n", -		       __func__, ret); -} - -static inline void aic3x_i2c_exit(void) -{ -	i2c_del_driver(&aic3x_i2c_driver); -} -#endif - -static int __init aic3x_modinit(void) -{ -	int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -	ret = i2c_add_driver(&aic3x_i2c_driver); -	if (ret != 0) { -		printk(KERN_ERR "Failed to register TLV320AIC3x I2C driver: %d\n", -		       ret); -	} -#endif -	return ret; -} -module_init(aic3x_modinit); - -static void __exit aic3x_exit(void) -{ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -	i2c_del_driver(&aic3x_i2c_driver); -#endif -} -module_exit(aic3x_exit); +module_i2c_driver(aic3x_i2c_driver);  MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");  MODULE_AUTHOR("Vladimir Barinov");  | 
