diff options
Diffstat (limited to 'sound/soc/codecs/tlv320aic3x.c')
| -rw-r--r-- | sound/soc/codecs/tlv320aic3x.c | 795 |
1 files changed, 424 insertions, 371 deletions
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 3bedab26892..e12fafbb1e0 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -40,7 +40,8 @@ #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> @@ -72,11 +73,10 @@ 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; @@ -86,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, @@ -157,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; @@ -165,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); @@ -176,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->dapm->card->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->dapm); + 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" }; @@ -225,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), @@ -237,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 */ @@ -323,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), @@ -356,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, @@ -367,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, @@ -376,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 = @@ -489,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 = @@ -525,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), @@ -547,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), @@ -575,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, @@ -589,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)), @@ -607,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"), @@ -629,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), @@ -642,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"}, @@ -658,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"}, @@ -713,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"}, @@ -779,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"}, @@ -792,17 +841,20 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec) struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, - ARRAY_SIZE(aic3x_dapm_widgets)); - - /* set up audio path interconnects */ - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - - if (aic3x->model == AIC3X_MODEL_3007) { + 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(dapm, intercon_3007, ARRAY_SIZE(intercon_3007)); + break; } return 0; @@ -812,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 */ @@ -854,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 @@ -884,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. @@ -948,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); @@ -985,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; } @@ -1008,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; @@ -1044,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) { @@ -1081,7 +1112,7 @@ static int aic3x_regulator_event(struct notifier_block *nb, */ 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; @@ -1090,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), @@ -1099,12 +1129,6 @@ 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 (gpio_is_valid(aic3x->gpio_reset)) { udelay(1); @@ -1112,16 +1136,19 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power) } /* 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); } @@ -1133,7 +1160,6 @@ 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: @@ -1142,9 +1168,8 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, 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: @@ -1153,9 +1178,8 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, 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: @@ -1168,65 +1192,11 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, 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 = 0, 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 = 0; - 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 = 0; - 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, @@ -1237,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,}, @@ -1251,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); @@ -1265,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 @@ -1272,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); @@ -1289,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); @@ -1324,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); @@ -1336,13 +1309,15 @@ 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; @@ -1367,33 +1342,8 @@ static int aic3x_probe(struct snd_soc_codec *codec) int ret, i; INIT_LIST_HEAD(&aic3x->list); - codec->control_data = aic3x->control_data; aic3x->codec = codec; - codec->dapm.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 (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; - 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; @@ -1407,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) { @@ -1418,13 +1368,37 @@ 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); - list_add(&aic3x->list, &reset_list); return 0; @@ -1432,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 (gpio_is_valid(aic3x->gpio_reset) && - !aic3x_is_shared_reset(aic3x)) - gpio_free(aic3x->gpio_reset); -err_gpio: return ret; } @@ -1448,44 +1416,52 @@ static int aic3x_remove(struct snd_soc_codec *codec) aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); list_del(&aic3x->list); - if (gpio_is_valid(aic3x->gpio_reset) && - !aic3x_is_shared_reset(aic3x)) { - gpio_set_value(aic3x->gpio_reset, 0); - gpio_free(aic3x->gpio_reset); - } 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 @@ -1495,79 +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, }; -#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"); |
